[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