[MOBY-guts] biomoby commit

Paul Gordon gordonp at dev.open-bio.org
Fri Mar 30 21:17:27 UTC 2007


gordonp
Fri Mar 30 17:17:27 EDT 2007
Update of /home/repository/moby/moby-live/Java/src/main/org/biomoby/client
In directory dev.open-bio.org:/tmp/cvs-serv17813/src/main/org/biomoby/client

Modified Files:
	MobyRequest.java 
Added Files:
	AsyncClient.java 
Log Message:
Initial commit of asynchronous client code, somewhat tested (single jobs)
moby-live/Java/src/main/org/biomoby/client AsyncClient.java,NONE,1.1 MobyRequest.java,1.22,1.23
===================================================================
RCS file: /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/MobyRequest.java,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -r1.22 -r1.23
--- /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/MobyRequest.java	2007/02/08 16:59:58	1.22
+++ /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/MobyRequest.java	2007/03/30 21:17:27	1.23
@@ -1,9 +1,6 @@
 package org.biomoby.client;
 
-// Defines Web service input, output, access
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
+import java.io.*;
 import java.util.*;
 
 import javax.xml.namespace.QName;
@@ -21,9 +18,13 @@
 import org.apache.xpath.XPathContext;
 import org.apache.xpath.objects.XNodeSet;
 import org.apache.xpath.objects.XObject;
+
 import org.biomoby.shared.*;
 import org.biomoby.shared.data.*;
 import org.biomoby.shared.parser.MobyTags;  // defined the Moby XML element names
+import org.biomoby.w3c.addressing.EndpointReference;
+import org.omg.lsae.notifications.AnalysisEvent;
+
 import org.w3c.dom.*;
 
 /**
@@ -150,14 +151,14 @@
     }
 
     /**
-     * @param service the MobyService that should be executed when invokeService is called
+     * @param mobyservice the MobyService that should be executed when invokeService is called
      */
