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("" + GSXML.ERROR_ELEM + ">");
buf.append("" + GSXML.RESPONSE_ELEM + ">");
buf.append("" + GSXML.MESSAGE_ELEM + ">");
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");
}
}