[MOBY-guts] biomoby commit

Paul Gordon gordonp at dev.open-bio.org
Wed Dec 6 16:07:10 UTC 2006


gordonp
Wed Dec  6 11:07:10 EST 2006
Update of /home/repository/moby/moby-live/Java/src/main/org/biomoby/service
In directory dev.open-bio.org:/tmp/cvs-serv24370/src/main/org/biomoby/service

Modified Files:
	MobyServlet.java 
Added Files:
	Asynchronous.java 
Log Message:
Major commit to allow automated construction of MobyServlet.war, and revised cvode for Java annotation-based service meta-data specification
moby-live/Java/src/main/org/biomoby/service Asynchronous.java,NONE,1.1 MobyServlet.java,1.2,1.3
===================================================================
RCS file: /home/repository/moby/moby-live/Java/src/main/org/biomoby/service/MobyServlet.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- /home/repository/moby/moby-live/Java/src/main/org/biomoby/service/MobyServlet.java	2006/11/22 22:23:55	1.2
+++ /home/repository/moby/moby-live/Java/src/main/org/biomoby/service/MobyServlet.java	2006/12/06 16:07:10	1.3
@@ -7,6 +7,9 @@
  * the documentation on <a href="http://biomoby.open-bio.org/CVS_CONTENT/moby-live/Java/docs/deployingServices.html">how to use this servlet</a>.
  */
 
+import org.biomoby.service.test.TestServletConfig;
+import org.biomoby.service.test.TestServletContext;
+
 import org.biomoby.shared.*;
 import org.biomoby.shared.data.*;
 import org.biomoby.shared.parser.*;
@@ -29,6 +32,13 @@
 import java.util.StringTokenizer;
 import java.util.Vector;
 
+ at mobyService(name="MobyServlet",
+	     type="Testing", 
+	     provider="moby.ucalgary.ca", 
+	     author="gordonp at ucalgary.ca",
+	     in={},
+	     out={},
+             description={"No-operation base service implementation"})
 public class MobyServlet extends HttpServlet implements Remote{
 
     public static final String MOBY_CENTRAL_URL_PARAM = "mobyCentralURL";
@@ -37,6 +47,7 @@
     public static final String MOBY_SERVICE_DESC_PARAM = "mobyServiceDescription";
     public static final String MOBY_PROVIDER_URI_PARAM = "mobyProviderURI";
     public static final String MOBY_SERVICETYPE_PARAM = "mobyServiceType";
+    public static final String MOBY_SERVICENAME_PARAM = "mobyServiceName";
     public static final String MOBY_INPUT_PARAM = "mobyInput";
     public static final String MOBY_SECONDARY_PARAM = "mobySecondaryInput";
     public static final String MOBY_OUTPUT_PARAM = "mobyOutput";
@@ -45,8 +56,9 @@
     public static final String ADMIN_MODE = "admin";
     public static final int INIT_OUTPUT_BUFFER_SIZE = 100000;
 
+    private static boolean shouldExit = true;
+
     protected static MobyRequest mobyRequest;
-    protected static MobyService thisService;
     protected static DocumentBuilder docBuilder;
 
     // Members used to wrap the response in SOAP
@@ -57,18 +69,20 @@
     protected final static String stringEncAttrValue = "xsd:string";
     protected static Name faultName;
 
+    protected MobyService thisService;
     protected MobyContentInstance currentContent = null;
     protected boolean isInitialized = false;
     /** Changing this value makes logging more or less verbose */
     protected boolean isDebug = false;
 
-    protected void doGet(HttpServletRequest request,
+    public void doGet(HttpServletRequest request,
 			 HttpServletResponse response)
 	throws ServletException, java.io.IOException{
 	if(thisService == null){
 	    try{
 		thisService = createServiceFromConfig(request);
 	    } catch(Exception e){
+		log("While attempting to configure service on first run:", e);
 		throw new ServletException("While attempting to configure service on first run: " + e);
 	    }
 	}
@@ -183,10 +197,11 @@
      */
     protected boolean validSOAPEndPoint(HttpServletRequest request, 
 					HttpServletResponse response){
-	String methodName = "http://biomoby.org/#"+getServletName();
+	String methodName = "http://biomoby.org/#"+getServiceName();
 	String qMethodName = "\""+methodName+"\"";
 	String requestedActionName = null;
 	boolean endPointMatches = false;
+	// TODO: This is where we should add code to deal with asynchronous calls
 	for(java.util.Enumeration actionsCalled = request.getHeaders("SOAPAction");
 	    actionsCalled.hasMoreElements();){
 
@@ -223,7 +238,7 @@
 		return false;
 		
 	    } catch(Exception e){
-		log("While writing SOAP fault response to client for " + getServletName(), e);
+		log("While writing SOAP fault response to client for " + getServiceName(), e);
 		return false;
 	    }
 	}
@@ -377,7 +392,6 @@
 	    // The SOAP envelope (headerless) consists of a body with one string element inside it
 	    if(soapMessageFactory == null){
 		soapMessageFactory = MessageFactory.newInstance();
-		log("SOAP Message Factory (when writeResponse is called) is " + soapMessageFactory);
 	    }
 	    SOAPMessage message = soapMessageFactory.createMessage();
 	    message.getSOAPHeader().detachNode();
@@ -394,7 +408,7 @@
 	    out.write(outBuffer.toByteArray());
 
 	} catch(Exception e){
-	    log("While writing SOAP response to client for " + getServletName(), e);
+	    log("While writing SOAP response to client for " + getServiceName(), e);
 	    return;
 	}
 
@@ -407,10 +421,10 @@
 	if(currentContent != null){
 	    currentContent.addException(se);
 	    if(isInitialized){
-		log("While executing Moby Service " + getServletName(), se);
+		log("While executing Moby Service (initialized) " + getServiceName(), se);
 	    }
-	    else{
-		System.err.println("While executing Moby Service " + getClass().getName());
+	    else if(thisService != null){
+		System.err.println("While executing Moby Service (uninitialized) " + getClass().getName());
 		se.printStackTrace();
 	    }
 	}
@@ -437,10 +451,10 @@
 						       ex.toString()));
 	}
 	if(isInitialized){
-	    log("While executing Moby Service " + getServletName(), ex);
+	    log("While executing Moby Service (initialized) " + getServiceName(), ex);
 	}