-    public void setService(MobyService service){
-	if(service == null){
+    public void setService(MobyService mobyservice){
+	if(mobyservice == null){
 	    mobyService = null;
 	}
-	else if(mobyService == null || !service.equals(mobyService)){
-	    mobyService = service;
+	else if(mobyService == null || !mobyservice.equals(mobyService)){
+	    mobyService = mobyservice;
 	}
     }
 
@@ -263,19 +264,94 @@
 
     // Used internally for asynchronous thread calls that all need the XML data
     // and can't rely on the answer from thread-insensitive getResponseXML()
-    private MobyContentInstance invokeService(StringBuffer contentsXML) throws Exception, MobyException, SOAPException, NoSuccessException{
+    private MobyContentInstance invokeService(StringBuffer contentsXML) 
+	throws Exception, MobyException, SOAPException, NoSuccessException{
+	return invokeService(contentsXML, null, 0);
+    }
+
+    private MobyContentInstance invokeService(StringBuffer contentsXML, MobyRequestEventHandler handler, int requestId) 
+	throws Exception, MobyException, SOAPException, NoSuccessException{
 
 	if(mobyService == null){
 	    throw new MobyException("Tried to invoke null service from MobyRequest (call setService first)");
 	}
 
-        Call call = getServiceFromWSDL();
-	verifyInput();
-	String mobyXML = convertMOBYDataToMOBYRequest(inputData);
-	Element mobyDOM = performSOAPRequest(call, mobyXML, contentsXML);
-	// The following parses the DOM and extracts all the appropriate jMOBY objects to represent the XML in Java
-	outputData = MobyDataUtils.fromXMLDocument(mobyDOM);  
-	return outputData;
+	Element mobyDOM = null;
+	if(mobyService.isAsynchronous()){
+	    // Async is "simpler", because it had to merge DOMs together into a single MobyContentInstance anyway
+	    return performAsyncSOAPRequest(mobyService, inputData, handler, requestId);
+	}
+	else{
+	    String mobyXML = convertMOBYDataToMOBYRequest(inputData);
+	    Call call = getServiceFromWSDL();
+	    mobyDOM = performSOAPRequest(call, mobyXML, contentsXML);
+	    // The following parses the DOM and extracts all the appropriate jMOBY objects to represent the XML in Java
+	    return MobyDataUtils.fromXMLDocument(mobyDOM);  
+	}
+    }
+
+    protected MobyContentInstance performAsyncSOAPRequest(MobyService mservice, MobyContentInstance inData, 
+							  MobyRequestEventHandler handler, int requestId) 
+	throws Exception{
+	String mobyXML = convertMOBYDataToMOBYRequest(inData);  
+	EndpointReference epr = AsyncClient.sendRequest(mservice, mobyXML);
+
+	// Essentially cloning, so removing ids doesn't change the 
+	// MobyContentInstance "data" (which we will use again later on)
+	MobyContentInstance finalContents = new MobyContentInstance();
+	Set<String> queryIDs = new HashSet(inData.keySet());
+	try {
+	    // Should add some timeout here...
+	    while(!queryIDs.isEmpty()){
+		// todo: make this setable
+		Thread.sleep(5000);
+
+		AnalysisEvent[] events = 
+		    AsyncClient.poll(epr, queryIDs);
+
+		Vector<String> newDataAvailable = new Vector<String>();
+		for(AnalysisEvent event: events){
+		    if(event != null && event.isCompleted()){
+			queryIDs.remove(event.getQueryId());
+			newDataAvailable.add(event.getQueryId());
+		    }
+		}
+
+		// Parse and merge the new data into the existing contents
+		InputStream resultStream = AsyncClient.getResultStream(epr, newDataAvailable);
+		Element mobyDOM = asyncSoapTextToMobyDOM(resultStream);
+		MobyContentInstance newResults = MobyDataUtils.fromXMLDocument(mobyDOM);
+		// The merge
+		for(String jobid: newResults.keySet()){
+		    finalContents.put(jobid, newResults.get(jobid));
+		}
+
+		// Inform the handler that some data has been added to the response (for incremental display?)
+		if(newDataAvailable.size() > 0 && handler != null){
+		    MobyRequestEvent mre = new MobyRequestEvent(finalContents, this, null, requestId);
+		    StringWriter xmlWriter = new StringWriter();
+		    MobyDataUtils.toXMLDocument(xmlWriter, finalContents);
+
+		    mre.setContentsXML(xmlWriter.toString());
+		    handler.processEvent(mre);
+		}
+	    }
+	} catch (Exception e) {
+	    e.printStackTrace();
+	    AsyncClient.destroy(epr);
+	    throw new Exception("Exception occured while polling the service invocation: " + e);
+	}
+
+	return finalContents;
+    }
+
+    private Element asyncSoapTextToMobyDOM(InputStream inStream) throws Exception{
+	Element soapDOM = null;
+	synchronized(docBuilder){
+	    soapDOM = docBuilder.parse(inStream).getDocumentElement();
+	}
+	final boolean IS_ASYNC_SERVICE_CALL = true;
+	return decodeSOAPMessage(soapDOM,  null,  null, IS_ASYNC_SERVICE_CALL);
     }
 
     /**
@@ -296,19 +372,19 @@
     // This is the class that asynchronously calls the service and does a callback to 
     // the handler specified in the invocation.
     class InvocationThread extends Thread {
-	MobyService service;
+	MobyService mservice;
 	MobyRequest mobyRequest; 
 	MobyRequestEventHandler handler;
 	int requestId;
 
 	InvocationThread(MobyRequest mr, MobyRequestEventHandler h, int id){
 	    mobyRequest = mr;
-	    service = mobyRequest.getService();
+	    mservice = mobyRequest.getService();
 	    handler = h;
 	    requestId = id;
 
 	    // Name the thread after the service being run, mostly for ease of debugging
-	    setName(service.getName()+requestId);
+	    setName(mservice.getName()+requestId);
 	}
 
 	public void run() {
@@ -317,7 +393,7 @@
 	    handler.start(mobyRequest, requestId);
 	    StringBuffer contentsXML = new StringBuffer();  //to be filled in by the RPC call below
 	    try{
-		content = mobyRequest.invokeService(contentsXML); //RPC call...
+		content = mobyRequest.invokeService(contentsXML, handler, requestId); //RPC call...
 	    }
 	    catch(Exception e){
 		responseEvent = new MobyRequestEvent(content, mobyRequest, e, requestId);
@@ -348,33 +424,6 @@
     }
 
     /**
-     * @throws MobyException if the number of input parameters was wrong, or the paramters were of the wrong type.
-     */
-    protected void verifyInput() throws MobyException{
-	/*MobyData[] requiredInputData = mobyService.getPrimaryInputs();
-	
-	if(requiredInputData == null){
-	    if(inputData != null && inputData.size() != 0){
-		throw new MobyException("Service invocation expects no input parameters, " +
-					"but setInput has not been called with a null or zero-length array argument");
-	    }
-	    return;  // otherwise there are no parameters to check
-	}
-	if(inputData == null || inputData.size() == 0){
-	    throw new MobyException("Service invocation expects " + requiredInputData.length + " input " +
-				    "parameters, but setInput has not been called to provide any input");
-	}
-	if (requiredInputData.length != inputData.length){
-	    throw new MobyException("Wrong number of input parameters to service invocation method: expected " +
-				    requiredInputData.length + ", but setInput was provided " + inputData.length);
-				    }*/
-
-	// BUG WORKAROUND: Since the Moby-generated WSDL doesn't define the input, we assume the
-	// data types are right, and we just have to check the number of them.
-
-    }
-
-    /**
      * This method retrieves from Moby Central a copy of the WSDL document for the service
      * (or uses an internally cached copy from a previous invocation), and sets the variables for the 
      * SOAP call appropriately so you can consequently call performSOAPRequest.
@@ -469,6 +518,11 @@
 
     }
 
+    public Element decodeSOAPMessage(Element n, StringBuffer contentsXMLOutput, String inputXML)
+	throws SOAPException, MobyException{
+	return decodeSOAPMessage(n, contentsXMLOutput, inputXML, false);
+    }
+
     /**
      * Isolates the MOBY Data from the SOAP message returned by the remote service host.
      *
@@ -477,22 +531,29 @@
      *
      * @return The root element of the MOBY response DOM
      */
-    public Element decodeSOAPMessage(Element n, StringBuffer contentsXMLOutput, String inputXML) throws SOAPException, MobyException{
+    public Element decodeSOAPMessage(Element n, StringBuffer contentsXMLOutput, String inputXML, boolean async) 
+	throws SOAPException, MobyException{
 	if(n == null){
 	    throw new SOAPException("SOAP Message given to decode is null");
 	}
 
 	NodeList node_list = null;
-	Node responseNode = null;
 	XPath responseElementXPath = null;
 	try{
-	    responseElementXPath = new XPath("//"+ MobyPrefixResolver.MOBY_TRANSPORT_PREFIX+
-					     ":"+mobyService.getName()+"Response | //" +
-					     mobyService.getName()+"Response | " +
-					     "//"+ MobyPrefixResolver.MOBY_TRANSPORT_PREFIX+
-					     ":"+mobyService.getName() + " | //" +
-					     mobyService.getName(), 
-					     null, mobyPrefixResolver, XPath.SELECT);
+	    if(async){
+		responseElementXPath = new XPath("//"+MobyPrefixResolver.WSRP_PREFIX +
+						 ":"+AsyncClient.WSRP_MULTI_PROPERTY_TAG_NAME+"Response",
+						 null, mobyPrefixResolver, XPath.SELECT);
+	    }
+	    else{
+		responseElementXPath = new XPath("//"+ MobyPrefixResolver.MOBY_TRANSPORT_PREFIX+
+						 ":"+mobyService.getName()+"Response | //" +
+						 mobyService.getName()+"Response | " +
+						 "//"+ MobyPrefixResolver.MOBY_TRANSPORT_PREFIX+
+						 ":"+mobyService.getName() + " | //" +
+						 mobyService.getName(), 
+						 null, mobyPrefixResolver, XPath.SELECT);
+	    }
      	}catch(TransformerException te){
             throw new SOAPException("Cannot select SOAP nodes due to exception "+
 				    "while compiling XPath statement (code bug?):" +te);
@@ -513,8 +574,29 @@
 	    throw new SOAPException("Found more than one response element in SOAP payload, " +
 				    "unable to resolve ambiguity of the payload (service provider error?)");
 	}
-	responseNode = node_list.item(0);
 
+	Node[] responseNodes = null;
+	if(async){
+	    Vector<Node> nodes = new Vector<Node>();
+	    NodeList resultNodeList = node_list.item(0).getChildNodes();
+	    for(int i = 0; resultNodeList != null && i < resultNodeList.getLength(); i++){
+		if(!(resultNodeList.item(i) instanceof Element)){
+		    continue;
+		}
+		Element resultElement = (Element) resultNodeList.item(i);
+		if(resultElement.getLocalName().startsWith(AsyncClient.MOBY_RESULT_PROPERTY_PREFIX)){
+		    nodes.add(resultElement);
+		}
+	    }
+	    responseNodes = nodes.toArray(new Node[nodes.size()]);
+	}
+	else{
+	    responseNodes = new Node[]{node_list.item(0)};
+	}
+
+	Element domRoot = null;  // Where the result will be put
+
+	for(Node responseNode: responseNodes){
 	// Find base64 encoded elements in the SOAP message using XPath and 
 	// replace them with the real decoded contents
 	node_list = null;
@@ -597,7 +679,8 @@
 	    }
 	}
 
-	String localResponseString = "";
+	Element predefinedDOM = null;  // Choice of ripping DOM Element for moby payload out of SOAP DOM
+	String localResponseString = "";  // or storing raw XML strings, to be converted to a DOM later
 	for(int j = 0; j < children.getLength(); j++){
 	    Node child = children.item(j);
 	    if(child instanceof CDATASection || child instanceof Text){
@@ -612,16 +695,27 @@
 		}
 		localResponseString += child.getNodeValue();//.replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("(&amp;|&#x46;)", "&");
 	    }
+	    if(child instanceof Element && child.getLocalName().equals(MobyTags.MOBY)){
+		debugPS.println("Warning: The MOBY contents was found as raw XML inside the SOAP response!\n" +
+				"This is illegal according to the MOBY-API, please inform the service\n " +
+				" provider, as parsing such text may not be supported in the future");
+		localResponseString = null;
+		// Store the moby payload root element's DOM represntation, so we don't
+		// have to serialize it to the localResponseString and then parse it out 
+		// again (that would be wasteful).
+		predefinedDOM = (Element) child;
+		break;
+	    }
 	}
 
-	if(localResponseString.length() == 0){
+	if(localResponseString != null && localResponseString.length() == 0){
 	    throw new MobyException("The MOBY payload has no text contents at all"); 
 	}
 
 	// Check if the payload is an XML document.  If not, try a last ditch effort 
 	// by base64 decoding the contents.  This is technically not allowable in the 
 	// MOBY spec, but we are being lenient.
-	if(!localResponseString.startsWith("<?xml")){
+	if(localResponseString != null && !localResponseString.startsWith("<?xml")){
 	    // Is the XML declaration missing?
 	    if(localResponseString.startsWith("<moby:MOBY") || localResponseString.startsWith("<MOBY")){
 		localResponseString = "<?xml version=\"1.0\"?>\n"+localResponseString;
@@ -652,15 +746,58 @@
 	// later on because once the document is parsed, there is no way to get the 
 	// spaces back!  I set the attribute explicitly at the top level of each data
 	// element to compensate.  Unless of course this was already done.
-	if(localResponseString.indexOf("xml:space=\"preserve\"") == -1){
+	if(localResponseString != null && localResponseString.indexOf("xml:space=\"preserve\"") == -1){
 	    localResponseString = localResponseString.replaceAll("<String", "<String xml:space=\"preserve\"");
 	}
 
-	Document domDoc = null;
 	try{
-	    // Synchronized to avoid Xerces exception FWK005: concurrent parsing is disallowed
-	    synchronized(docBuilder){
-		domDoc = docBuilder.parse(new ByteArrayInputStream(localResponseString.getBytes()));
+	    if(async && domRoot != null){
+		// We will actually be appending several full MOBY messages together, so 
+		// we need to do a DOM-meld at the mobyData tag level.
+		Element newContentsTag = null;
+		if(predefinedDOM != null){  // we have the MOBY element as a DOM Element already from the SOAP DOM
+		    newContentsTag = MobyPrefixResolver.getChildElement(predefinedDOM, MobyTags.MOBYCONTENT);
+		}
+		else{
+		    synchronized(docBuilder){
+			Document newDoc = docBuilder.parse(new ByteArrayInputStream(localResponseString.getBytes()));
+			newContentsTag = MobyPrefixResolver.getChildElement(newDoc.getDocumentElement(), MobyTags.MOBYCONTENT);
+		    }
+		}
+		// Find the mobyContents tag under the root MOBY tag...
+		Element existingContentsTag = MobyPrefixResolver.getChildElement(domRoot, MobyTags.MOBYCONTENT);
+		NodeList newJobTags = newContentsTag.getChildNodes();
+		for(int i = 0; newJobTags != null && i < newJobTags.getLength(); i++){
+		    // Service notes blocks must be merged
+		    if(newJobTags.item(i) instanceof Element &&
+		       newJobTags.item(i).getLocalName().equals(MobyTags.SERVICENOTES)){
+			Element existingServiceNotes = 
+			    MobyPrefixResolver.getChildElement(existingContentsTag, MobyTags.SERVICENOTES);
+			if(existingServiceNotes == null){
+			    existingContentsTag.appendChild(newJobTags.item(i));
+			}
+			else{
+			    NodeList newServiceData = newJobTags.item(i).getChildNodes();
+			    for(int j = 0; newServiceData != null && j < newServiceData.getLength(); j++){
+				existingServiceNotes.appendChild(newServiceData.item(j));
+			    }
+			}
+		    }
+		    else{  //everything else is at the same level (i.e. mobyData blocks)
+			existingContentsTag.appendChild(newJobTags.item(i));
+		    }
+		}
+	    }
+	    else{  //synchronous service call, or first async call (which needs to create a doc anyway)
+		// Synchronized to avoid Xerces exception FWK005: concurrent parsing is disallowed
+		if(predefinedDOM != null){  // we have the MOBY element as a DOM Element already from the SOAP DOM
+		    domRoot = predefinedDOM;
+		}
+		else{
+		    synchronized(docBuilder){
+			domRoot = docBuilder.parse(new ByteArrayInputStream(localResponseString.getBytes())).getDocumentElement();
+		    }
+		}      
 	    }
 	} catch(org.xml.sax.SAXException saxe){
 	    throw new MobyException("The SOAP payload defining the MOBY Result " +
@@ -678,11 +815,12 @@
 	else{
 	    responseString = localResponseString;
 	}
+	} // end for responseNode in responseNodes
 
 	// release resources related to the Xpath execution, since we won't be using this doc anymore
 	releaseXPath(n);
 	    
-	return domDoc.getDocumentElement();
+	return domRoot;
     }
 
     public String convertMOBYDataToMOBYRequest(MobyDataInstance data) throws MobyException{
@@ -690,6 +828,8 @@
     }
 
     /**
+     * Creates an XML representation of the data, renamed to fit the needs of the service if necessary.
+     *
      * @param data the array of input parameters to put in a MOBY XML request
      *
      * @return the XML representation of the input data
@@ -718,7 +858,7 @@
 		Object param = query.get(name);
 		if(param == null){
 		    throw new MobyException("Query " + queryName + 
-					    "contained a null input parameter (" + name + ")");
+					    " contained a null input parameter (" + name + ")");
 		}
 		else if(param instanceof MobyPrimaryData){
 		    primaryParams.put(name, param);
@@ -767,7 +907,7 @@
 
 	ByteArrayOutputStream mobyRequest = new ByteArrayOutputStream();
 	try{
-	    MobyDataUtils.toXMLDocument(mobyRequest, inputData);
+	    MobyDataUtils.toXMLDocument(mobyRequest, data);
 	}
 	catch(MobyException me){
 	    throw me;




More information about the MOBY-guts mailing list