/* * MessageRouter.java * Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.greenstone.gsdl3.core; import org.greenstone.gsdl3.util.*; import org.greenstone.gsdl3.service.*; import org.greenstone.gsdl3.comms.*; import org.greenstone.gsdl3.collection.*; // XML classes import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.InputSource; import javax.xml.parsers.*; // other java classes import java.io.File; import java.io.Reader; import java.io.StringReader; import java.net.Authenticator; import java.net.PasswordAuthentication; import java.util.HashMap; import java.util.Iterator; import java.util.Properties; import org.apache.log4j.*; /** * The hub of a Greenstone system. * * Accepts XML requests (via process method of ModuleInterface) and routes them to the appropriate collection or * service or external entity. * * contains a map of module objects - may be services, collections, comms * objects talking to other MessageRouters etc. * * * @author Katherine Don * @version $Revision: 15191 $ * @see ModuleInterface * @see Collection * @see ServiceRack * @see Communicator * * Since some service classes are moved into a separate directory in order for them to be checked out from a different repository, * we modify the configureServices method to search some of the classes in other place if they are not found in the service directory. */ public class MessageRouter implements ModuleInterface { static Logger logger = Logger.getLogger(org.greenstone.gsdl3.core.MessageRouter.class.getName()); /** the (directory) name of the site */ protected String site_name = null; /** site home - the home directory for the site */ protected String site_home=null; /** the http address for this site */ protected String site_http_address=null; protected String library_name = null; /** map of names to Module objects */ protected HashMap module_map=null; /** container Document to create XML Nodes */ protected Document doc=null; /** the full description of this site */ // should these things be separated into local and remote?? /** the original xml config element */ public Element config_info = null; /** list of collections that can be reached */ protected Element collection_list = null; /** list of collections that are loaded but are private */ protected Element private_collection_list = null; /** list of collections that are public and OAI-supportive */ protected Element oai_collection_list = null; /** list of service clusters that can be reached */ protected Element cluster_list = null; /** list of single services that can be reached */ protected Element service_list = null; /** list of sites that can be reached */ protected Element site_list = null; /** a converter class to parse XML and create Docs */ protected XMLConverter converter=null; //*************************************************************** // public methods //*************************************************************** /** constructor */ public MessageRouter() { this.converter = new XMLConverter(); this.doc = this.converter.newDOM(); } public void cleanUp() { cleanUpModuleMapEntire(); } /** site_name must be set before configure is called */ public void setSiteName(String site_name) { this.site_name = site_name; } public String getSiteName() { return this.site_name; } /** library_name must be set before configure is called */ public void setLibraryName(String library_name) { this.library_name = library_name; } public String getLibraryName() { return this.library_name; } /** * configures the system * * looks in site_home/collect for collections, reads config file * site_home/siteConfig.xml * */ public boolean configure() { logger.info("configuring the Message Router"); if (this.site_name==null) { logger.error(" You must set site_name before calling configure"); return false; } this.site_home = GSFile.siteHome(GlobalProperties.getGSDL3Home(), this.site_name); this.site_http_address = GlobalProperties.getGSDL3WebAddress()+"/sites/"+this.site_name; // are we behind a firewall?? - is there a better place to set up the proxy? String host = GlobalProperties.getProperty("proxy.host"); String port = GlobalProperties.getProperty("proxy.port"); final String user = GlobalProperties.getProperty("proxy.user"); final String passwd = GlobalProperties.getProperty("proxy.password"); if (host != null && !host.equals("") && port !=null && !port.equals("")) { System.setProperty("http.proxyType", "4"); System.setProperty("http.proxyHost", host); System.setProperty("http.proxyPort", port); System.setProperty("http.proxySet", "true"); // have we got a user/password? if (user != null && !user.equals("") && passwd != null && !passwd.equals("")) { try { // set up the authenticator Authenticator.setDefault(new Authenticator(){ protected PasswordAuthentication getPasswordAuthentication(){ return new PasswordAuthentication(user, new String(passwd).toCharArray()); } }); } catch (Exception e) { logger.error("MessageRouter Error: couldn't set up an authenticator the proxy"); } } } this.module_map = new HashMap(); // This stuff may be done at a reconfigure also return configureLocalSite(); } /** * Process an XML request - as a String * * @param xml_in the request to process * @return the response - contains any error messages * @see String */ public String process(String xml_in) { Document doc = this.converter.getDOM(xml_in); Element result = process(doc.getDocumentElement()); return this.converter.getString(result); } /** * Process an XML request - as a DOM Element * * @param xml_in the message to process - should be * @return the response - contains any error messages * @see Element */ public Element process(Element message) { // check that its a correct message tag if (!message.getTagName().equals(GSXML.MESSAGE_ELEM)) { logger.error(" Invalid message. GSDL message should start with <"+GSXML.MESSAGE_ELEM+">, instead it starts with:"+message.getTagName()+"."); return null; } NodeList requests = message.getElementsByTagName(GSXML.REQUEST_ELEM); Element mainResult = this.doc.createElement(GSXML.MESSAGE_ELEM); // empty request if (requests.getLength()==0) { logger.error("empty request"); return mainResult; } Document message_doc = message.getOwnerDocument(); // for now, just process each request one by one, and append the results to mainResult // Note: if you add an element to another node in the same document, it // gets removed from where it was. This changes the node list - you cant iterate over the node list in a normal manner if you are moving elements out of it int num_requests = requests.getLength(); for (int i=0; i< num_requests; i++) { Node result=null; Element req = (Element)requests.item(i); if (req == null) { logger.error("request "+i+" is null"); continue; } String path = req.getAttribute(GSXML.TO_ATT); // returns "" if no att of this name if (path.equals("")) { // its a message for the message router String type_att = req.getAttribute(GSXML.TYPE_ATT); if (type_att.equals(GSXML.REQUEST_TYPE_MESSAGING)) { // its a messaging request - modifies the requests/responses result = modifyMessages(req, message, mainResult); } else { // standard request result = processMessage(req); } mainResult.appendChild(this.doc.importNode(result, true)); } else { // The message needs to go to another module. The same message can // be passed to multiple modules - they will be in a comma // separated list in the 'to' attribute String [] modules = path.split(","); for (int j=0; j=0; i--) { Element item = (Element)elements.item(i); String name = item.getAttribute(GSXML.NAME_ATT); String potential_site_name = GSPath.getFirstLink(name); if (remote_site != null) { if (remote_site.equals(potential_site_name)) { list.removeChild(item); } } else { if (name.equals(potential_site_name)) {// there was no site list.removeChild(item); ModuleInterface m = (ModuleInterface)this.module_map.remove(name); m.cleanUp(); // clean up any open files/connections etc m=null; } } } logger.error(this.converter.getString(list)); } /** removes all site modules from module_map, and any stored info about this sites collections and services */ protected void cleanUpAllExternalSiteInfo() { NodeList site_nodes = this.site_list.getChildNodes(); for(int i=site_nodes.getLength()-1; i>=0; i--) { Element item = (Element)site_nodes.item(i); String name = item.getAttribute(GSXML.NAME_ATT); // will remove the node from site_list deactivateModule(GSXML.SITE_ELEM, name); } } /** read thru own site config file - create services and connect to sites */ protected boolean configureLocalSite() { // this may be a reconfigure, so clean up the old moduleMap cleanUpModuleMapEntire(); File configFile = new File(GSFile.siteConfigFile(this.site_home)); if (!configFile.exists() ) { logger.error(" site config file: "+configFile.getPath()+" not found!"); return false; } Document config_doc = this.converter.getDOM(configFile); if (config_doc == null) { logger.error(" couldn't parse site config file: "+configFile.getPath()); return false; } this.config_info = config_doc.getDocumentElement(); // load up the services: serviceRackList this.service_list = this.doc.createElement(GSXML.SERVICE_ELEM+GSXML.LIST_MODIFIER); Element service_rack_list_elem = (Element)GSXML.getChildByTagName(config_info, GSXML.SERVICE_CLASS_ELEM+GSXML.LIST_MODIFIER); configureServices(service_rack_list_elem); // load up the service clusters this.cluster_list = this.doc.createElement(GSXML.CLUSTER_ELEM+GSXML.LIST_MODIFIER); Element cluster_list_elem = (Element)GSXML.getChildByTagName(config_info, GSXML.CLUSTER_ELEM+GSXML.LIST_MODIFIER); configureClusters(cluster_list_elem); // load up the collections this.collection_list = this.doc.createElement(GSXML.COLLECTION_ELEM+GSXML.LIST_MODIFIER); this.private_collection_list = this.doc.createElement(GSXML.COLLECTION_ELEM+GSXML.LIST_MODIFIER); this.oai_collection_list = this.doc.createElement(GSXML.COLLECTION_ELEM+GSXML.LIST_MODIFIER); configureCollections(); // load up the external sites - this also adds their services/clusters/collections to the other lists - so must be done last this.site_list = this.doc.createElement(GSXML.SITE_ELEM+GSXML.LIST_MODIFIER); Element site_list_elem = (Element)GSXML.getChildByTagName(config_info, GSXML.SITE_ELEM+GSXML.LIST_MODIFIER); configureExternalSites(site_list_elem); return true; } protected boolean configureServices(Element service_rack_list) { // load up the individual services logger.info("loading service modules..."); if (service_rack_list == null) { logger.info("... none to be loaded"); return true; } NodeList service_racks = service_rack_list.getElementsByTagName(GSXML.SERVICE_CLASS_ELEM); if (service_racks.getLength()==0) { logger.info("... none to be loaded"); return true; } Element service_message = this.doc.createElement(GSXML.MESSAGE_ELEM); Element service_request = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_DESCRIBE, "", "", ""); service_message.appendChild(service_request); for(int i=0; i0) { for (int i=0; i0) { for (int i=0; i0) { for (int i=0; i * @return the result Element - should be */ protected Element processMessage(Element req) { // message for self, should be type=describe/configure at this stage String type = req.getAttribute(GSXML.TYPE_ATT); Element response = this.doc.createElement(GSXML.RESPONSE_ELEM); response.setAttribute(GSXML.FROM_ATT, ""); if (type.equals(GSXML.REQUEST_TYPE_DESCRIBE)) { response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_DESCRIBE); // check the param list Element param_list = (Element) GSXML.getChildByTagName(req, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER); if (param_list == null) { response.appendChild(this.collection_list); response.appendChild(this.cluster_list); response.appendChild(this.site_list); response.appendChild(this.service_list); return response; } NodeList params = param_list.getElementsByTagName(GSXML.PARAM_ELEM); // go through the param list and see what components are wanted for (int i=0; i