/*
* 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