-	else{
-	    System.err.println("While executing Moby Service " + getClass().getName());
+	else if(thisService != null){
+	    System.err.println("While executing Moby Service (uninitialized) " + getClass().getName());
 	    ex.printStackTrace();
 	}
     }
@@ -461,10 +475,10 @@
 						       ex.toString()));
 	}
 	if(isInitialized){
-	    log("While executing Moby Service " + getServletName() + ", job '" + job.getID()+"'", ex);
+	    log("While executing Moby Service (initialized) " + getServiceName() + ", job '" + job.getID()+"'", ex);
 	}
-	else{
-	    System.err.println("While executing Moby Service " + getClass().getName() + ", job '" + job.getID()+"'");
+	else if(thisService != null){
+	    System.err.println("While executing Moby Service (uninitialized) " + getClass().getName() + ", job '" + job.getID()+"'");
 	    ex.printStackTrace();
 	}
     }
@@ -492,17 +506,17 @@
 				   "org.apache.axis.soap.MessageFactoryImpl");
 	    }
 	    soapMessageFactory = MessageFactory.newInstance();  // Should find Axis by default
-	    log("SOAP Message Factory (when init is called) is " + soapMessageFactory);
+	    //log("SOAP Message Factory (when init is called) is " + soapMessageFactory);
 
