package org.greenstone.gs3services; import java.io.File; import java.io.InputStream; import java.util.Enumeration; import java.util.Properties; import org.apache.log4j.Logger; import org.greenstone.gsdl3.core.MessageRouter; import org.greenstone.gsdl3.util.GSFile; import org.greenstone.gsdl3.util.GSXML; import org.greenstone.gsdl3.util.GlobalProperties; import org.greenstone.gsdl3.util.XMLConverter; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * A beginning to the AdminSOAPServer class which will contain management/admin * related web services such as adding new documents, creating, building and * importing collections and configuring Greenstone modules. * * NOTE: to run this class, the folder containing this web service class' * properties helpFile should be on the classpath. */ public class AdminSOAPServer { /** The Logger for this class */ private static Logger LOG = Logger.getLogger(AdminSOAPServer.class); /** Error message loading helpFile. Remains at "" if everything is fine */ protected static String helpErrormessage = ""; /** Properties map with mappings from methodname to help * description string */ protected static Properties properties; /** The help properties file describing the web service operations */ protected static String helpFile = "AdminWebServicesHelp.properties"; // static code block to initialise the help Properties from the helpFile static { properties = new Properties(); // load the properties file from a location with respect to the // the Web Service .class file InputStream input = null; try { // load the properties file from a location with respect to the // the Web Service .class file input = AdminSOAPServer.class.getClassLoader().getResourceAsStream( helpFile); if(input == null) { helpErrormessage = "Cannot find file " + helpFile + " to load."; LOG.warn(helpErrormessage); } else { properties.load(input); input.close(); } } catch(Exception e) { helpErrormessage = "Exception loading properties from help file " + helpFile + "\n" + e.getMessage(); LOG.warn("Exception loading properties from help file " + helpFile + "\n" + e.getMessage()); } } /** site_name the MessageRouter works with, here set to "localsite" */ protected String site_name = "localsite"; /** Message Router object to pass requests messages to and which * will process them.*/ protected MessageRouter mr = null; /** Container Document to create XML Nodes */ protected Document doc=null; /** A converter class to parse XML and create Docs */ protected XMLConverter converter=null; /** Constructor that initializes the web services' MessageRouter object * Reads from GlobalProperties to get gsdl3_home and set the sitename. */ public AdminSOAPServer() { String gsdl3_home = GlobalProperties.getGSDL3Home(); if (gsdl3_home == null || gsdl3_home.equals("")) { LOG.error( "Couldn't access GSDL3Home from GlobalProperties.getGSDL3HOME," + "can't initialize the SOAP Server."); return; } String site_home = GSFile.siteHome(gsdl3_home, this.site_name); File site_file = new File(site_home); if (!site_file.isDirectory()) { LOG.error("The site directory "+site_file.getPath() +" doesn't exist. Can't initialize the SOAP Server."); return; } this.converter = new XMLConverter(); this.doc = this.converter.newDOM(); mr = new MessageRouter(); mr.setSiteName(this.site_name); mr.configure(); } // (5) Status-type messages, manual pages 42, 43 /** Sends a status-type message to poll the status of a process that was * initiated but which may not yet have terminated. * @see The Greenstone 3 Developer's Manual - pages 42, 43 * @param to - the processing service to send this status message to. * @param language - the preferred language of the display content in the * response. * @param pid - the process id of the process whose status is requested. */ public String status(String to, String language, String pid) { // Create message element: Element message = this.doc.createElement(GSXML.MESSAGE_ELEM); // Element request = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_STATUS, to, language, ""); // create the element of param elements: Element paramList = this.doc.createElement( GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER); paramList.appendChild(GSXML.createParameter(this.doc, "pid", pid)); // now finish constructing the request message: request.appendChild(paramList); message.appendChild(request); // Send it off to the Message Router and return the response return this.processInternal(message); } // (6) System-type messages, manual pages 41, 42 /** Sends a configure system-type message to configure all of the Message Router. * @see The Greenstone 3 Developer's Manual - pp. 13, 41, 42 */ public String reconfigureMessageRouter() { return this.reconfigure(""); // subset=MessageRouter="", type=configure // Not applicable: systemModuleName="", systemModuleType="" //return this.system("", GSXML.SYSTEM_TYPE_CONFIGURE, subset, "", ""); } /** Sends a configure system-type message to configure the Message Router. * @see The Greenstone 3 Developer's Manual - pp. 13, 41, 42 * @param subset - the particular subset of the module which is to be * reconfigured. For a collection/serviceRack it can be metadataList, * serviceList. For the MessageRouter this can be siteList, collectionList, * clusterList, serviceList */ public String reconfigure(String subset) { // type=configure // Not applicable: systemModuleName="", systemModuleType="" return this.system(GSXML.SYSTEM_TYPE_CONFIGURE, subset, "", ""); } /** Sends a (re-)activate system-type message to activate a Greenstone module. * @see The Greenstone 3 Developer's Manual - pp. 13, 41, 42 * @param systemModuleName - name of the module to be (re-)activated. * @param systemModuleType - type of the module. For instance, this may be * site or collection. */ public String activate(String systemModuleType, String systemModuleName) { // type=activate // Not applicable: subset="" return this.system(GSXML.SYSTEM_TYPE_ACTIVATE, "", systemModuleType, systemModuleName); } /** Sends a deactivate system-type message to activate a Greenstone module. * @see The Greenstone 3 Developer's Manual - pp. 13, 41, 42 * @param systemModuleName - name of the module to be deactivated. * @param systemModuleType - type of the module. For instance, this may be * site or collection. */ public String deactivate(String systemModuleType, String systemModuleName) { // type=deactivate // Not applicable: subset="" return this.system(GSXML.SYSTEM_TYPE_DEACTIVATE, "", systemModuleType, systemModuleName); } /** For sending system-type messages. * For parameter type=configure, the subset value is set (or ""). * For parameter type=(de)activate, the systemModuleName and systemModuleType * are set. * @param type - one of GSXML.SYSTEM_TYPE_CONFIGURE, * GSXML.SYSTEM_TYPE_ACTIVATE, GSXML.SYSTEM_TYPE_DEACTIVATE * (configure, activate or deactivate, respectively). * @param subset - for system type configure, this can specify the To module's * subset that needs to be reconfigured. * @param systemModuleName - for system type (de)activate, this specifies the * name of the module to be (de)activated. * @param systemModuleType - for system type (de)activate, this specifies the * type of the module to be (de)activated. For instance, this may be * site or collection. */ protected String system(String type, String subset, String systemModuleType, String systemModuleName) { // Create message element: Element message = this.doc.createElement(GSXML.MESSAGE_ELEM); // Element request = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_SYSTEM, "", "", ""); // no to, no language // (to is the MessageRouter which deals with system requests) Element system = this.doc.createElement(GSXML.SYSTEM_ELEM); system.setAttribute(GSXML.TYPE_ATT, type); if(type.equals(GSXML.SYSTEM_TYPE_CONFIGURE)) { // EITHER: , OR: // system.setAttribute(GSXML.SYSTEM_SUBSET_ATT, subset); } else { // GSXML.SYSTEM_TYPE_ACTIVATE || GSXML.SYSTEM_TYPE_DEACTIVATE // For example: // system.setAttribute(GSXML.SYSTEM_MODULE_TYPE_ATT, systemModuleType); system.setAttribute(GSXML.SYSTEM_MODULE_NAME_ATT, systemModuleName); } // now finish constructing the request message: request.appendChild(system); message.appendChild(request); // Send it off to the Message Router and return the response return this.processInternal(message); } /** Sends a format-type message to a Service containing format (Greenstone * Format, GSF) XSLT statements to specify how a collection looks. * @see The Greenstone 3 Developer's Manual - page 42 * @param collection - the name of the collection this message is sent to. * @param service - name of the service this format message is to be sent to. * @param language - the preferred language of the display content in the * response. */ public String format(String collection, String service, String language) { String to = service; if(!collection.equals("")) to = collection+"/"+to; // prepend collection with slash to service // Create message element: Element message = this.doc.createElement(GSXML.MESSAGE_ELEM); // Element request = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_FORMAT, to, language, ""); // no language message.appendChild(request); // Send it off to the Message Router and return the response return this.processInternal(message); } /** Called by most other methods in order to send the constructed message * to the Greenstone's MessageRouter, intercept the response and return it. * @param message is the XML message Element to send to GS3's MessageRouter. * @return the XML response in String format. */ protected String processInternal(Element message) { // Let the messagerouter process the request message and get the response LOG.debug(this.converter.getPrettyString(message)); // Let the messagerouter process the request message and get the response Element response = converter.nodeToElement(mr.process(message)); // won't be null, MR always returns some XML response // Return it as a String formatted for display return this.converter.getPrettyString(response); } /** Creates a String response message to represent an XML error response * message using the error specified in the message parameter. A String is * created because this method ensures that a response message is reliably * constructed (no exceptions are thrown) that can be sent to clients. * @param errorMessage - the errormessage to be conveyed * @return an XML response message containing an GS3 error element. */ protected String error(String errorMessage) { StringBuffer buf = new StringBuffer("<" + GSXML.MESSAGE_ELEM + ">"); buf.append("<" + GSXML.RESPONSE_ELEM + " " + GSXML.FROM_ATT + "=\"" + "Greenstone 3 Web Services\"" + ">"); buf.append("<" + GSXML.ERROR_ELEM + " " + GSXML.ERROR_TYPE_ATT + "=\""+ GSXML.ERROR_TYPE_OTHER + "\"" + ">"); buf.append(errorMessage+"\n"); buf.append(""); buf.append(""); buf.append(""); return buf.toString(); } /* Look in the helpFile - Have a properties file that maps methodname to help string specific to the method. - Read it all in statically at the start of the class, into a Properties Map. - When this method is called, display the usage: "help methodname" and list all the available methods by going over the keys in the Map. - When the helpWithMethod(String methodname) method is called, return the value of the Map for the methodname key. This value would be the help description for that method. */ /** @return a help string for listing all the web service methods. */ public static String help() { if(!helpErrormessage.equals("")) { return helpErrormessage; } StringBuffer helpString = new StringBuffer( "USAGE: helpWithMethod(String )\n"); helpString.append( "\nNearly all the web service operations return a String\n"); helpString.append( "representing a Greenstone 3 XML response message.\n"); helpString.append("\nA list of all the method names: \n"); Enumeration props = properties.keys(); while(props.hasMoreElements()){ String methodName = (String)props.nextElement(); helpString.append("\t"); helpString.append(methodName); helpString.append("\n"); } return helpString.toString(); } /** @param methodname is the name of the method to be described. * @return a help string for the given method, explaining what the method * does, what parameters it expects and their types and what it returns. */ public static String helpWithMethod(String methodname) { if(!helpErrormessage.equals("")) { return helpErrormessage; } // else we can get the method's description from the properties // map loaded from the QBRWebServicesHelp.properties file: String helpString = properties.getProperty(methodname, "No description for " + methodname); // if the method does not exist return helpString; } // Here we just try calling some of the methods public static void main(String[] args) { AdminSOAPServer ws = new AdminSOAPServer(); // (1) activate, deactivate and the 2 reconfigures System.out.println("activate:\n" + ws.activate("collection", "gs2mgppdemo")); System.out.println("deactivate:\n" + ws.deactivate("site", "site1")); System.out.println("configure MR:\n" + ws.reconfigureMessageRouter()); System.out.println("configure:\n" + ws.reconfigure("collectionList")); // (2) 1 format System.out.println("format:\n" + ws.format("gs2mgppdemo", "FieldQuery", "")); // (3) How to test status(), especially when processType messages above don't work? // System.out.println("status:\n" + ws.status(toProcess/service, language, pid)); System.out.println("status:\n" + ws.status("build/NewCollection", "", "1")); // (4) try help System.out.println(ws.help()); System.out.println(ws.helpWithMethod("describe")); System.out.println(ws.helpWithMethod("help")); System.out.println(ws.helpWithMethod("helpWithMethod")); System.out.println("Finished executing"); } }