/* * 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.util.HashMap; import java.util.Iterator; import java.io.Reader; import java.io.StringReader; /** * 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: 4141 $ * @see ModuleInterface * @see Collection * @see ServiceRack * @see Communicator * */ public class MessageRouter implements ModuleInterface { /** 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; /** the global name for this site */ protected String global_site_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?? /** list of collections that can be reached */ protected Element 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(); } /** site_home must be set before configure called */ public void setSiteHome(String home) { this.site_home = home; } /** * configures the system * * looks in site_home/collect for collections, reads config file * site_home/sitecfg.xml * */ public boolean configure() { System.out.println("MessageRouter:configuring site"); if (this.site_home==null) { System.err.println("You must set site_home before calling configure"); return false; } // read thru own config file - create services and connect to sites File configFile = new File(GSFile.siteConfigFile(this.site_home)); if (!configFile.exists() ) { System.err.println("MessageRouter: site config file: "+configFile.getPath()+" not found!"); return false; } this.module_map = new HashMap(); Element config = this.converter.getDOM(configFile).getDocumentElement(); Element local_site_name = (Element)GSXML.getChildByTagName(config, GSXML.SITE_NAME_ELEM); if (local_site_name == null) { System.err.println("MessageRouter configure error:no site name in config file"); return false; } else { this.global_site_name = local_site_name.getAttribute(GSXML.VALUE_ATT); } Element http_address = (Element)GSXML.getChildByTagName(config, GSXML.SITE_HTTP_ADDRESS_ELEM); if (http_address == null) { System.err.println("MessageRouter configure error: no http address in config file"); return false; } else { this.site_http_address = http_address.getAttribute(GSXML.VALUE_ATT); } // load up the services Element service_rack_list = (Element)GSXML.getChildByTagName(config, GSXML.SERVICE_CLASS_ELEM+GSXML.LIST_MODIFIER); configureServices(service_rack_list); // load up the service clusters Element cluster_list = (Element)GSXML.getChildByTagName(config, GSXML.CLUSTER_ELEM+GSXML.LIST_MODIFIER); configureClusters(cluster_list); // load up the collections configureCollections(); // load up the external sites - this also adds their services/clusters/collections to the other lists - so must be done last Element site_list = (Element)GSXML.getChildByTagName(config, GSXML.SITE_ELEM+GSXML.LIST_MODIFIER); configureSites(site_list); return true; } /** * 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) { System.out.println("MR received request"); System.out.println(this.converter.getString(message)); // check that its a correct message tag if (!message.getTagName().equals(GSXML.MESSAGE_ELEM)) { System.err.println("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) { 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(0); String path = req.getAttribute(GSXML.TO_ATT); // returns "" if no att of this name if (path.equals("")) { // its a message for the message router result = processMessage(req); mainResult.appendChild(this.doc.importNode(result, true)); } else { // find the module to pass it on to // need to put the request into a message element Element mess = message_doc.createElement(GSXML.MESSAGE_ELEM); mess.appendChild(req); String obj = GSPath.getFirstLink(path); if (this.module_map.containsKey(obj)) { result = ((ModuleInterface)this.module_map.get(obj)).process(mess); if (result !=null ) { // append the contents of the message to the mainResult - there will only be one response at this stage mainResult.appendChild(this.doc.importNode(GSXML.getChildByTagName(result, GSXML.RESPONSE_ELEM), true)); } else { System.err.println("MessageRouter Error: request had null result!"); } } else { System.err.println("MessageRouter Error: request has illegal module name in:\n"+this.converter.getString(req)); } } } // for each request System.out.println("MR returned response"); System.out.println(this.converter.getString(mainResult)); return mainResult; } // ******************************************************************** // auxiliary configure methods // ******************************************************************* protected boolean configureServices(Element service_rack_list) { this.service_list = this.doc.createElement(GSXML.SERVICE_ELEM+GSXML.LIST_MODIFIER); // load up the individual services System.out.println("loading service modules..."); if (service_rack_list == null) { System.out.println("... none to be loaded"); return true; } NodeList service_racks = service_rack_list.getElementsByTagName(GSXML.SERVICE_CLASS_ELEM); if (service_racks.getLength()==0) { System.out.println("... 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; } System.out.println("params found, getting subset"); NodeList params = param_list.getElementsByTagName(GSXML.PARAM_ELEM); // go through the param list and see what components are wanted for (int i=0; i