[MOBY-guts] biomoby commit

Paul Gordon gordonp at dev.open-bio.org
Mon Jan 7 22:16:36 UTC 2008


gordonp
Mon Jan  7 17:16:35 EST 2008
Update of /home/repository/moby/moby-live/Java/src/main/org/biomoby/service
In directory dev.open-bio.org:/tmp/cvs-serv15814/src/main/org/biomoby/service

Modified Files:
	MobyServlet.java 
Log Message:
Added concurrent thread support for doPost calls (specifically, addException() wasn't kosher), and added runtime templated data type support
moby-live/Java/src/main/org/biomoby/service MobyServlet.java,1.10,1.11
===================================================================
RCS file: /home/repository/moby/moby-live/Java/src/main/org/biomoby/service/MobyServlet.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- /home/repository/moby/moby-live/Java/src/main/org/biomoby/service/MobyServlet.java	2007/12/17 18:31:09	1.10
+++ /home/repository/moby/moby-live/Java/src/main/org/biomoby/service/MobyServlet.java	2008/01/07 22:16:35	1.11
@@ -23,8 +23,7 @@
 import javax.xml.parsers.ParserConfigurationException;
 
 import javax.xml.soap.*;
-import java.util.StringTokenizer;
-import java.util.Vector;
+import java.util.*;
 import java.math.*;
 
 /**
@@ -73,12 +72,16 @@
     protected static Registry registry; 
 
     protected MobyService thisService;
-    protected MobyContentInstance currentContent = null;
     protected boolean isInitialized = false;
+    protected boolean hasDataTypeTemplates = false;  // Will we need to consider runtime changes to data type specs?
     /** Changing this value makes logging more or less verbose */
     protected boolean isDebug = false;
     /** Where data type, namespace and service type definition come from, if null, the default registry is used */
 
+    // Only use for keep track of MobyServices if they have template values that are filled in at service invocation
+    protected Map<Thread,MobyService> templatedServicesMap; 
+    protected Map<Thread,MobyContentInstance> responseContentInstanceMap; 
+
     public void doGet(HttpServletRequest request,
 		      HttpServletResponse response)
 	throws ServletException, java.io.IOException{
@@ -284,7 +287,7 @@
     }
 
     /**
-     * Throws an exception if the data in the job does not match the input spec of the service
+     * Throws an exception if the data in the job does not match the input spec of the service.
      */
     public static void validateArguments(MobyDataJob job, 
 					 MobyService service, 
@@ -347,7 +350,27 @@
 						   param.getDataType().getName()+
 						   " in the MOBY Central object ontology)");
 	    }
-
+	    if(param.getDataType() instanceof MobyDataTypeTemplate){
+		// First instance. The runtime data type constraint is being defined here,
+		// i.e. the effective datatype for output parameters using the same template
+		// object is now set.
+		MobyDataTypeTemplate dtTemplate = (MobyDataTypeTemplate) param.getDataType();
+		// Do not change the global definition of the template, but rather just an instance for this invocation
+		MobyDataType dtBoundTemplate = valueType;  
+		param.setDataType(dtBoundTemplate);
+		// Propagate the change to other params using this data type
+		for(MobyPrimaryData primary: service.getPrimaryInputs()){
+		    if(primary.getDataType() == dtTemplate){  //refers to original template we just bound
+			primary.setDataType(dtBoundTemplate);
+		    }
+		}
+		for(MobyPrimaryData primary: service.getPrimaryOutputs()){
+		    if(primary.getDataType() == dtTemplate){  //refers to original template we just bound
+			primary.setDataType(dtBoundTemplate);
+		    }
+		}
+		dtTemplate.bindTemplate(param.getDataType());
+	    }
 	    // Looks good so far for a match.  Make sure namespace matches
 	    // if part of the template too.
 	    MobyNamespace[] reqNS = param.getNamespaces();
@@ -462,6 +485,65 @@
 	}
     }
 