-	    if(System.getProperty("javax.xml.soap.MessageFactory") == null){
+	    if(System.getProperty("javax.xml.soap.SOAPFactory") == null){
 		System.setProperty("javax.xml.soap.SOAPFactory", 
 				   "org.apache.axis.soap.SOAPFactoryImpl");
 	    }
 	    SOAPFactory soapFactory = SOAPFactory.newInstance();
-	    bodyContentsName = soapFactory.createName(getServletName()+"Return",
+	    bodyContentsName = soapFactory.createName(getServiceName()+"Return",
 						      MobyPrefixResolver.MOBY_TRANSPORT_PREFIX,
 						      MobyPrefixResolver.MOBY_TRANSPORT_NAMESPACE);
-	    bodyName = soapFactory.createName(getServletName()+"Response",
+	    bodyName = soapFactory.createName(getServiceName()+"Response",
 					      MobyPrefixResolver.MOBY_TRANSPORT_PREFIX, 
 					      MobyPrefixResolver.MOBY_TRANSPORT_NAMESPACE);
 	    stringEncAttrName = soapFactory.createName("type",
@@ -531,46 +545,60 @@
 	isInitialized = true;
     }
 
-    private synchronized MobyService createServiceFromConfig(HttpServletRequest request) throws Exception{
-	MobyService service = new MobyService(getServletName());
+    public synchronized MobyService createServiceFromConfig(HttpServletRequest request) throws Exception{
+	MobyService service = new MobyService(getServiceName());
 
 	Vector<MobyPrimaryData> inputTypes = new Vector<MobyPrimaryData>();
 	Vector<MobyPrimaryData> outputTypes = new Vector<MobyPrimaryData>();
 
-	javax.servlet.ServletContext context = getServletContext();
+	mobyService ann = 
+	    this.getClass().getAnnotation(mobyService.class);
+	if(ann == null){
+	    throw new Exception("The servlet does not have a @mobyService() class annotation as required, " +
+				"cannot configure the service");
+	}
+	else{
+	    //log("Reading service meta-data from Java class annotation for "+ann.name());
+	}
+
+	javax.servlet.ServletConfig config = getServletConfig();
 
-	java.util.Enumeration paramNamesEnum = context.getInitParameterNames();
 	String paramNames = "";
-	if(paramNamesEnum.hasMoreElements()){
-	    paramNames = paramNamesEnum.nextElement().toString();
-	}
-	while(paramNamesEnum.hasMoreElements()){
-	    paramNames += ", "+paramNamesEnum.nextElement();
+	if(config != null){
+	    java.util.Enumeration paramNamesEnum = config.getInitParameterNames();
+	    if(paramNamesEnum.hasMoreElements()){
+		paramNames = paramNamesEnum.nextElement().toString();
+	    }
+	    while(paramNamesEnum.hasMoreElements()){
+		paramNames += ", "+paramNamesEnum.nextElement();
+	    }
 	}
 
 	// Inputs and outputs and service type must be defined
-	String ins = context.getInitParameter(MOBY_INPUT_PARAM);
+	String[] ins = ann.in();
+	if(config != null && config.getInitParameter(MOBY_INPUT_PARAM) != null){
+	    ins = config.getInitParameter(MOBY_INPUT_PARAM).split(",");
+	}
 	if(ins == null){
 	    throw new Exception("Could not find required " + MOBY_INPUT_PARAM + 
-				" parameter in servlet context, available parameters were: " + paramNames);
+				" parameter in servlet config, available parameters were: " + paramNames);
 	}
-	StringTokenizer st = new StringTokenizer(ins, ",");
-	while(st.hasMoreTokens()){
-	    String value = st.nextToken();
+	for(int i = 0; i < ins.length; i++){
 	    // non-void param
-	    inputTypes.add(stringToPrimaryDataTemplate(value));
+	    inputTypes.add(stringToPrimaryDataTemplate(ins[i]));
 	}
 
-	String outs = context.getInitParameter(MOBY_OUTPUT_PARAM);
+	String[] outs = ann.out();
+	if(config != null && config.getInitParameter(MOBY_OUTPUT_PARAM) != null){
+	    outs = config.getInitParameter(MOBY_OUTPUT_PARAM).split(",");
+	}
 	if(outs == null){
 	    throw new Exception("Could not find required " + MOBY_OUTPUT_PARAM + 
-				" parameter in servlet context ");
+				" parameter in servlet config ");
 	}
-	st = new StringTokenizer(outs, ",");
-	while(st.hasMoreTokens()){
-	    String value = st.nextToken();
-	    if(value.length() > 0){// non-void param
-		outputTypes.add(stringToPrimaryDataTemplate(value));
+	for(int i = 0; i < outs.length; i++){
+	    if(outs[i] != null && outs[i].length() > 0){// non-void param
+		outputTypes.add(stringToPrimaryDataTemplate(outs[i]));
 	    }
 	}
 
@@ -578,14 +606,18 @@
 	service.setOutputs(outputTypes.toArray(new MobyData[outputTypes.size()]));
 
 	// A description and provider URI must be available too
-	String param = context.getInitParameter(MOBY_SERVICETYPE_PARAM);
+	String param = ann.type();
+	// Did we override the service type in the web.xml?
+	if(config != null && config.getInitParameter(MOBY_SERVICETYPE_PARAM) != null){
+	    param = config.getInitParameter(MOBY_SERVICETYPE_PARAM);
+	}
 	if(param == null){
 	    throw new Exception("Could not find required " + MOBY_SERVICETYPE_PARAM + 
-				" parameter in servlet context ");
+				" parameter in servlet config ");
 	}
 	if(param.length() == 0){
 	    throw new Exception("Required " + MOBY_SERVICETYPE_PARAM + 
-				" parameter in servlet context cannot be blank");
+				" parameter in servlet config cannot be blank");
 	}
 	MobyServiceType serviceType = MobyServiceType.getServiceType(param);
 	if(serviceType == null){
@@ -594,80 +626,126 @@
 	}
 	service.setServiceType(serviceType);
 
-	param = context.getInitParameter(MOBY_SERVICE_DESC_PARAM);
-	if(param == null){
+	String[] desc = ann.description();
+	// Did we override the service type in the web.xml?
+	if(config != null && config.getInitParameter(MOBY_SERVICETYPE_PARAM) != null){
+	    desc = new String[1];
+	    desc[0] = config.getInitParameter(MOBY_SERVICETYPE_PARAM);
+	}
+	if(desc == null){
 	    throw new Exception("Could not find required " + MOBY_SERVICE_DESC_PARAM + 
-				" parameter in servlet context ");
+				" parameter in servlet config ");
 	}
-	if(param.length() == 0){
+	if(desc.length == 0){
 	    throw new Exception("Required " + MOBY_SERVICE_DESC_PARAM + 
-				" parameter in servlet context cannot be blank");
+				" parameter in servlet config cannot be blank");
+	}
+	StringBuffer descBuffer = new StringBuffer();
+	for(int i = 0; i < desc.length; i++){
+	    descBuffer.append(desc[i]);
+	}
+	service.setDescription(descBuffer.toString());
+
+	param = ann.provider();
+	// Did we override the provider info in web.xml?
+	if(config != null && config.getInitParameter(MOBY_PROVIDER_URI_PARAM) != null){
+	    param = config.getInitParameter(MOBY_PROVIDER_URI_PARAM);
 	}
-	service.setDescription(param);
-
-	param = context.getInitParameter(MOBY_PROVIDER_URI_PARAM);
 	if(param == null){
 	    throw new Exception("Could not find required " + MOBY_PROVIDER_URI_PARAM + 
-				" parameter in servlet context ");
+				" parameter in servlet config ");
 	}
 	if(param.length() == 0){
 	    throw new Exception("Required " + MOBY_PROVIDER_URI_PARAM + 
-				" parameter in servlet context cannot be blank");
+				" parameter in servlet config cannot be blank");
 	}
 	service.setAuthority(param);
+
 	// Now we have all the info we need to create the LSID for the service
 	String time = MobyDataDateTime.getString(
 			  new java.util.GregorianCalendar(
 				 java.util.TimeZone.getTimeZone("Zulu"))).replaceAll(":", "-");
-	service.setLSID("urn:lsid:biomoby.org:serviceinstance:"+param+","+getServletName()+":"+time);
+	service.setLSID("urn:lsid:biomoby.org:serviceinstance:"+param+","+getServiceName()+":"+time);
 
-	param = context.getInitParameter(MOBY_CONTACT_PARAM);
+	param = ann.author();
+	// Did we override the contact info in web.xml?
+	if(config != null && config.getInitParameter(MOBY_CONTACT_PARAM) != null){
+	    param = config.getInitParameter(MOBY_CONTACT_PARAM);
+	}
 	if(param == null){
 	    throw new Exception("Could not find required " + MOBY_CONTACT_PARAM + 
-				" parameter in servlet context ");
+				" parameter in servlet config ");
 	}
 	if(param.length() == 0){
 	    throw new Exception("Required " + MOBY_CONTACT_PARAM + 
-				" parameter in servlet context cannot be blank " +
+				" parameter in servlet config cannot be blank " +
 				"(must have form name at mail.domain)");
 	}
 	if(param.indexOf("@") < 2 || param.indexOf("@") > param.length()-2 ||
 	   param.indexOf(".") < 2 || param.indexOf(".") > param.length()-2 ||
 	   param.lastIndexOf("@") > param.lastIndexOf(".")){
 	    throw new Exception("Required " + MOBY_CONTACT_PARAM + 
-				" parameter in servlet context did not have the form \"name at mail.domain\")");
+				" parameter in servlet config did not have the form \"name at mail.domain\")");
 	}
 	
 	service.setEmailContact(param);
 
 	// From the request, determine the URL used to access this service
-	String endPointURL = HttpUtils.getRequestURL(request).toString();
+	String endPointURL = request == null ? "" : request.getRequestURL().toString();
 	// When we POST this URL, the service is executed
 	service.setURL(endPointURL);
 	// When we GET this URL, the RDF is returned
 	service.setSignatureURL(endPointURL+"?"+MODE_HTTP_PARAM+"="+RDF_MODE);
 	// Other fields (authoritative and contact info) are highly recommended, but optional
-	param = context.getInitParameter(MOBY_AUTHORITATIVE_PARAM);
-	if(param != null){
-	    if("YES".equals(param.toUpperCase()) || "Y".equals(param.toUpperCase()) || "1".equals(param)){
-		service.setAuthoritative(true);
+	if(config != null){
+	    param = config.getInitParameter(MOBY_AUTHORITATIVE_PARAM);
+	    if(param != null){
+		if("YES".equals(param.toUpperCase()) || "Y".equals(param.toUpperCase()) || "1".equals(param)){
+		    service.setAuthoritative(true);
+		}
+		else{
+		    log("Interpreting config parameter '" + param + "' as NO for authoritative property of the service");
+		    service.setAuthoritative(false);
+		}
 	    }
 	    else{
-		log("Interpreting config parameter '" + param + "' as NO for authoritative property of the service");
-		service.setAuthoritative(false);
+		service.setAuthoritative(ann.authoritative());
 	    }
 	}
 	else{
-	    service.setAuthoritative(false);
+	    service.setAuthoritative(ann.authoritative());
 	}
 
-
 	mobyRequest.setService(service);
 
 	return service;
     }
 
     /**
+     * Reads the service name from the mobyService annotation.
+     */
+    public String getServiceName(){
+	mobyService ann = 
+	    this.getClass().getAnnotation(mobyService.class);
+
+	javax.servlet.ServletConfig config = getServletConfig();
+	String param = null;
+	// Did we override the service type in the web.xml?
+	if(config != null && config.getInitParameter(MOBY_SERVICENAME_PARAM) != null){
+	    param = config.getInitParameter(MOBY_SERVICENAME_PARAM);
+	    if(param != null && param.length() != 0){
+		return param;
+	    }
+	}
+
+	if(ann == null){
+	    return "AnonymousService"; // this should never happen unless the class files are out of sync...
+	}
+
+	return ann.name();
+    }
+
+    /**
      * Strings have the form name:objectType:namespace, with ":namespace" optional
      * If the input is expected to be a Collection, then the syntax is name:Collection(objectType):namespace
      */
@@ -694,10 +772,12 @@
 
 	MobyPrimaryData dataTemplate = null;
 
+	boolean isCollection = false;
 	if(objectType.indexOf("Collection(") == 0 && 
 	   objectType.lastIndexOf(")") == objectType.length()-1){
 	    objectType = objectType.substring(11, objectType.length()-1);
 	    dataTemplate = new MobyPrimaryDataSet(name);
+	    isCollection = true;
 	}
 	else{
 	    dataTemplate = new MobyPrimaryDataSimple(name);
@@ -712,6 +792,12 @@
 	    System.exit(1);
 	}
 	dataTemplate.setDataType(type);
+	if(isCollection){
+	    // Example data element in set needed for RDF creator to recognize the data type
+	    MobyPrimaryDataSimple exampleData = new MobyPrimaryDataSimple(name);
+	    exampleData.setDataType(type);
+	    ((MobyPrimaryDataSet) dataTemplate).addElement(exampleData);  
+	}
 
 	// namespace is optional
 	if(st.hasMoreTokens()){
@@ -763,7 +849,13 @@
 		    // Should happen, unless main() was called
 		    validateArguments(currentRequest, thisService, "While executing service");
 		}
-		processRequest(currentRequest, resultContents.get(jobName));
+		MobyDataJob currentResult = resultContents.get(jobName);
+		if(currentResult == null){
+		    currentResult = new MobyDataJob();
+		    currentResult.setID(jobName);
+		    resultContents.put(jobName, currentResult);
+		}
+		processRequest(currentRequest, currentResult);
 	    }catch(Throwable e){
 		addException(resultContents, requestContents.get(jobName), e);
 	    }
@@ -787,6 +879,15 @@
     }
 
     /**
+     * Determines whether call to main() that terminate on error should call System.exit()
+     * or not.  This is the default behaviour, and should generally be switched only for unit testing
+     * purposes.
+     */
+    public static void setMainTerminationExit(boolean b){
+	shouldExit = b;
+    }
+
+    /**
      * Expects one argument, an example MOBY XML input file or URL.
      * processRequest() is then called for every job in the input data, 
      * and the results are printed to the screen.
@@ -794,9 +895,10 @@
     public static void main(String[] args) throws Exception{
 
 	if(args.length != 2){
-            System.err.println("MobyServlet is ignoring the main method, executing the subclass's main method.");
+            System.err.println("MobyServlet is ignoring the main method (called with " + args.length + " args) " +
+			       ", executing the subclass's main method.");
 	    System.err.println("If you wish to run the Servlet test, the usage is: ");
-            System.err.println("java ServletName ServetName <mobyInputExample.xml | mobyExampleURL>");
+            System.err.println("java ServletName SerlvetName <mobyInputExample.xml | mobyExampleURL>");
             return;
 	}
 
@@ -837,30 +939,45 @@
         } catch(Exception e){
            System.err.println("The test data was invalid:");
            e.printStackTrace();
-           System.exit(2);
+           if(shouldExit)
+	       System.exit(2);
+	   else
+	       throw e;
         }
 	// TO DO: Should we validate the input somehow?
 	// They'll find out when they run the servlet tests later, but the earlier the better...
 
 	MobyContentInstance testResults = new MobyContentInstance();
 
-	// Create as many output slots (blank) as there were input slots
-	// According to the MOBY spec, there must be a 1:1 relationship here
-	for(String jobName : testData.keySet()){
-	    MobyDataJob result = new MobyDataJob();
-	    result.setID(jobName);
-	    testResults.put(jobName, result);
-	}
-	
+	// Configure the service, minimally, to check the input data
+	servlet.init(new TestServletConfig(args[0], new TestServletContext()));
+	servlet.thisService = servlet.createServiceFromConfig(null);
+
 	// It should populate the testResults
         try{
 	    servlet.processRequests(testData, testResults);
         } catch(Exception e){
             System.err.println("An error occured while testing your code's processRequest method:");
             e.printStackTrace();
-        }
+	    if(shouldExit){
+		System.exit(2);
+	    } 
+	    else{
+		throw e;
+	    }
+	}
 
-	System.out.println(testResults.toString());
-        System.exit(0); //so that subclasses's main methods don;t executed if the test was run
+	if(testResults.hasExceptions(ServiceException.WARNING)){
+	    for(ServiceException se: testResults.getExceptions()){
+		se.printStackTrace();
+	    }
+	    throw new Exception("Executing service " + args[0] + " cause exceptions");
+	}
+	else{
+	    System.out.println(testResults.toString());
+	}
+        if(shouldExit){
+	    System.exit(0); //so that subclasses's main methods don't executed if the test was run
+	}
     }
 }




More information about the MOBY-guts mailing list