+    /**
+     * Allocates thread-specific (depending on service specification) variables to use during an invocation.
+     */
+    protected synchronized MobyService startServiceInvocation(MobyContentInstance mci){
+	if(responseContentInstanceMap == null){  // init() not called yet?
+	    return null;
+	}
+	responseContentInstanceMap.put(Thread.currentThread(), mci);
+
+	if(!hasDataTypeTemplates){
+	    return thisService;
+	}
+	MobyService invocationSpecificService = thisService.clone();
+	templatedServicesMap.put(Thread.currentThread(), invocationSpecificService);
+	return invocationSpecificService;
+    }
+
+    /**
+     * Cleans up invocation-specific variables.
+     */
+    protected synchronized void endServiceInvocation(){
+	if(responseContentInstanceMap != null){
+	    responseContentInstanceMap.remove(Thread.currentThread());
+	}
+	if(hasDataTypeTemplates && templatedServicesMap != null){
+	    templatedServicesMap.remove(Thread.currentThread());
+	}
+    }
+
+    /**
+     * Returns the MobyContentInstance for the invocation response 
+     * (or null if not in the middle of an invocation).
+     */
+    protected MobyContentInstance getResponseContentInstance(){
+	// The only time we don't return thisService is if an invocation-specific datatype is defined
+	if(responseContentInstanceMap != null &&
+	   responseContentInstanceMap.containsKey(Thread.currentThread())){
+	    return responseContentInstanceMap.get(Thread.currentThread());
+	}
+	else{
+	    return null;
+	}
+    }
+
+    /**
+     * Returns the (possibly invocation-specific) MobyService definition
+     */
+    protected MobyService getService(){
+	// The only time we don't return thisService is if an invocation-specific datatype is defined
+	if(templatedServicesMap != null &&
+	   hasDataTypeTemplates && 
+	   templatedServicesMap.containsKey(Thread.currentThread())){
+	    return templatedServicesMap.get(Thread.currentThread());
+	}
+	else{
+	    return thisService;
+	}
+    }
+
     private void writeResponse(HttpServletResponse response, MobyContentInstance mobyResults){
 
 	java.io.OutputStream out = null;
@@ -516,12 +598,14 @@
      * Writes the error to the servlet log, and adds a MobyException block to the response
      */
     protected void addException(ServiceException se){
+	MobyContentInstance currentContent = getResponseContentInstance();
+	(new Exception("createing exception " +se)).printStackTrace(); 
 	if(currentContent != null){
 	    currentContent.addException(se);
 	    if(isInitialized){
 		log("While executing Moby Service (initialized) " + getServiceName(), se);
 	    }
-	    else if(thisService != null){
+	    else if(getService() != null){
 		System.err.println("While executing Moby Service (uninitialized) " + getClass().getName());
 		se.printStackTrace();
 	    }
@@ -551,7 +635,7 @@
 	if(isInitialized){
 	    log("While executing Moby Service (initialized) " + getServiceName(), ex);
 	}
-	else if(thisService != null){
+	else if(getService() != null){
 	    System.err.println("While executing Moby Service (uninitialized) " + getClass().getName());
 	    ex.printStackTrace();
 	}
@@ -575,7 +659,7 @@
 	if(isInitialized){
 	    log("While executing Moby Service (initialized) " + getServiceName() + ", job '" + job.getID()+"'", ex);
 	}
-	else if(thisService != null){
+	else if(getService() != null){
 	    System.err.println("While executing Moby Service (uninitialized) " + getClass().getName() + ", job '" + job.getID()+"'");
 	    ex.printStackTrace();
 	}
@@ -623,7 +707,11 @@
 	    faultName = soapFactory.createName("Client",
 					       "", 
 					       SOAPConstants.URI_NS_SOAP_ENVELOPE);
-	    
+
+	    // Maps storing invocation-specific context variable
+	    templatedServicesMap = new HashMap<Thread,MobyService>();
+	    responseContentInstanceMap = new HashMap<Thread,MobyContentInstance>();
+
 	    // Determine the Moby Central (and hence ontologies) to associate with the service
 	    String centralURL = null;
 	    mobyService ann = 
@@ -719,9 +807,11 @@
 	    throw new Exception("Could not find required " + MOBY_INPUT_PARAM + 
 				" parameter in servlet config, available parameters were: " + paramNames);
 	}
+	// The map allows definitions like <T extends GenericSequence>
+	Map<String,MobyDataTypeTemplate> templateDataTypeMap = new HashMap<String,MobyDataTypeTemplate>();
 	for(int i = 0; i < ins.length; i++){
 	    // non-void param
-	    inputTypes.add(stringToPrimaryDataTemplate(ins[i]));
+	    inputTypes.add(stringToPrimaryDataTemplate(ins[i], templateDataTypeMap));
 	}
 
 	String[] outs = ann.out();
@@ -732,11 +822,15 @@
 	    throw new Exception("Could not find required " + MOBY_OUTPUT_PARAM + 
 				" parameter in servlet config ");
 	}
+	// The templateDataTypeMap map allows us to now use T in an output data type spec
 	for(int i = 0; i < outs.length; i++){
 	    if(outs[i] != null && outs[i].length() > 0){// non-void param
-		outputTypes.add(stringToPrimaryDataTemplate(outs[i]));
+		outputTypes.add(stringToPrimaryDataTemplate(outs[i], templateDataTypeMap));
 	    }
 	}
+	if(!templateDataTypeMap.isEmpty()){
+	    hasDataTypeTemplates = true;  // affects runtime service behaviours
+	}
 
 	String[] secondaries = ann.secondaryParams();
 	if(getCoCInitParameter(MOBY_SECONDARYINPUT_PARAM) != null){
@@ -1032,7 +1126,8 @@
      * 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
      */
-    public static MobyPrimaryData stringToPrimaryDataTemplate(String spec) throws Exception{
+    public static MobyPrimaryData stringToPrimaryDataTemplate(String spec, Map<String,MobyDataTypeTemplate> runTimeTemplates) 
+	throws Exception{
 	StringTokenizer st = new StringTokenizer(spec, ":");
 	String name = st.nextToken();
 	if(name == null || name.length() == 0){
@@ -1044,7 +1139,7 @@
 				spec + ") must have the minimal form " +
 				"\"name:objectType\", aborting!");
 	}
-	String objectType = st.nextToken();
+	String objectType = st.nextToken().trim();
 	if(objectType == null || objectType.length() == 0){
 	    throw new Exception("The parameter specification (" + 
 			       spec + ") has a blank object type, aborting!");
@@ -1055,7 +1150,7 @@
 	boolean isCollection = false;
 	if(objectType.indexOf("Collection(") == 0 && 
 	   objectType.lastIndexOf(")") == objectType.length()-1){
-	    objectType = objectType.substring(11, objectType.length()-1);
+	    objectType = objectType.substring(11, objectType.length()-1).trim();
 	    dataTemplate = new MobyPrimaryDataSet(name);
 	    isCollection = true;
 	}
@@ -1063,13 +1158,58 @@
 	    dataTemplate = new MobyPrimaryDataSimple(name);
 	}
 
-	MobyDataType type = MobyDataType.getDataType(objectType, registry);
+	// Allow runtime templating like <T extends GenericSequence>, so for example we can
+	// restrict output to the same type T as the input that was provided.
+	String newTemplateToken = null;
+	if(objectType.indexOf("<") == 0 &&
+	   objectType.lastIndexOf(">") == objectType.length()-1){
+	    String[] tokens = objectType.substring(2, objectType.length()-1).split("\\s+");
+	    if(tokens.length != 3 || !"extends".equals(tokens[1]) ||
+	       tokens[0] == null || tokens[0].length() == 0 ||
+	       tokens[2] == null || tokens[2].length() == 0 ){
+		throw new Exception("The parameter specification (" + 
+				spec + ") attempts to define a template data type, but it is not of the " +
+				    "expected form <T extends ClassName>");
+	    }
+	    if(runTimeTemplates.containsKey(tokens[0])){
+		throw new Exception("The parameter specification (" + 
+				    spec + ") attempts to define a template data type " + tokens[0] +
+				    ", but that token has already been bound to " +
+				    " a template definition in this context (" +
+				    runTimeTemplates.get(tokens[0]).getName() + ")");
+	    }
+	    newTemplateToken = tokens[0];
+	    if(MobyDataType.getDataType(newTemplateToken, registry) != null){
+		System.err.println("The parameter specification (" + 
+				   spec + ") defines a template data type " + tokens[0] +
+				   ", but that token conflicts with the name of an existing " +
+				   "data type in Moby Central.  The template definition will " +
+				   "be used, but this may have unintended consequences.  " +
+				   "It is suggested that you change the template name " +
+				   " to an unambiguous one.");	   
+	    }
+	    objectType = tokens[2];
+	}
+	MobyDataType type = null;
+	// Using a previously defined template data type
+	if(runTimeTemplates.containsKey(objectType)){
+	    type = runTimeTemplates.get(objectType);
+	}
+	else{  // Using a literal data type name
+	    type = MobyDataType.getDataType(objectType, registry);
+	}
 	if(type == null){
 	    throw new Exception("The parameter specification (" + 
 				spec + ") has a data type not found in the " +
 				"MOBY registry, aborting! (Either correct the type," +
 				" or register it as a new type in MOBY Central");
 	}
+	// Defining a template data type, MobyDataTypeTemplate will 
+	// bind to a subclass during service execution.
+	if(newTemplateToken != null){  
+	    runTimeTemplates.put(newTemplateToken, new MobyDataTypeTemplate(type));
+	    type = runTimeTemplates.get(newTemplateToken);
+	}
 	dataTemplate.setDataType(type);
 	if(isCollection){
 	    // Example data element in set needed for RDF creator to recognize the data type
@@ -1077,7 +1217,7 @@
 	    exampleData.setDataType(type);
 	    ((MobyPrimaryDataSet) dataTemplate).addElement(exampleData);  
 	}
-
+	
 	// namespace is optional
 	if(st.hasMoreTokens()){
 	    String namespaceValue = st.nextToken();
@@ -1120,13 +1260,14 @@
      */
     public void processRequests(MobyContentInstance requestContents, MobyContentInstance resultContents) 
 	throws Exception{
-	currentContent = resultContents;  //for exception handling within processRequest w/out content being passed around
+	// Set up the per-invocation variable context
+	startServiceInvocation(resultContents);  
 	for(String jobName: requestContents.keySet()){
 	    try{
 		MobyDataJob currentRequest = requestContents.get(jobName);
-		if(thisService != null){
+		if(getService() != null){
 		    // Should happen, unless main() was called
-		    validateArguments(currentRequest, thisService, "While executing service");
+		    validateArguments(currentRequest, getService(), "While executing service");
 		}
 		MobyDataJob currentResult = resultContents.get(jobName);
 		if(currentResult == null){
@@ -1139,7 +1280,8 @@
 		addException(resultContents, requestContents.get(jobName), e);
 	    }
 	}
-	currentContent = null;
+	// Makes sure per-invocation variables can get GC'ed
+	endServiceInvocation();
     }
 
     /**




More information about the MOBY-guts mailing list