[MOBY-guts] biomoby commit
Andreas Groscurth
groscurt at dev.open-bio.org
Wed Nov 26 08:53:43 UTC 2008
groscurt
Wed Nov 26 03:53:43 EST 2008
Update of /home/repository/moby/moby-live/Java/src/main/org/biomoby/client
In directory dev.open-bio.org:/tmp/cvs-serv17488/src/main/org/biomoby/client
Modified Files:
MobyRequest.java CentralImpl.java BaseClient.java
Log Message:
changes done enabling the authentication for services
moby-live/Java/src/main/org/biomoby/client MobyRequest.java,1.40,1.41 CentralImpl.java,1.57,1.58 BaseClient.java,1.12,1.13
===================================================================
RCS file: /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/MobyRequest.java,v
retrieving revision 1.40
retrieving revision 1.41
diff -u -r1.40 -r1.41
--- /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/MobyRequest.java 2008/11/14 20:32:02 1.40
+++ /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/MobyRequest.java 2008/11/26 08:53:43 1.41
@@ -1,1019 +1,1028 @@
-package org.biomoby.client;
-
-import java.io.*;
-import java.util.*;
-
-import javax.xml.namespace.QName;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.rpc.Service;
-import javax.xml.transform.TransformerException;
-
-import org.apache.axis.client.Call;
-import org.apache.axis.message.MessageElement;
-import org.apache.xml.utils.PrefixResolver;
-import org.apache.xml.utils.PrefixResolverDefault;
-import org.apache.xpath.XPath;
-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.*;
-
-/**
- * This class handles the WSDL transaction to request a response
- * from a remote SOAP Web service that handles the
- * <a href="http://www.biomoby.org">MOBY</a> format. It depends on
- * having already retrieved the definition of the Web service via
- * the MOBY central registry using the
- * <a href="http://www.biomoby.org/moby-live/Java/docs/index.html">jMOBY</a> API,
- * and for now it uses the
- * <a href="http://ws.apache.org/axis/index.html">Apache
- * Axis</a> Web services framework, as well as Apache Xalan. There are code comments for the
- * few lines that rely on Axis classes rather than the JAX-RPC interfaces.
- *
- * @author Paul Gordon gordonp at ucalgary.ca
- */
-public class MobyRequest{
-
- protected MobyService mobyService = null;
- protected MobyContentInstance inputData = null;
- protected MobyContentInstance outputData = null;
- protected Central mobyCentral = null;
- protected PrefixResolver mobyPrefixResolver = null;
-
- protected Hashtable wsdlCache = null;
- protected String lastWsdlCacheKey = null;
- protected DocumentBuilder docBuilder = null;
- protected Service service = null;
-
- protected Class stringType;
- protected static boolean debug = false;
- protected PrintStream debugPS = System.err;
- protected XPathContext xpath_context;
- protected String responseString = null;
-
- private XPath stringEncodedXPath;
- private XPath base64EncodedXPath;
- private XPath simpleChildXPath;
- private XPath collectionChildXPath;
-
- private int autoID = 0;
-
- // Used as invocation callback if MobyRequest is acting as a server,
- // or is executing services as a client asynchronously
- private Vector<MobyRequestEventHandler> eventHandlers;
-
- /**
- * Default constructor. You should have a Central instance around since you're going to
- * be retrieving MobyServices to pass into here. Lets reuse it.
- *
- * @param central An instance of a Moby central object so we can make requests about object types, etc.
- * @throws ParserConfigurationException if JAXP doesn't have any valid DOM-building XML parsers set up for use
- */
- public MobyRequest(Central central) throws ParserConfigurationException{
- mobyCentral = central;
- wsdlCache = new Hashtable();
-
- eventHandlers = new Vector<MobyRequestEventHandler>();
-
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- dbf.setNamespaceAware(true);
- docBuilder = dbf.newDocumentBuilder();
-
- try{
- stringType = Class.forName("java.lang.String");
- }catch(ClassNotFoundException classe){
- debugPS.println("WARNING: Something is very wrong, could not find Class definition of String: " + classe);
- }
-
- xpath_context = new XPathContext();
- mobyPrefixResolver = new MobyPrefixResolver();
-
- // Now compile the XPath statements that will be used fetch data from the server response
- try{
- base64EncodedXPath = new XPath("//*[starts-with(substring-after(@"+
- MobyPrefixResolver.XSI1999_PREFIX+
- ":type, ':'), \"base64\") or starts-with(substring-after(@"+
- MobyPrefixResolver.XSI2001_PREFIX+
- ":type, ':'), \"base64\")]", null,
- mobyPrefixResolver, XPath.SELECT);
- stringEncodedXPath = new XPath("//*[substring-after(@"+
- MobyPrefixResolver.XSI1999_PREFIX+
- ":type, ':')=\"string\" or substring-after(@"+
- MobyPrefixResolver.XSI2001_PREFIX+
- ":type, ':')=\"string\"] | //"+
- MobyPrefixResolver.SOAP_ENC_PREFIX+":string", null,
- mobyPrefixResolver, XPath.SELECT);
- simpleChildXPath = new XPath("moby:Simple | Simple", null,
- mobyPrefixResolver, XPath.SELECT);
- collectionChildXPath = new XPath("moby:Collection | Collection", null,
- mobyPrefixResolver, XPath.SELECT);
- }
- catch(TransformerException te){
- debugPS.println("Syntax error encountered while compiling XPath " +
- "statements for internal use (code bug?): " + te);
- }
- setDebugMode(System.getProperty("moby.debug") != null);
- }
-
- /**
- * @param mode if true, debugging information is printed to the stream returned by getDebugOutputStream
- */
- public void setDebugMode(boolean mode){
- debug = mode;
- }
-
- /**
- * Standard error is used unless this method is called.
- *
- * @param ps the OutputStream to which debugging information is sent.
- * @throws IllegalArgumentException if the stream is null
- */
- public void setDebugPrintStream(PrintStream ps) throws IllegalArgumentException{
- if(ps == null){
- throw new IllegalArgumentException("The OutputStream specified to MobyRequest was null");
- }
- debugPS = ps;
- }
-
- /**
- * @return the instance of the class implementing Central that we are using
- */
- public Central getCentralImpl(){
- return mobyCentral;
- }
-
- /**
- * @param mobyservice the MobyService that should be executed when invokeService is called
- */
- public void setService(MobyService mobyservice){
- if(mobyservice == null){
- mobyService = null;
- }
- else if(mobyService == null || !mobyservice.equals(mobyService)){
- mobyService = mobyservice;
- }
- }
-
- /**
- * @return the MobyService that will be executed when invokeService is called
- */
- public MobyService getService(){
- return mobyService;
- }
-
- /**
- * @return the Raw MOBY XML response as a string
- */
- public String getResponseXML(){
- return responseString;
- }
-
- /**
- * Sets the input data for the MOBY service request. The length of the input array, and the
- * number of input parameters required by the service must be equal when invokeService() is called.
- * This method strictly enforces that the input be of the appropriate type for the service.
- *
- * Note that there is no requirement to use MobyDataInstance.setXmlMode() before passing in
- * data, this class will temporarily set the XML mode of the data when it is required.
- *
- * @throws IllegalArgumentException if the input does not fit the criteria of the service (e.g. wrong data type)
- */
- public void setInput(MobyContentInstance data) throws MobyException{
- inputData = data;
- }
-
- /**
- * Takes the data in the array, with their current articleNames, as input for the service
- */
- public void setInput(MobyDataInstance[] data) throws MobyException{
- MobyDataJob job = new MobyDataJob();
- for(MobyDataInstance param: data){
- job.put(param.getName(), param);
- }
- inputData = new MobyContentInstance();
- inputData.put(job);
- }
-
- /**
- * Convenience method to run services that take one argument. If the service
- * requires the input to have a name, it will be automatically assigned.
- */
- public void setInput(MobyDataInstance datum) throws MobyException{
- setInput(datum, "");
- }
-
- /**
- * Convenience method to run services that take one named argument.
- */
- public void setInput(MobyDataInstance datum, String paramName) throws MobyException{
- inputData = new MobyContentInstance(datum, paramName);
- }
-
- /**
- * @return the MobyService that will be executed when invokeService is called
- */
- public MobyContentInstance getInput(){
- return inputData;
- }
-
- /**
- * Same functionality as setSecondaryInput(MobyDataSecondaryInstance[])
- */
- public void setSecondaryInput(Collection<MobyDataSecondaryInstance> secondaryData) throws MobyException{
- setSecondaryInput(secondaryData.toArray(new MobyDataSecondaryInstance[secondaryData.size()]));
- }
-
- /**
- * This method will assign the provided secondary parameters to all primary input data currently
- * in this object. This is covenient if you are running 100 seqs through BLAST and only want to set
- * the parameters once. If you instead want to set secondary input differently for all primary inputs, you'll
- * need to create a custom MobyContentInstance as input to setInput().
- *
- * @throws MobyException if a parameter name is blank, or overrides a primary parameter
- */
- public void setSecondaryInput(MobyDataSecondaryInstance[] secondaryData) throws MobyException{
-
- Iterator queryNames = inputData.keySet().iterator();
- // For each query
- while(queryNames.hasNext()){
- MobyDataJob queryParams = inputData.get(queryNames.next());
- // Set all the secondary params (overwrites any old ones)
- for(int i = 0; i < secondaryData.length; i++){
- String secName = secondaryData[i].getName();
- if(secName == null || secName.length() == 0){
- throw new MobyException("A secondary parameter cannot have a blank name (array index " + i + ")");
- }
- if(queryParams.containsKey(secName) && queryParams.get(secName) instanceof MobyPrimaryData){
- throw new MobyException("A secondary parameter cannot override an existing primary parameter " +
- "with the same name (" + secName + ")");
- }
- queryParams.put(secName, secondaryData[i]);
- }
- }
- }
-
- /**
- * @return a vector of MobyDataInstance[], each element of the vector is the collection of response objects for the correspondingly indexed input request.
- *
- * @throws MobyException if you try to get the results before calling InvokeService
- */
- public MobyContentInstance getOutput() throws MobyException{
- if(outputData == null){
- throw new MobyException("Trying to access MOBY service results " +
- "before the service is invoked");
- }
- else{
- return outputData;
- }
- }
-
- /**
- * The main method of the class. If all of the MOBY input objects
- * are properly defined according to the Web service definition,
- * a SOAP request will be sent to the remote server, and the method
- * will return one or more MOBY objects (synchronous).
- * Call this method after calling setService, and setInput. If you do not call
- * setSecondaryInput, the default secondary parameter values will be used.
- *
- * @return the results of the remote Web service in response to the give input
- *
- * @throws MobyException i.e. there was something wrong with the input, output or remote service's logic
- * @throws SOAPException i.e. there was a problem with the underlying transaction/transport layer
- */
- public MobyContentInstance invokeService() throws Exception, MobyException, SOAPException, NoSuccessException{
- return mobyService.isAsynchronous() ? invokeService(inputData, new StringBuffer()) : invokeService(inputData, (StringBuffer) null);
- }
-
- // 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(MobyContentInstance inData, StringBuffer contentsXML)
- throws Exception, MobyException, SOAPException, NoSuccessException{
- return invokeService(inData, contentsXML, null, 0);
- }
-
- private MobyContentInstance invokeService(MobyContentInstance inData, 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)");
- }
-
- Element mobyDOM = null;
- if(mobyService.isAsynchronous()){
- // Async is "simpler", because it had to merge DOMs together into a single MobyContentInstance anyway
- MobyContentInstance mci = performAsyncSOAPRequest(mobyService, inData, handler, requestId);
- StringWriter writer = new StringWriter();
- MobyDataUtils.toXMLDocument(writer, mci);
- contentsXML.append(writer.toString());
- return mci;
- }
- else{
- String mobyXML = convertMOBYDataToMOBYRequest(inData);
- 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, mobyService.getServiceType().getRegistry());
- }
- }
-
- 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<String>(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());
- }
- }
-
- if(newDataAvailable.size() > 0){
- // 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, mservice.getServiceType().getRegistry());
- // 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(handler != null){
- MobyRequestEvent mre = new MobyRequestEvent(finalContents, this, mservice, null, requestId);
- StringWriter xmlWriter = new StringWriter();
- MobyDataUtils.toXMLDocument(xmlWriter, finalContents);
-
- mre.setContentsXML(xmlWriter.toString());
- if(!queryIDs.isEmpty()){
- // Send an update event only if we aren't finished yet.
- // If we are finished, the client is going to get this event as the
- // invocation thread finishes up (no need to double up).
- 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);
- }
-
- /**
- * Asynchronous call to invokeService. A callback to the passed-in handler will be made when
- * the response is ready, or there is an exception.
- *
- * @return the id that the callback event will return from getID(), allowing a client to distinguish between multiple concurrent invocation callbacks
- */
- public synchronized int invokeService(MobyRequestEventHandler handler){
- int id = autoID++;
-
- Thread t = new InvocationThread(this, inputData, handler, id); // see internal class definition below
- t.start();
-
- return id;
- }
-
- // This is the class that asynchronously calls the service and does a callback to
- // the handler specified in the invocation.
- class InvocationThread extends Thread {
- MobyContentInstance data;
- MobyService mservice;
- MobyRequest mobyRequest;
- MobyRequestEventHandler handler;
- int requestId;
-
- InvocationThread(MobyRequest mr, MobyContentInstance inData, MobyRequestEventHandler h, int id){
- data = inData;
- mobyRequest = mr;
- mservice = mobyRequest.getService();
- handler = h;
- requestId = id;
-
- // Name the thread after the service being run, mostly for ease of debugging
- setName(mservice.getName()+requestId);
- }
-
- public void run() {
- MobyRequestEvent requestEvent = new MobyRequestEvent(data, mobyRequest, mservice, null, requestId);
- // Tell the handler we're starting the request, with the given data
- handler.start(requestEvent);
-
- MobyRequestEvent responseEvent = null;
- MobyContentInstance content = null;
- StringBuffer contentsXML = new StringBuffer(); //to be filled in by the RPC call below
- try{
- content = mobyRequest.invokeService(data, contentsXML, handler, requestId); //RPC call...
- }
- catch(Exception e){
- responseEvent = new MobyRequestEvent(content, mobyRequest, mservice, e, requestId);
- }
- catch(Error err){
- responseEvent = new MobyRequestEvent(content, mobyRequest, mservice, err, requestId);
- }
- if(responseEvent == null){
- responseEvent = new MobyRequestEvent(content, mobyRequest, mservice, null, requestId);
- }
- // We've got the raw XML laying around, so why not provide it unmolested to the callback?
- responseEvent.setContentsXML(contentsXML.toString());
- handler.processEvent(responseEvent);
- handler.stop(mobyRequest, requestId);
- }
- }
-
- public void addEventHandler(MobyRequestEventHandler h){
- eventHandlers.add(h);
- }
-
- public void removeEventHandler(MobyRequestEventHandler h){
- eventHandlers.remove(h);
- }
-
- public void sendResponse(MobyRequestEvent mre){
- // Not yet implemented, need to conform to some web.xml specification here...
- }
-
- /**
- * 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.
- */
- protected Call getServiceFromWSDL() throws MobyException, NoSuccessException{
-// String wsdl = null;
-
-// // Since this is how we retrieve a service from Central, use the same values as the key to the hash
-// String wsdlCacheKey = mobyService.getName() + "@" + mobyService.getAuthority();
-
-// // This is the same call as last time, so we don't need to change the setup
-// if(wsdlCacheKey.equals(lastWsdlCacheKey)){
-// return setCallFromWSDL((String) wsdlCache.get(wsdlCacheKey));
-// }
-// // We haven't encountered this service yet
-// else if(!wsdlCache.containsKey(wsdlCacheKey)){
-// wsdl = mobyCentral.getServiceWSDL(mobyService.getName(), mobyService.getAuthority());
-// wsdlCache.put(wsdlCacheKey, wsdl);
-// }
-// // We've dealt with this one before
-// else{
-// wsdl = (String) wsdlCache.get(wsdlCacheKey);
-// }
-
-// lastWsdlCacheKey = wsdlCacheKey; // Keep track of the last invocation
-
- // Get ready to do SOAP
- return setCallFromWSDL(null);
- }
-
- /**
- * Creates the SOAP Call that will be invoked later. This should be based on the WSDL document
- * and parameter information from the MobyService, but these are currently not up to snuff.
- */
- protected Call setCallFromWSDL(String wsdl) throws MobyException, SOAPException{
- if(service == null){
- service = new org.apache.axis.client.Service(); // AXIS SPECIFIC This acts as a factory for Calls
- }
-
- Call soapCall;
- try{
- soapCall = (Call) service.createCall();//create a fresh Call each time
- }catch(javax.xml.rpc.ServiceException se){
- throw new SOAPException("Could not instatiate call to SOAP Service: " + se);
- }
-
- // Should initialize endpoint, etc. This call is AXIS SPECIFIC, otherwise you'll
- // have to do the call's info setting manually.
- //((org.apache.axis.client.Call) soapCall).setSOAPService(soapService);
- soapCall.removeAllParameters();
- soapCall.setTargetEndpointAddress(mobyService.getURL());
- soapCall.setPortName(new QName("http://biomoby.org/",
- mobyService.getName() + "PortType"));
- //soapCall.setOperationName(new QName("http://biomoby.org/",
- // mobyService.getName()));
- soapCall.setSOAPActionURI("http://biomoby.org/#" + mobyService.getName());
- return soapCall;
- }
-
- /**
- * Calls the invoke() method of the JAX-RPC Call interface.
- */
- protected Element performSOAPRequest(Call soapCall, String mobyInputXML, StringBuffer contentsXMLOutput) throws SOAPException{
- // First, turn the input objects into a MOBY XML request
- String[] mobyXMLInputData = new String[1];
-
- //Setup
- mobyXMLInputData[0] = mobyInputXML;
-
- if(debug)
- debugPS.println("returnType just before invoke call is " + soapCall.getReturnType());
- Object returnedObject = null;
- try{
- returnedObject = soapCall.invoke(new QName("http://biomoby.org/",
- mobyService.getName()), mobyXMLInputData);
- }
- catch(Exception e){
- e.printStackTrace();
- //System.err.println("Input: "+mobyInputXML);
- throw new SOAPException("While invoking SOAP Call: " + e);
- }
-
- try{
- if(debug){
- debugPS.println("SOAP Response was:\n");
- debugPS.println(soapCall.getResponseMessage().getSOAPPart().getEnvelope());
- }
- Element resultDom = ((MessageElement) soapCall.getResponseMessage().getSOAPPart().getEnvelope()).getAsDOM();
- return decodeSOAPMessage(resultDom, contentsXMLOutput, mobyInputXML);
- } catch(Exception e){
- e.printStackTrace();
- throw new SOAPException("Could not get SOAP response as DOM Element: "+ e);
- }
-
- }
-
- 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.
- *
- * @throws SOAPException if the MOBY payload cannot be found in the SOAP message
- * @throws MobyException if the MOBY message is not well-formed XML
- *
- * @return The root element of the MOBY response DOM
- */
- 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;
- XPath responseElementXPath = null;
- try{
- 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);
- }
- try{
- node_list = runXPath(responseElementXPath, n);
- }catch(TransformerException te){
- throw new SOAPException("Cannot select SOAP nodes due to exception "+
- "while executing XPath statement:" +te);
- }
-
- if(node_list == null || node_list.getLength() == 0){
- throw new SOAPException("Could not find a response element in SOAP payload (service " +
- mobyService.getName() + ")");
- }
-
- if(node_list.getLength() > 1){
- throw new SOAPException("Found more than one response element in SOAP payload, " +
- "unable to resolve ambiguity of the payload (service provider error?)");
- }
-
- 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;
- try{
- node_list = runXPath(base64EncodedXPath, responseNode);
- }
- catch(TransformerException te){
- throw new SOAPException("Cannot select base64 encoded SOAP nodes due to exception "+
- "while executing XPath statement:" +te);
- }
- if(debug && node_list != null){
- debugPS.println("There were " + node_list.getLength() +
- " base64 encoded elements in the data");
- }
-
- // Do decoding for each base64 part found
- for(int i = 0; node_list != null && i < node_list.getLength(); i++){
- org.w3c.dom.Node change = node_list.item(i);
- /* Make sure the text data is all put into one contiguous piece for decoding*/
- change.normalize();
-
- byte[] decodedBytes = org.apache.axis.encoding.Base64.decode(change.getFirstChild().getNodeValue());
- String newText = new String(decodedBytes);
- if(debug){
- debugPS.println("New decoded text is" + newText);
- }
-
- // Swap out this node for the decoded data
- change.getParentNode().replaceChild(n.getOwnerDocument().createTextNode(new String(decodedBytes)),
- change);
- }
-
- // Now see if there are any strings that need decoding
- node_list = null;
- try{
- node_list = runXPath(stringEncodedXPath, responseNode);
- }
- catch(TransformerException te){
- throw new SOAPException("Cannot select string encoded SOAP nodes due to exception "+
- "while executing XPath statement:" +te);
- }
-
- // Do concatenation for each plain string part found
- for(int i = 0; node_list != null && i < node_list.getLength(); i++){
- org.w3c.dom.Node change = node_list.item(i);
- /* Make sure the text data is all put into one contiguous piece for decoding*/
- change.normalize();
- String plainString = "";
- int j = 0;
- for(NodeList children = change.getChildNodes();
- children != null && j < children.getLength();
- j++){
- Node child = children.item(j);
- if(child instanceof CDATASection || child instanceof Text){
- plainString += child.getNodeValue();
- if(debug){
- debugPS.println("Plain string is now " + plainString);
- }
- }
- }
-
- // Swap out this node for the decoded data
- change.getParentNode().replaceChild(n.getOwnerDocument().createCDATASection(plainString), change);
- }
- if(debug && node_list != null){
- debugPS.println("There were " + node_list.getLength() +
- " XML Schema string encoded elements in the data");
- }
-
- // Parse the MOBY XML document payload
- responseNode.normalize();
- NodeList children = responseNode.getChildNodes();
- if(children == null){
- throw new MobyException("The MOBY payload has no contents at all");
- }
- if(children.getLength() != 1){
- if(debug){
- debugPS.println("Warning: MOBY Payload appears to have more than " +
- "just text in it, skipping the non-text sections");
- }
- }
-
- 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){
- // Unescape XML special characters in the string, so we can later on
- // parse the payload as regular XML.
- // Ignore whitespace-only node
- if(child.getNodeValue().matches("^\\s+$")){
- continue;
- }
- if(debug){
- debugPS.println("Concatenating text in response " + child.getNodeValue());
- }
- localResponseString += child.getNodeValue();//.replaceAll("<", "<").replaceAll(">", ">").replaceAll("(&|F)", "&");
- }
- 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 != null){
- if(localResponseString.length() == 0){
- throw new MobyException("The MOBY payload has no text contents at all");
- }
- if(Character.isWhitespace(localResponseString.charAt(0))){
- localResponseString = localResponseString.trim();
- }
- }
-
- // 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 != null && !localResponseString.startsWith("<?xml")){
- // Is the XML declaration missing?
- if(localResponseString.startsWith("<moby:MOBY") || localResponseString.startsWith("<MOBY")){
- localResponseString = "<?xml version=\"1.0\"?>\n"+localResponseString;
- debugPS.println("Warning: The MOBY contents was missing an XML declaration, but it is " +
- "required by the MOBY API, and may stop working in the future without it. Please " +
- "contact the client's provider to correct this.");
- }
- else{
- String oldResponse = localResponseString;
- localResponseString = new String(org.apache.axis.encoding.Base64.decode(localResponseString));
- if(!localResponseString.startsWith("<?xml")){
- throw new MobyException("The SOAP payload defining the MOBY contents " +
- "does not start with the xml processing instruction, and is therefore not " +
- "an XML document, as specified in the MOBY API. " +
- "Please contact the service provider. Contents was: " +
- oldResponse);
- }
- debugPS.println("Warning: The MOBY contents was needlessly base64 encoded (the SOAP " +
- "envelope does this for you). It has been decoded, but this is not " +
- "part of the MOBY API, and may stop working in the future. Please " +
- "contact the service provider to correct this.");
- }
- }
-
- // A bit of a hack: most MOBY data represented in string form
- // should have its formatting preserved (e.g. BLAST report), yet
- // no-one uses the xml:space="preserve" attribute. This causes problems
- // 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 != null && localResponseString.indexOf("xml:space=\"preserve\"") == -1){
- localResponseString = localResponseString.replaceAll("<String", "<String xml:space=\"preserve\"");
- }
-
- try{
- 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 " +
- "could not be parsed: " + saxe);
- } catch(java.io.IOException ioe){
- throw new MobyException("The SOAP payload defining the MOBY Result " +
- " could not be read (from a String!)" + ioe);
- }
-
- // Now, either save the xml we got in the class instance member (for synchronous calls to MobyRequest)
- // or in the StringBuffer given as a parameter to this function (async calls)
- if(contentsXMLOutput != null){
- contentsXMLOutput.append(localResponseString);
- }
- 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 domRoot;
- }
-
- public String convertMOBYDataToMOBYRequest(MobyDataInstance data) throws MobyException{
- return convertMOBYDataToMOBYRequest(new MobyContentInstance(data, ""));
- }
-
- /**
- * Creates an XML representation of the data, renamed to fit the needs of the service if necessary,
- * and adding any secondary parameter default values if not already specified in the incoming data.
- *
- * @param data the array of input parameters to put in a MOBY XML request
- *
- * @return the XML representation of the input data
- */
- public String convertMOBYDataToMOBYRequest(MobyContentInstance data) throws MobyException{
-
- MobyData[] inputs = mobyService.getPrimaryInputs();
- MobySecondaryData[] secondaries = mobyService.getSecondaryInputs();
-
- // Make sure the number of input args is correct for each query being submitted
- for(Map.Entry<String,MobyDataJob> entry: data.entrySet()){
- String queryName = entry.getKey();
- MobyDataJob query = entry.getValue();
-
- // Additionally, we check if they are MobyDataInstances below
- Map<String,MobyPrimaryData> primaryParams = new HashMap<String,MobyPrimaryData>();
- Map<String,MobySecondaryData> secondaryParams = new HashMap<String,MobySecondaryData>();
-
- // To store the primary input parameter name as given by the user,
- // in case we need it later on for parameter renaming...
- String primaryParamName = null;
-
- for(Map.Entry<String,MobyDataInstance> subentry: query.entrySet()){
- String name = subentry.getKey();
- MobyDataInstance param = subentry.getValue();
- if(param == null){
- throw new MobyException("Query " + queryName +
- " contained a null input parameter (" + name + ")");
- }
- else if(param instanceof MobyPrimaryData){
- primaryParams.put(name, (MobyPrimaryData) param);
- primaryParamName = name;
- }
- else if(param instanceof MobySecondaryData){
- secondaryParams.put(name, (MobySecondaryData) param);
- }
- else{
- System.err.println("Input parameter " + name + " (query " + queryName +
- ") was not a MobyPrimaryData or MobySecondaryData " +
- "as expected, but rather was of class " + param.getClass().getName());
- }
- }
-
- if(inputs != null && inputs.length != primaryParams.size()){
- throw new MobyException("Service " + mobyService.getName() + " was provided " +
- primaryParams.size() +
- " primary input parameter(s), but takes " + inputs.length +
- " (query " + queryName + ")");
- }
- if(secondaries != null){
- // If no secondaries provided, fill them in by default
- if(secondaries.length != 0){
- for(MobySecondaryData secondary: secondaries){
- if(!secondaryParams.containsKey(secondary.getName())){
- if(debug){
- System.err.println("Setting default secondary param value for missing param " + secondary);
- }
- query.put(secondary.getName(), new MobyDataSecondaryInstance(secondary));
- }
- }
- }
- if(secondaries.length != secondaryParams.size()){
- throw new MobyException("Service " + mobyService.getName() + " was provided " +
- secondaryParams.size() +
- " secondary input parameter(s), but takes " + secondaries.length +
- " (query " + queryName + "). Extra secondary" +
- " parameters must have been specified");
- }
- }
-
- // If there was one anonymous input, assign the name automatically in
- // the case the service requires it to be named. This is the only
- // unambiguous case in which we can do this.
- if(inputs.length == 1){
- String serviceParamName = inputs[0].getName(); // name as req'd by the service
-
- // name isn't the same as required currently
- if(serviceParamName != null && serviceParamName.length() > 0 &&
- !serviceParamName.equals(primaryParamName)){
- // take out the old parameter
- MobyPrimaryData theInputToRename = (MobyPrimaryData) query.remove(primaryParamName);
-
- // Add in the same parameter, but with the appropriate name
- query.put(serviceParamName, (MobyDataInstance) theInputToRename);
- }
- }
- }
-
- ByteArrayOutputStream mobyRequest = new ByteArrayOutputStream();
- try{
- MobyDataUtils.toXMLDocument(mobyRequest, data);
- }
- catch(MobyException me){
- throw me;
- }
- catch(Exception e){
- e.printStackTrace();
- throw new MobyException("Could not create MOBY payload XML from input data: " +e);
- }
-
- if(debug){
- debugPS.println("Input to MOBY Service is:");
- debugPS.print(mobyRequest.toString());
- }
-
- return mobyRequest.toString();
- }
-
- /**
- * A method that sets up the execution environment for and runs a compiled XPath statement against a DOM node
- * You should call releaseXPath when you're done with the results
- * @return the list of Nodes that satisfy the XPath in this Node's context
- */
- protected NodeList runXPath(XPath xpath, Node n) throws TransformerException{
- NodeList result = null;
- int dtm_node_handle = xpath_context.getDTMHandleFromNode(n);
- PrefixResolver node_prefix_resolver = new PrefixResolverDefault(n);
- XObject xobject = xpath.execute(xpath_context, n, node_prefix_resolver);
- if(xobject instanceof XNodeSet){
- result = ((XNodeSet) xobject).nodelist();
- }
- else if(debug && xobject != null){
- debugPS.println("Output of XPath was not a XNodeSet as expected, found " + xobject.getClass().getName());
- debugPS.flush();
- }
- return result;
- }
-
- protected void releaseXPath(Node n){
- xpath_context.release(xpath_context.getDTM(xpath_context.getDTMHandleFromNode(n)), false);
- }
-}
+
+package org.biomoby.client;
+
+import java.io.*;
+import java.util.*;
+
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.rpc.Service;
+import javax.xml.transform.TransformerException;
+
+import org.apache.axis.client.Call;
+import org.apache.axis.message.MessageElement;
+import org.apache.xml.utils.PrefixResolver;
+import org.apache.xml.utils.PrefixResolverDefault;
+import org.apache.xpath.XPath;
+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.*;
+
+/**
+ * This class handles the WSDL transaction to request a response from a remote SOAP Web service that handles the <a
+ * href="http://www.biomoby.org">MOBY</a> format. It depends on having already retrieved the definition of the Web
+ * service via the MOBY central registry using the <a href="http://www.biomoby.org/moby-live/Java/docs/index.html">jMOBY</a>
+ * API, and for now it uses the <a href="http://ws.apache.org/axis/index.html">Apache Axis</a> Web services framework,
+ * as well as Apache Xalan. There are code comments for the few lines that rely on Axis classes rather than the JAX-RPC
+ * interfaces.
+ *
+ * @author Paul Gordon gordonp at ucalgary.ca
+ */
+public class MobyRequest {
+
+ protected MobyService mobyService = null;
+ protected MobyContentInstance inputData = null;
+ protected MobyContentInstance outputData = null;
+ protected Central mobyCentral = null;
+ protected PrefixResolver mobyPrefixResolver = null;
+
+ protected Hashtable wsdlCache = null;
+ protected String lastWsdlCacheKey = null;
+ protected DocumentBuilder docBuilder = null;
+ protected Service service = null;
+
+ protected Class stringType;
+ protected static boolean debug = false;
+ protected PrintStream debugPS = System.err;
+ protected XPathContext xpath_context;
+ protected String responseString = null;
+
+ private XPath stringEncodedXPath;
+ private XPath base64EncodedXPath;
+ private XPath simpleChildXPath;
+ private XPath collectionChildXPath;
+
+ private int autoID = 0;
+
+ // Used as invocation callback if MobyRequest is acting as a server,
+ // or is executing services as a client asynchronously
+ private Vector< MobyRequestEventHandler > eventHandlers;
+
+ private String user;
+ private String password;
+
+ /**
+ * Default constructor. You should have a Central instance around since you're going to be retrieving MobyServices
+ * to pass into here. Lets reuse it.
+ *
+ * @param central An instance of a Moby central object so we can make requests about object types, etc.
+ * @throws ParserConfigurationException if JAXP doesn't have any valid DOM-building XML parsers set up for use
+ */
+ public MobyRequest( Central central ) throws ParserConfigurationException {
+ mobyCentral = central;
+ wsdlCache = new Hashtable();
+
+ eventHandlers = new Vector< MobyRequestEventHandler >();
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware( true );
+ docBuilder = dbf.newDocumentBuilder();
+
+ try {
+ stringType = Class.forName( "java.lang.String" );
+ }
+ catch ( ClassNotFoundException classe ) {
+ debugPS.println( "WARNING: Something is very wrong, could not find Class definition of String: " + classe );
+ }
+
+ xpath_context = new XPathContext();
+ mobyPrefixResolver = new MobyPrefixResolver();
+
+ // Now compile the XPath statements that will be used fetch data from the server response
+ try {
+ base64EncodedXPath = new XPath( "//*[starts-with(substring-after(@" + MobyPrefixResolver.XSI1999_PREFIX
+ + ":type, ':'), \"base64\") or starts-with(substring-after(@" + MobyPrefixResolver.XSI2001_PREFIX
+ + ":type, ':'), \"base64\")]", null, mobyPrefixResolver, XPath.SELECT );
+ stringEncodedXPath = new XPath( "//*[substring-after(@" + MobyPrefixResolver.XSI1999_PREFIX
+ + ":type, ':')=\"string\" or substring-after(@" + MobyPrefixResolver.XSI2001_PREFIX
+ + ":type, ':')=\"string\"] | //" + MobyPrefixResolver.SOAP_ENC_PREFIX + ":string", null,
+ mobyPrefixResolver, XPath.SELECT );
+ simpleChildXPath = new XPath( "moby:Simple | Simple", null, mobyPrefixResolver, XPath.SELECT );
+ collectionChildXPath = new XPath( "moby:Collection | Collection", null, mobyPrefixResolver, XPath.SELECT );
+ }
+ catch ( TransformerException te ) {
+ debugPS.println( "Syntax error encountered while compiling XPath "
+ + "statements for internal use (code bug?): " + te );
+ }
+ setDebugMode( System.getProperty( "moby.debug" ) != null );
+ }
+
+ /**
+ * @param mode if true, debugging information is printed to the stream returned by getDebugOutputStream
+ */
+ public void setDebugMode(boolean mode) {
+ debug = mode;
+ }
+
+ /**
+ * Standard error is used unless this method is called.
+ *
+ * @param ps the OutputStream to which debugging information is sent.
+ * @throws IllegalArgumentException if the stream is null
+ */
+ public void setDebugPrintStream(PrintStream ps) throws IllegalArgumentException {
+ if ( ps == null ) {
+ throw new IllegalArgumentException( "The OutputStream specified to MobyRequest was null" );
+ }
+ debugPS = ps;
+ }
+
+ /**
+ * @param user the user for a possible service authentication
+ * @param password the passoword for a possible service authentication
+ */
+ public void setAuthentication(String user, String password) {
+ this.user = user;
+ this.password = password;
+ }
+
+ /**
+ * @return the instance of the class implementing Central that we are using
+ */
+ public Central getCentralImpl() {
+ return mobyCentral;
+ }
+
+ /**
+ * @param mobyservice the MobyService that should be executed when invokeService is called
+ */
+ public void setService(MobyService mobyservice) {
+ if ( mobyservice == null ) {
+ mobyService = null;
+ }
+ else if ( mobyService == null || !mobyservice.equals( mobyService ) ) {
+ mobyService = mobyservice;
+ }
+ }
+
+ /**
+ * @return the MobyService that will be executed when invokeService is called
+ */
+ public MobyService getService() {
+ return mobyService;
+ }
+
+ /**
+ * @return the Raw MOBY XML response as a string
+ */
+ public String getResponseXML() {
+ return responseString;
+ }
+
+ /**
+ * Sets the input data for the MOBY service request. The length of the input array, and the number of input
+ * parameters required by the service must be equal when invokeService() is called. This method strictly enforces
+ * that the input be of the appropriate type for the service.
+ *
+ * Note that there is no requirement to use MobyDataInstance.setXmlMode() before passing in data, this class will
+ * temporarily set the XML mode of the data when it is required.
+ *
+ * @throws IllegalArgumentException if the input does not fit the criteria of the service (e.g. wrong data type)
+ */
+ public void setInput(MobyContentInstance data) throws MobyException {
+ inputData = data;
+ }
+
+ /**
+ * Takes the data in the array, with their current articleNames, as input for the service
+ */
+ public void setInput(MobyDataInstance[] data) throws MobyException {
+ MobyDataJob job = new MobyDataJob();
+ for ( MobyDataInstance param : data ) {
+ job.put( param.getName(), param );
+ }
+ inputData = new MobyContentInstance();
+ inputData.put( job );
+ }
+
+ /**
+ * Convenience method to run services that take one argument. If the service requires the input to have a name, it
+ * will be automatically assigned.
+ */
+ public void setInput(MobyDataInstance datum) throws MobyException {
+ setInput( datum, "" );
+ }
+
+ /**
+ * Convenience method to run services that take one named argument.
+ */
+ public void setInput(MobyDataInstance datum, String paramName) throws MobyException {
+ inputData = new MobyContentInstance( datum, paramName );
+ }
+
+ /**
+ * @return the MobyService that will be executed when invokeService is called
+ */
+ public MobyContentInstance getInput() {
+ return inputData;
+ }
+
+ /**
+ * Same functionality as setSecondaryInput(MobyDataSecondaryInstance[])
+ */
+ public void setSecondaryInput(Collection< MobyDataSecondaryInstance > secondaryData) throws MobyException {
+ setSecondaryInput( secondaryData.toArray( new MobyDataSecondaryInstance[ secondaryData.size() ] ) );
+ }
+
+ /**
+ * This method will assign the provided secondary parameters to all primary input data currently in this object.
+ * This is covenient if you are running 100 seqs through BLAST and only want to set the parameters once. If you
+ * instead want to set secondary input differently for all primary inputs, you'll need to create a custom
+ * MobyContentInstance as input to setInput().
+ *
+ * @throws MobyException if a parameter name is blank, or overrides a primary parameter
+ */
+ public void setSecondaryInput(MobyDataSecondaryInstance[] secondaryData) throws MobyException {
+
+ Iterator queryNames = inputData.keySet().iterator();
+ // For each query
+ while (queryNames.hasNext()) {
+ MobyDataJob queryParams = inputData.get( queryNames.next() );
+ // Set all the secondary params (overwrites any old ones)
+ for ( int i = 0; i < secondaryData.length; i++ ) {
+ String secName = secondaryData[ i ].getName();
+ if ( secName == null || secName.length() == 0 ) {
+ throw new MobyException( "A secondary parameter cannot have a blank name (array index " + i + ")" );
+ }
+ if ( queryParams.containsKey( secName ) && queryParams.get( secName ) instanceof MobyPrimaryData ) {
+ throw new MobyException( "A secondary parameter cannot override an existing primary parameter "
+ + "with the same name (" + secName + ")" );
+ }
+ queryParams.put( secName, secondaryData[ i ] );
+ }
+ }
+ }
+
+ /**
+ * @return a vector of MobyDataInstance[], each element of the vector is the collection of response objects for the
+ * correspondingly indexed input request.
+ *
+ * @throws MobyException if you try to get the results before calling InvokeService
+ */
+ public MobyContentInstance getOutput() throws MobyException {
+ if ( outputData == null ) {
+ throw new MobyException( "Trying to access MOBY service results " + "before the service is invoked" );
+ }
+ else {
+ return outputData;
+ }
+ }
+
+ /**
+ * The main method of the class. If all of the MOBY input objects are properly defined according to the Web service
+ * definition, a SOAP request will be sent to the remote server, and the method will return one or more MOBY objects
+ * (synchronous). Call this method after calling setService, and setInput. If you do not call setSecondaryInput, the
+ * default secondary parameter values will be used.
+ *
+ * @return the results of the remote Web service in response to the give input
+ *
+ * @throws MobyException i.e. there was something wrong with the input, output or remote service's logic
+ * @throws SOAPException i.e. there was a problem with the underlying transaction/transport layer
+ */
+ public MobyContentInstance invokeService() throws Exception, MobyException, SOAPException, NoSuccessException {
+ return mobyService.isAsynchronous()
+ ? invokeService( inputData, new StringBuffer() )
+ : invokeService( inputData, ( StringBuffer ) null );
+ }
+
+ // 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(MobyContentInstance inData, StringBuffer contentsXML) throws Exception,
+ MobyException, SOAPException, NoSuccessException {
+ return invokeService( inData, contentsXML, null, 0 );
+ }
+
+ private MobyContentInstance invokeService(MobyContentInstance inData, 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)" );
+ }
+
+ Element mobyDOM = null;
+ if ( mobyService.isAsynchronous() ) {
+ // Async is "simpler", because it had to merge DOMs together into a single MobyContentInstance anyway
+ MobyContentInstance mci = performAsyncSOAPRequest( mobyService, inData, handler, requestId );
+ StringWriter writer = new StringWriter();
+ MobyDataUtils.toXMLDocument( writer, mci );
+ contentsXML.append( writer.toString() );
+ return mci;
+ }
+ else {
+ String mobyXML = convertMOBYDataToMOBYRequest( inData );
+ Call call = getServiceFromWSDL();
+ if ( user != null && password != null ) {
+ call.setProperty( Call.USERNAME_PROPERTY, user );
+ call.setProperty( Call.PASSWORD_PROPERTY, password );
+ }
+ 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, mobyService.getServiceType().getRegistry() );
+ }
+ }
+
+ 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< String >( 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() );
+ }
+ }
+
+ if ( newDataAvailable.size() > 0 ) {
+ // 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,
+ mservice.getServiceType()
+ .getRegistry() );
+ // 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 ( handler != null ) {
+ MobyRequestEvent mre = new MobyRequestEvent( finalContents, this, mservice, null, requestId );
+ StringWriter xmlWriter = new StringWriter();
+ MobyDataUtils.toXMLDocument( xmlWriter, finalContents );
+
+ mre.setContentsXML( xmlWriter.toString() );
+ if ( !queryIDs.isEmpty() ) {
+ // Send an update event only if we aren't finished yet.
+ // If we are finished, the client is going to get this event as the
+ // invocation thread finishes up (no need to double up).
+ 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 );
+ }
+
+ /**
+ * Asynchronous call to invokeService. A callback to the passed-in handler will be made when the response is ready,
+ * or there is an exception.
+ *
+ * @return the id that the callback event will return from getID(), allowing a client to distinguish between
+ * multiple concurrent invocation callbacks
+ */
+ public synchronized int invokeService(MobyRequestEventHandler handler) {
+ int id = autoID++;
+
+ Thread t = new InvocationThread( this, inputData, handler, id ); // see internal class definition below
+ t.start();
+
+ return id;
+ }
+
+ // This is the class that asynchronously calls the service and does a callback to
+ // the handler specified in the invocation.
+ class InvocationThread extends Thread {
+ MobyContentInstance data;
+ MobyService mservice;
+ MobyRequest mobyRequest;
+ MobyRequestEventHandler handler;
+ int requestId;
+
+ InvocationThread( MobyRequest mr, MobyContentInstance inData, MobyRequestEventHandler h, int id ) {
+ data = inData;
+ mobyRequest = mr;
+ mservice = mobyRequest.getService();
+ handler = h;
+ requestId = id;
+
+ // Name the thread after the service being run, mostly for ease of debugging
+ setName( mservice.getName() + requestId );
+ }
+
+ public void run() {
+ MobyRequestEvent requestEvent = new MobyRequestEvent( data, mobyRequest, mservice, null, requestId );
+ // Tell the handler we're starting the request, with the given data
+ handler.start( requestEvent );
+
+ MobyRequestEvent responseEvent = null;
+ MobyContentInstance content = null;
+ StringBuffer contentsXML = new StringBuffer(); // to be filled in by the RPC call below
+ try {
+ content = mobyRequest.invokeService( data, contentsXML, handler, requestId ); // RPC call...
+ }
+ catch ( Exception e ) {
+ responseEvent = new MobyRequestEvent( content, mobyRequest, mservice, e, requestId );
+ }
+ catch ( Error err ) {
+ responseEvent = new MobyRequestEvent( content, mobyRequest, mservice, err, requestId );
+ }
+ if ( responseEvent == null ) {
+ responseEvent = new MobyRequestEvent( content, mobyRequest, mservice, null, requestId );
+ }
+ // We've got the raw XML laying around, so why not provide it unmolested to the callback?
+ responseEvent.setContentsXML( contentsXML.toString() );
+ handler.processEvent( responseEvent );
+ handler.stop( mobyRequest, requestId );
+ }
+ }
+
+ public void addEventHandler(MobyRequestEventHandler h) {
+ eventHandlers.add( h );
+ }
+
+ public void removeEventHandler(MobyRequestEventHandler h) {
+ eventHandlers.remove( h );
+ }
+
+ public void sendResponse(MobyRequestEvent mre) {
+ // Not yet implemented, need to conform to some web.xml specification here...
+ }
+
+ /**
+ * 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.
+ */
+ protected Call getServiceFromWSDL() throws MobyException, NoSuccessException {
+ // String wsdl = null;
+
+ // // Since this is how we retrieve a service from Central, use the same values as the key to the hash
+ // String wsdlCacheKey = mobyService.getName() + "@" + mobyService.getAuthority();
+
+ // // This is the same call as last time, so we don't need to change the setup
+ // if(wsdlCacheKey.equals(lastWsdlCacheKey)){
+ // return setCallFromWSDL((String) wsdlCache.get(wsdlCacheKey));
+ // }
+ // // We haven't encountered this service yet
+ // else if(!wsdlCache.containsKey(wsdlCacheKey)){
+ // wsdl = mobyCentral.getServiceWSDL(mobyService.getName(), mobyService.getAuthority());
+ // wsdlCache.put(wsdlCacheKey, wsdl);
+ // }
+ // // We've dealt with this one before
+ // else{
+ // wsdl = (String) wsdlCache.get(wsdlCacheKey);
+ // }
+
+ // lastWsdlCacheKey = wsdlCacheKey; // Keep track of the last invocation
+
+ // Get ready to do SOAP
+ return setCallFromWSDL( null );
+ }
+
+ /**
+ * Creates the SOAP Call that will be invoked later. This should be based on the WSDL document and parameter
+ * information from the MobyService, but these are currently not up to snuff.
+ */
+ protected Call setCallFromWSDL(String wsdl) throws MobyException, SOAPException {
+ if ( service == null ) {
+ service = new org.apache.axis.client.Service(); // AXIS SPECIFIC This acts as a factory for Calls
+ }
+
+ Call soapCall;
+ try {
+ soapCall = ( Call ) service.createCall();// create a fresh Call each time
+ }
+ catch ( javax.xml.rpc.ServiceException se ) {
+ throw new SOAPException( "Could not instatiate call to SOAP Service: " + se );
+ }
+
+ // Should initialize endpoint, etc. This call is AXIS SPECIFIC, otherwise you'll
+ // have to do the call's info setting manually.
+ // ((org.apache.axis.client.Call) soapCall).setSOAPService(soapService);
+ soapCall.removeAllParameters();
+ soapCall.setTargetEndpointAddress( mobyService.getURL() );
+ soapCall.setPortName( new QName( "http://biomoby.org/", mobyService.getName() + "PortType" ) );
+ // soapCall.setOperationName(new QName("http://biomoby.org/",
+ // mobyService.getName()));
+ soapCall.setSOAPActionURI( "http://biomoby.org/#" + mobyService.getName() );
+ return soapCall;
+ }
+
+ /**
+ * Calls the invoke() method of the JAX-RPC Call interface.
+ */
+ protected Element performSOAPRequest(Call soapCall, String mobyInputXML, StringBuffer contentsXMLOutput)
+ throws SOAPException {
+ // First, turn the input objects into a MOBY XML request
+ String[] mobyXMLInputData = new String[ 1 ];
+
+ // Setup
+ mobyXMLInputData[ 0 ] = mobyInputXML;
+
+ if ( debug )
+ debugPS.println( "returnType just before invoke call is " + soapCall.getReturnType() );
+ Object returnedObject = null;
+ try {
+ returnedObject = soapCall.invoke( new QName( "http://biomoby.org/", mobyService.getName() ),
+ mobyXMLInputData );
+ }
+ catch ( Exception e ) {
+ e.printStackTrace();
+ // System.err.println("Input: "+mobyInputXML);
+ throw new SOAPException( "While invoking SOAP Call: " + e );
+ }
+
+ try {
+ if ( debug ) {
+ debugPS.println( "SOAP Response was:\n" );
+ debugPS.println( soapCall.getResponseMessage().getSOAPPart().getEnvelope() );
+ }
+ Element resultDom = ( ( MessageElement ) soapCall.getResponseMessage().getSOAPPart().getEnvelope() ).getAsDOM();
+ return decodeSOAPMessage( resultDom, contentsXMLOutput, mobyInputXML );
+ }
+ catch ( Exception e ) {
+ e.printStackTrace();
+ throw new SOAPException( "Could not get SOAP response as DOM Element: " + e );
+ }
+
+ }
+
+ 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.
+ *
+ * @throws SOAPException if the MOBY payload cannot be found in the SOAP message
+ * @throws MobyException if the MOBY message is not well-formed XML
+ *
+ * @return The root element of the MOBY response DOM
+ */
+ 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;
+ XPath responseElementXPath = null;
+ try {
+ 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 );
+ }
+ try {
+ node_list = runXPath( responseElementXPath, n );
+ }
+ catch ( TransformerException te ) {
+ throw new SOAPException( "Cannot select SOAP nodes due to exception " + "while executing XPath statement:"
+ + te );
+ }
+
+ if ( node_list == null || node_list.getLength() == 0 ) {
+ throw new SOAPException( "Could not find a response element in SOAP payload (service "
+ + mobyService.getName() + ")" );
+ }
+
+ if ( node_list.getLength() > 1 ) {
+ throw new SOAPException( "Found more than one response element in SOAP payload, "
+ + "unable to resolve ambiguity of the payload (service provider error?)" );
+ }
+
+ 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;
+ try {
+ node_list = runXPath( base64EncodedXPath, responseNode );
+ }
+ catch ( TransformerException te ) {
+ throw new SOAPException( "Cannot select base64 encoded SOAP nodes due to exception "
+ + "while executing XPath statement:" + te );
+ }
+ if ( debug && node_list != null ) {
+ debugPS.println( "There were " + node_list.getLength() + " base64 encoded elements in the data" );
+ }
+
+ // Do decoding for each base64 part found
+ for ( int i = 0; node_list != null && i < node_list.getLength(); i++ ) {
+ org.w3c.dom.Node change = node_list.item( i );
+ /* Make sure the text data is all put into one contiguous piece for decoding */
+ change.normalize();
+
+ byte[] decodedBytes = org.apache.axis.encoding.Base64.decode( change.getFirstChild().getNodeValue() );
+ String newText = new String( decodedBytes );
+ if ( debug ) {
+ debugPS.println( "New decoded text is" + newText );
+ }
+
+ // Swap out this node for the decoded data
+ change.getParentNode().replaceChild( n.getOwnerDocument().createTextNode( new String( decodedBytes ) ),
+ change );
+ }
+
+ // Now see if there are any strings that need decoding
+ node_list = null;
+ try {
+ node_list = runXPath( stringEncodedXPath, responseNode );
+ }
+ catch ( TransformerException te ) {
+ throw new SOAPException( "Cannot select string encoded SOAP nodes due to exception "
+ + "while executing XPath statement:" + te );
+ }
+
+ // Do concatenation for each plain string part found
+ for ( int i = 0; node_list != null && i < node_list.getLength(); i++ ) {
+ org.w3c.dom.Node change = node_list.item( i );
+ /* Make sure the text data is all put into one contiguous piece for decoding */
+ change.normalize();
+ String plainString = "";
+ int j = 0;
+ for ( NodeList children = change.getChildNodes(); children != null && j < children.getLength(); j++ ) {
+ Node child = children.item( j );
+ if ( child instanceof CDATASection || child instanceof Text ) {
+ plainString += child.getNodeValue();
+ if ( debug ) {
+ debugPS.println( "Plain string is now " + plainString );
+ }
+ }
+ }
+
+ // Swap out this node for the decoded data
+ change.getParentNode().replaceChild( n.getOwnerDocument().createCDATASection( plainString ), change );
+ }
+ if ( debug && node_list != null ) {
+ debugPS.println( "There were " + node_list.getLength()
+ + " XML Schema string encoded elements in the data" );
+ }
+
+ // Parse the MOBY XML document payload
+ responseNode.normalize();
+ NodeList children = responseNode.getChildNodes();
+ if ( children == null ) {
+ throw new MobyException( "The MOBY payload has no contents at all" );
+ }
+ if ( children.getLength() != 1 ) {
+ if ( debug ) {
+ debugPS.println( "Warning: MOBY Payload appears to have more than "
+ + "just text in it, skipping the non-text sections" );
+ }
+ }
+
+ 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 ) {
+ // Unescape XML special characters in the string, so we can later on
+ // parse the payload as regular XML.
+ // Ignore whitespace-only node
+ if ( child.getNodeValue().matches( "^\\s+$" ) ) {
+ continue;
+ }
+ if ( debug ) {
+ debugPS.println( "Concatenating text in response " + child.getNodeValue() );
+ }
+ localResponseString += child.getNodeValue();// .replaceAll("<", "<").replaceAll(">",
+ // ">").replaceAll("(&|F)", "&");
+ }
+ 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 != null ) {
+ if ( localResponseString.length() == 0 ) {
+ throw new MobyException( "The MOBY payload has no text contents at all" );
+ }
+ if ( Character.isWhitespace( localResponseString.charAt( 0 ) ) ) {
+ localResponseString = localResponseString.trim();
+ }
+ }
+
+ // 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 != null && !localResponseString.startsWith( "<?xml" ) ) {
+ // Is the XML declaration missing?
+ if ( localResponseString.startsWith( "<moby:MOBY" ) || localResponseString.startsWith( "<MOBY" ) ) {
+ localResponseString = "<?xml version=\"1.0\"?>\n" + localResponseString;
+ debugPS.println( "Warning: The MOBY contents was missing an XML declaration, but it is "
+ + "required by the MOBY API, and may stop working in the future without it. Please "
+ + "contact the client's provider to correct this." );
+ }
+ else {
+ String oldResponse = localResponseString;
+ localResponseString = new String( org.apache.axis.encoding.Base64.decode( localResponseString ) );
+ if ( !localResponseString.startsWith( "<?xml" ) ) {
+ throw new MobyException( "The SOAP payload defining the MOBY contents "
+ + "does not start with the xml processing instruction, and is therefore not "
+ + "an XML document, as specified in the MOBY API. "
+ + "Please contact the service provider. Contents was: " + oldResponse );
+ }
+ debugPS.println( "Warning: The MOBY contents was needlessly base64 encoded (the SOAP "
+ + "envelope does this for you). It has been decoded, but this is not "
+ + "part of the MOBY API, and may stop working in the future. Please "
+ + "contact the service provider to correct this." );
+ }
+ }
+
+ // A bit of a hack: most MOBY data represented in string form
+ // should have its formatting preserved (e.g. BLAST report), yet
+ // no-one uses the xml:space="preserve" attribute. This causes problems
+ // 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 != null && localResponseString.indexOf( "xml:space=\"preserve\"" ) == -1 ) {
+ localResponseString = localResponseString.replaceAll( "<String", "<String xml:space=\"preserve\"" );
+ }
+
+ try {
+ 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 " + "could not be parsed: " + saxe );
+ }
+ catch ( java.io.IOException ioe ) {
+ throw new MobyException( "The SOAP payload defining the MOBY Result "
+ + " could not be read (from a String!)" + ioe );
+ }
+
+ // Now, either save the xml we got in the class instance member (for synchronous calls to MobyRequest)
+ // or in the StringBuffer given as a parameter to this function (async calls)
+ if ( contentsXMLOutput != null ) {
+ contentsXMLOutput.append( localResponseString );
+ }
+ 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 domRoot;
+ }
+
+ public String convertMOBYDataToMOBYRequest(MobyDataInstance data) throws MobyException {
+ return convertMOBYDataToMOBYRequest( new MobyContentInstance( data, "" ) );
+ }
+
+ /**
+ * Creates an XML representation of the data, renamed to fit the needs of the service if necessary, and adding any
+ * secondary parameter default values if not already specified in the incoming data.
+ *
+ * @param data the array of input parameters to put in a MOBY XML request
+ *
+ * @return the XML representation of the input data
+ */
+ public String convertMOBYDataToMOBYRequest(MobyContentInstance data) throws MobyException {
+
+ MobyData[] inputs = mobyService.getPrimaryInputs();
+ MobySecondaryData[] secondaries = mobyService.getSecondaryInputs();
+
+ // Make sure the number of input args is correct for each query being submitted
+ for ( Map.Entry< String, MobyDataJob > entry : data.entrySet() ) {
+ String queryName = entry.getKey();
+ MobyDataJob query = entry.getValue();
+
+ // Additionally, we check if they are MobyDataInstances below
+ Map< String, MobyPrimaryData > primaryParams = new HashMap< String, MobyPrimaryData >();
+ Map< String, MobySecondaryData > secondaryParams = new HashMap< String, MobySecondaryData >();
+
+ // To store the primary input parameter name as given by the user,
+ // in case we need it later on for parameter renaming...
+ String primaryParamName = null;
+
+ for ( Map.Entry< String, MobyDataInstance > subentry : query.entrySet() ) {
+ String name = subentry.getKey();
+ MobyDataInstance param = subentry.getValue();
+ if ( param == null ) {
+ throw new MobyException( "Query " + queryName + " contained a null input parameter (" + name + ")" );
+ }
+ else if ( param instanceof MobyPrimaryData ) {
+ primaryParams.put( name, ( MobyPrimaryData ) param );
+ primaryParamName = name;
+ }
+ else if ( param instanceof MobySecondaryData ) {
+ secondaryParams.put( name, ( MobySecondaryData ) param );
+ }
+ else {
+ System.err.println( "Input parameter " + name + " (query " + queryName
+ + ") was not a MobyPrimaryData or MobySecondaryData "
+ + "as expected, but rather was of class " + param.getClass().getName() );
+ }
+ }
+
+ if ( inputs != null && inputs.length != primaryParams.size() ) {
+ throw new MobyException( "Service " + mobyService.getName() + " was provided " + primaryParams.size()
+ + " primary input parameter(s), but takes " + inputs.length + " (query " + queryName + ")" );
+ }
+ if ( secondaries != null ) {
+ // If no secondaries provided, fill them in by default
+ if ( secondaries.length != 0 ) {
+ for ( MobySecondaryData secondary : secondaries ) {
+ if ( !secondaryParams.containsKey( secondary.getName() ) ) {
+ if ( debug ) {
+ System.err.println( "Setting default secondary param value for missing param "
+ + secondary );
+ }
+ query.put( secondary.getName(), new MobyDataSecondaryInstance( secondary ) );
+ }
+ }
+ }
+ if ( secondaries.length != secondaryParams.size() ) {
+ throw new MobyException( "Service " + mobyService.getName() + " was provided "
+ + secondaryParams.size() + " secondary input parameter(s), but takes " + secondaries.length
+ + " (query " + queryName + "). Extra secondary" + " parameters must have been specified" );
+ }
+ }
+
+ // If there was one anonymous input, assign the name automatically in
+ // the case the service requires it to be named. This is the only
+ // unambiguous case in which we can do this.
+ if ( inputs.length == 1 ) {
+ String serviceParamName = inputs[ 0 ].getName(); // name as req'd by the service
+
+ // name isn't the same as required currently
+ if ( serviceParamName != null && serviceParamName.length() > 0
+ && !serviceParamName.equals( primaryParamName ) ) {
+ // take out the old parameter
+ MobyPrimaryData theInputToRename = ( MobyPrimaryData ) query.remove( primaryParamName );
+
+ // Add in the same parameter, but with the appropriate name
+ query.put( serviceParamName, ( MobyDataInstance ) theInputToRename );
+ }
+ }
+ }
+
+ ByteArrayOutputStream mobyRequest = new ByteArrayOutputStream();
+ try {
+ MobyDataUtils.toXMLDocument( mobyRequest, data );
+ }
+ catch ( MobyException me ) {
+ throw me;
+ }
+ catch ( Exception e ) {
+ e.printStackTrace();
+ throw new MobyException( "Could not create MOBY payload XML from input data: " + e );
+ }
+
+ if ( debug ) {
+ debugPS.println( "Input to MOBY Service is:" );
+ debugPS.print( mobyRequest.toString() );
+ }
+
+ return mobyRequest.toString();
+ }
+
+ /**
+ * A method that sets up the execution environment for and runs a compiled XPath statement against a DOM node You
+ * should call releaseXPath when you're done with the results
+ *
+ * @return the list of Nodes that satisfy the XPath in this Node's context
+ */
+ protected NodeList runXPath(XPath xpath, Node n) throws TransformerException {
+ NodeList result = null;
+ int dtm_node_handle = xpath_context.getDTMHandleFromNode( n );
+ PrefixResolver node_prefix_resolver = new PrefixResolverDefault( n );
+ XObject xobject = xpath.execute( xpath_context, n, node_prefix_resolver );
+ if ( xobject instanceof XNodeSet ) {
+ result = ( ( XNodeSet ) xobject ).nodelist();
+ }
+ else if ( debug && xobject != null ) {
+ debugPS.println( "Output of XPath was not a XNodeSet as expected, found " + xobject.getClass().getName() );
+ debugPS.flush();
+ }
+ return result;
+ }
+
+ protected void releaseXPath(Node n) {
+ xpath_context.release( xpath_context.getDTM( xpath_context.getDTMHandleFromNode( n ) ), false );
+ }
+}
===================================================================
RCS file: /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/CentralImpl.java,v
retrieving revision 1.57
retrieving revision 1.58
diff -u -r1.57 -r1.58
--- /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/CentralImpl.java 2008/10/30 02:33:25 1.57
+++ /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/CentralImpl.java 2008/11/26 08:53:43 1.58
@@ -1,2139 +1,1957 @@
-// CentralImpl.java
-// A default client to the Moby Central service.
-//
-// senger at ebi.ac.uk
-// February 2003
-//
-
-package org.biomoby.client;
-
-import org.biomoby.registry.meta.Registry;
-import org.biomoby.shared.Central;
-import org.biomoby.shared.MobyData;
-import org.biomoby.shared.MobyDataType;
-import org.biomoby.shared.MobyException;
-import org.biomoby.shared.MobyNamespace;
-import org.biomoby.shared.MobyPrimaryDataSet;
-import org.biomoby.shared.MobyPrimaryDataSimple;
-import org.biomoby.shared.MobyRelationship;
-import org.biomoby.shared.MobySecondaryData;
-import org.biomoby.shared.MobyService;
-import org.biomoby.shared.MobyServiceType;
-import org.biomoby.shared.NoSuccessException;
-import org.biomoby.shared.PendingCurationException;
-import org.biomoby.shared.MobyResourceRef;
-import org.biomoby.shared.Utils;
-
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.namespace.QName;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import org.apache.axis.AxisFault;
-import org.apache.axis.client.Call;
-import org.apache.axis.client.Service;
-import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
-import org.apache.commons.httpclient.Header;
-import org.apache.commons.httpclient.HttpClient;
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.methods.HeadMethod;
-import org.apache.commons.httpclient.params.HttpMethodParams;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.LineNumberReader;
-import java.io.PrintStream;
-import java.io.InputStream;
-import java.lang.reflect.Constructor;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Vector;
-import java.util.TreeMap;
-import java.util.Comparator;
-import java.util.zip.GZIPInputStream;
-import java.util.logging.*;
-
-/**
- * A default implementation of the
- * interface {@link org.biomoby.shared.Central Central}
- * allowing access to a Moby registry.
- *<p>
- * This class is supposed to be used by all other clients that wish
- * to communicate with the Moby Registry, but do not want to know
- * about all XML details that are necessary for talking with the Moby Central
- * directly. This is an example of a client program:
- *<pre>
- * import org.biomoby.shared.Central;
- * import org.biomoby.shared.MobyException;
- * import org.biomoby.client.CentralImpl;
- * import java.util.Map;
- * import java.util.Iterator;
- *
- * public class Test {
- *
- * public static void main (String[] args)
- * throws MobyException {
- *
- * Central worker = new CentralImpl();
- * Map authorities = worker.getServiceNamesByAuthority();
- *
- * for (Iterator it = authorities.entrySet().iterator(); it.hasNext(); ) {
- * Map.Entry entry = (Map.Entry)it.next();
- * System.out.println (entry.getKey());
- * String[] names = (String[])entry.getValue();
- * for (int i = 0; i < names.length; i++)
- * System.out.println ("\t" + names[i]);
- * }
- * }
- * }
- *</pre>
- *
- * @author <A HREF="mailto:senger at ebi.ac.uk">Martin Senger</A>
- * @version $Id$
- */
-
-public class CentralImpl
- implements Central, SimpleCache {
-
- private URL endpoint;
- private String uri;
- protected boolean debug = false;
-
- /** Common central used to if getDefaultCentral() is called */
- protected static Map<String,CentralImpl> defaultCentrals = new HashMap<String,CentralImpl>();
-
- /** Default location (endpoint) of a Moby registry. */
- public static final String DEFAULT_ENDPOINT = "http://moby.ucalgary.ca/moby/MOBY-Central.pl";
-
- /** Default namespace used by the contacted Moby registry. */
- public static final String DEFAULT_NAMESPACE = "http://moby.ucalgary.ca/MOBY/Central";
-
- /**
- * The META-INF resource file that will be checked to determine what
- * default class should be instantiated in order to create a Central Implementation
- * when getDefaultCentral() is called.
- */
- public static final String CENTRAL_IMPL_RESOURCE_NAME = "org.biomoby.shared.CentralDefaultImpl";
- /** The class to use for getDefaultCentral if all else fails */
- public static final String DEFAULT_CENTRAL_IMPL_CLASSNAME = "org.biomoby.client.CentralDigestCachedImpl";
- private static Logger logger = Logger.getLogger("org.biomoby.client.CentralImpl");
-
- /**
- * Thread local that gives each thread its own
- * DocumentBuilderFactory (since it is not thread-safe). Code taken
- * from Apache's JaxpUtils.
- */
- public static ThreadLocal DOCUMENT_BUILDER_FACTORIES = new ThreadLocal() {
- protected synchronized Object initialValue() {
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- dbf.setNamespaceAware (true);
- return dbf;
- }
- };
-
-
- /*************************************************************************
- * Default constructor. It connects to a default Moby registry
- * (as defined in {@link #DEFAULT_ENDPOINT}) using a default namespace
- * (as defined int {@link #DEFAULT_NAMESPACE}).
- *************************************************************************/
- public CentralImpl()
- throws MobyException {
- this (DEFAULT_ENDPOINT, DEFAULT_NAMESPACE);
- }
-
- /*************************************************************************
- * Constructor allowing to specify which Moby Registry to use.
- *
- * @throws MobyException if 'endpoint' is not a valid URL, or if no
- * DOM parser is available
- *************************************************************************/
- public CentralImpl (String endpoint)
- throws MobyException {
- this (endpoint, DEFAULT_NAMESPACE);
- }
-
- /*************************************************************************
- * Constructor allowing to specify which Moby Registry and what
- * namespace to use. If any of the parameters is null, its default
- * value is used instead.
- *<p>
- * @throws MobyException if 'endpoint' is not a valid URL, or if no
- * DOM parser was found
- *************************************************************************/
- public CentralImpl (String endpoint, String namespace)
- throws MobyException {
-
- if (endpoint == null || "".equals (endpoint.trim()))
- endpoint = DEFAULT_ENDPOINT;
- if (namespace == null || "".equals (namespace.trim()))
- namespace = DEFAULT_NAMESPACE;
-
- try {
- this.endpoint = new URL (endpoint);
- } catch (MalformedURLException e) {
- throw new MobyException ("Bad URL: " + endpoint);
- }
- this.uri = namespace;
-
- cache = new Hashtable<String,Object>();
- useCache = true;
- }
-
- /*************************************************************************
- * Loads a DOM Document from an InputStream. Uses thread-safe
- * mechanism.
- *************************************************************************/
- public static Document loadDocument (InputStream input)
- throws MobyException {
- try {
- DocumentBuilderFactory dbf
- = (DocumentBuilderFactory)DOCUMENT_BUILDER_FACTORIES.get();
- DocumentBuilder db = dbf.newDocumentBuilder();
- return (db.parse (input));
- } catch (Exception e) {
- throw new MobyException ("Problem with reading XML input: " + e.toString(), e);
- }
- }
-
- /*************************************************************************
- * Call 'method' with 'parameters' and return its result.
- *************************************************************************/
- protected Object doCall (String method, Object[] parameters)
- throws MobyException {
-
- Call call = null;
- try {
- Service service = new Service();
- call = (Call) service.createCall();
- call.setTargetEndpointAddress (endpoint);
- call.setTimeout (new Integer (0));
-
- call.setSOAPActionURI (uri + "#" + method);
-
- if (debug) {
- System.err.println ("METHOD CALL: " + method);
- System.err.println ("------------");
- if (parameters.length > 0)
- System.err.println (parameters[0] + "\n");
- System.err.println ("------------\n");
-
- Object result = call.invoke (uri, method, parameters);
-
- System.err.println ("METHOD RETURN:");
- System.err.println ("------------");
- if (result != null)
- System.err.println (result + "\n");
- System.err.println ("------------\n");
-
- return resultToString (result);
-
- } else {
- return resultToString (call.invoke (uri, method, parameters));
- }
-
- } catch (AxisFault e) {
- throw new MobyException
- (formatFault (e,
- endpoint.toString(),
- (call == null ? null : call.getOperationName())),
- e);
-// (endpoint.toString()+(call == null ? "" : call.getOperationName()), e);
-
- } catch (Exception e) {
- throw new MobyException (e.toString(), e);
-// e.printStackTrace();
- }
- }
-
-
- /**************************************************************************
- * Parse the given XML sniplet to find tag 'success'. If it has value '1'
- * look further for tag 'id' and return it back (or return an empty string
- * if ID is not there). Otherwise raise an exception with the 'culprit'
- * and with the message from the tag 'message'. <p>
- *
- * The return value is a two-element long array. The first element
- * is the ID (given by BioMobe registry), and the second element
- * is RDF corresponding with the registered object (BioMoby
- * returns this only for service instances, so for other objects
- * this will be null). <p>
- *
- * This is how the XML is supposed to look:
- * <MOBYRegistration>
- * <success> <!-- 1 | 0 | -1 --> </success>
- * <id> <!-- some id number for your registration --> </id>
- * <message> <![CDATA[message here]]> </message>
- * <RDF> <!-- RDF of your service instance here (if applicable) --> </RDF>
- * </MOBYRegistration>
- *
- * Success takes the value "1" to indicate success, "0" to
- * indicate failure, and "-1" to indicate "Pending Curation".
- *************************************************************************/
- protected String[] checkRegistration (String xml, Object culprit)
- throws MobyException, NoSuccessException, PendingCurationException {
-
- String id = "", success = "0", message = "", rdf = "";
-
- // parse returned XML
- Document document = loadDocument (new ByteArrayInputStream (xml.getBytes()));
- Element root = document.getDocumentElement();
-
- NodeList children = root.getChildNodes();
- for (int i = 0; i < children.getLength(); i++) {
- if (children.item (i).getNodeType() != Node.ELEMENT_NODE)
- continue;
- Element elem = (Element)children.item (i);
- if (elem.getNodeName().equals ("id")) {
- if (elem.getFirstChild() != null)
- id = elem.getFirstChild().getNodeValue();
- } else if (elem.getNodeName().equals("success")) {
- if (elem.getFirstChild() != null)
- success = elem.getFirstChild().getNodeValue();
- } else if (elem.getNodeName().equals ("message")) {
- if (elem.getFirstChild() != null)
- message = elem.getFirstChild().getNodeValue();
- } else if (elem.getNodeName().equals ("RDF")) {
- if (elem.getFirstChild() != null)
- rdf = elem.getFirstChild().getNodeValue();
- }
- }
-
- if (success.equals ("0"))
- throw new NoSuccessException (message, culprit);
- else if (success.equals ("-1"))
- throw new PendingCurationException();
- return new String[] { id, rdf };
- }
-
- /**************************************************************************
- * Return a piece of XML created from the definitions representing input
- * data types and their usage in the given service. Only data considered
- * primary are included. Note that the main job of converting to XML is
- * done by instances of MobyPrimaryData.
- *
- * The returned XML looks like this:
- * <Input>
- * <!-- zero or more Primary (Simple and/or Complex) articles -->
- * </Input>
- *************************************************************************/
- protected String buildPrimaryInputTag (MobyService service) {
- StringBuffer buf = new StringBuffer();
- MobyData[] primaryInputs = service.getPrimaryInputs();
- buf.append ("<Input>\n");
- for (int i = 0; i < primaryInputs.length; i++)
- buf.append (primaryInputs[i].toXML());
- buf.append ("</Input>\n");
- return new String (buf);
- }
-
- /**************************************************************************
- * Return a piece of XML created from the definitions representing input
- * data types and their usage in the given service. Only data considered
- * secondary are included. Note that the main job of converting to XML is
- * done by instances of MobySecondaryData.
- *
- * The returned XML looks like this:
- * <secondaryArticles>
- * <!-- zero or more INPUT Secondary articles -->
- * </secondaryArticles>
- *************************************************************************/
- protected String buildSecondaryInputTag (MobyService service) {
- StringBuffer buf = new StringBuffer();
- MobyData[] secInputs = service.getSecondaryInputs();
- buf.append ("<secondaryArticles>\n");
- for (int i = 0; i < secInputs.length; i++) {
- buf.append (secInputs[i].toXML());
- }
- buf.append ("</secondaryArticles>\n");
- return new String (buf);
- }
-
- /**************************************************************************
- * Return a piece of XML created from the definitions representing output
- * data types and their usage in the given service. Only data considered
- * primary are included. Note that the main job of converting to XML is
- * done by instances of MobyPrimaryData.
- *
- * The returned XML looks like this:
- * <Output>
- * <!-- zero or more Primary (Simple and/or Complex) articles -->
- * </Output>
- *
- *************************************************************************/
- protected String buildOutputTag (MobyService service) {
- StringBuffer buf = new StringBuffer();
- MobyData[] primaryOutputs = service.getPrimaryOutputs();
- buf.append ("<Output>\n");
- for (int i = 0; i < primaryOutputs.length; i++)
- buf.append (primaryOutputs[i].toXML());
- buf.append ("</Output>\n");
- return new String (buf);
- }
-
- /**************************************************************************
- * Return a piece of XML represented a query object (an object used
- * to find a service).
- *
- * The returned XML looks like this:
- *
- * <inputObjects>
- * <Input>
- * <!-- one or more Simple or Complex Primary articles -->
- * </Input>
- * </inputObjects>
- * <outputObjects>
- * <Output>
- * <!-- one or more Simple or Complex Primary articles -->
- * </Output>
- * </outputObjects>
- * <serviceType>ServiceTypeTerm</serviceType>
- * <serviceName>ServiceName</serviceName>
- * <Category>moby</Category>
- * <authURI>http://desired.service.provider</authURI>;
- * <expandObjects>1|0</expandObjects>
- * <expandServices>1|0</expandServices>
- * <authoritative>1|0</authoritative>
- * <keywords>
- * <keyword>something</keyword>
- * ....
- * ....
- * </keywords>
- *************************************************************************/
- protected String buildQueryObject (MobyService service,
- String[] keywords,
- boolean expandObjects,
- boolean expandServices,
- boolean authoritative) {
- if (service == null) {
- service = new MobyService ("dummy");
- service.setCategory ("");
- }
- StringBuffer buf = new StringBuffer();
-
- buf.append ("<inputObjects>\n<Input>\n");
- MobyData[] pi = service.getPrimaryInputs();
- if (pi.length > 0) {
- for (int i = 0; i < pi.length; i++)
- buf.append (pi[i].toXML());
- }
- buf.append ("</Input>\n</inputObjects>\n");
-
- buf.append ("<outputObjects>\n<Output>\n");
- MobyData[] po = service.getPrimaryOutputs();
- if (po.length > 0) {
- for (int i = 0; i < po.length; i++)
- buf.append (po[i].toXML());
- }
- buf.append ("</Output>\n</outputObjects>\n");
-
- buf.append ("<serviceType>" + service.getType() + "</serviceType>\n");
-
- String name = service.getName();
- if (!name.equals ("") && !name.equals ("dummy") && !name.equals (MobyService.DUMMY_NAME))
- buf.append ("<serviceName>" + service.getName() + "</serviceName>\n");
-
- String sigURL = service.getSignatureURL();
- if (!sigURL.equals (""))
- buf.append ("<signatureURL>" + sigURL + "</signatureURL>\n");
-
- buf.append ("<Category>" + service.getCategory() + "</Category>\n");
- buf.append ("<authURI>" + service.getAuthority() + "</authURI>\n");
-
- buf.append ("<expandObjects>");
- buf.append (expandObjects ? "1" : "0");
- buf.append ("</expandObjects>\n");
-
- buf.append ("<expandServices>");
- buf.append (expandServices ? "1" : "0");
- buf.append ("</expandServices>\n");
-
- buf.append ("<authoritative>");
- buf.append (authoritative ? "1" : "0");
- buf.append ("</authoritative>\n");
-
- buf.append ("<keywords>\n");
- if (keywords != null && keywords.length > 0) {
- for (int i = 0; i < keywords.length; i++) {
- buf.append ("<keyword>");
- buf.append (keywords[i]);
- buf.append ("</keyword>\n");
- }
- }
- buf.append ("</keywords>\n");
-
- return new String (buf);
- }
-
- /**************************************************************************
- * Extract one or more MobyService objects from the given XML piece.
- * The XML should look like this:
- * <pre>
- * <Services>
- * <Service authURI="authority.URI.here" lsid="..." serviceName="MyService">
- * <serviceType>Service_Ontology_Term</serviceType>
- * <Category>moby</Category> <!-- or 'cgi' or 'soap' -->
- * <contactEmail>your at email.addy.here</contactEmail>
- * <signatureURL>http://service.RDF.here</signatureURL>
- * <URL>http://service.endpoint.here/scriptname</URL>
- * <authoritative>1</authoritative>
- * <Input>
- * <!-- one or more Simple and/or Complex Primary articles -->
- * </Input>
- * <Output>
- * <!-- one or more Simple and/or Complex Primary articles -->
- * </Output>
- * <secondaryArticles>
- * <!-- one or more Secondary articles -->
- * </secondaryArticles>
- * <Description><![CDATA[free text description here]]></Description>
- * </Service>
- * ... <!-- one or more Service blocks may be returned -->
- * ...
- * ...
- * </Services>
- * </pre>
- * @throws MobyException if the XML document is invalid
- *************************************************************************/
- public MobyService[] extractServices (String xml)
- throws MobyException {
-
- Document document = loadDocument (new ByteArrayInputStream (xml.getBytes()));
- NodeList list = document.getElementsByTagName ("Service");
- MobyService[] results = new MobyService [list.getLength()];
- for (int i = 0; i < list.getLength(); i++) {
- Element elem = (Element)list.item (i);
- MobyService service = new MobyService (elem.getAttribute ("serviceName"));
- service.setAuthority (elem.getAttribute ("authURI"));
- service.setLSID (elem.getAttribute ("lsid"));
- NodeList children = elem.getChildNodes();
- for (int j = 0; j < children.getLength(); j++) {
- String nodeName = children.item (j).getNodeName();
- if (nodeName.equals ("Description")) {
- service.setDescription (getFirstValue (children.item (j)));
- } else if (nodeName.equals ("Category")) {
- service.setCategory (getFirstValue (children.item (j)));
- } else if (nodeName.equals ("URL")) {
- service.setURL (getFirstValue (children.item (j)));
- } else if (nodeName.equals ("signatureURL")) {
- service.setSignatureURL (getFirstValue (children.item (j)));
- } else if (nodeName.equals ("contactEmail")) {
- service.setEmailContact (getFirstValue (children.item (j)));
- } else if (nodeName.equals ("serviceType")) {
- service.setType (getFirstValue (children.item (j)));
- MobyServiceType mst = new MobyServiceType(service.getType());
- NamedNodeMap map = (children.item (j).getAttributes());
- if (map != null) {
- Node node = map.getNamedItemNS(children.item(j).getNamespaceURI(),"lsid");
- if (node != null)
- mst.setLSID(node.getNodeValue());
- }
- service.setServiceType(mst);
- } else if (nodeName.equals ("authoritative")) {
- String authoritative = getFirstValue (children.item (j));
- service.setAuthoritative (authoritative.equals ("1") ? true : false);
- } else if (nodeName.equals ("Input")) {
- // <Input>
- // <!-- one or more Simple and/or Complex Primary articles -->
- // <Simple articleName="NameOfArticle">
- // ...
- // </Simple>
- // <Collection articleName="NameOfArticle">
- // <Simple>......</Simple>
- // <Simple>......</Simple>
- // </Collection>
- // </Input>
- NodeList inputs = children.item (j).getChildNodes();
- for (int k = 0; k < inputs.getLength(); k++) {
- if (inputs.item (k).getNodeName().equals ("Simple")) {
- MobyPrimaryDataSimple data = new MobyPrimaryDataSimple ((Element)inputs.item (k));
- service.addInput (data);
- } else if (inputs.item (k).getNodeName().equals ("Collection")) {
- MobyPrimaryDataSet data = new MobyPrimaryDataSet ((Element)inputs.item (k));
- service.addInput (data);
- }
- }
- } else if (nodeName.equals ("Output")) {
- // <Output>
- // <!-- one or more Simple and/or Complex Primary articles -->
- // </Output>
- NodeList inputs = children.item (j).getChildNodes();
- for (int k = 0; k < inputs.getLength(); k++) {
- if (inputs.item (k).getNodeName().equals ("Simple")) {
- MobyPrimaryDataSimple data = new MobyPrimaryDataSimple ((Element)inputs.item (k));
- service.addOutput (data);
- } else if (inputs.item (k).getNodeName().equals ("Collection")) {
- MobyPrimaryDataSet data = new MobyPrimaryDataSet ((Element)inputs.item (k));
- service.addOutput (data);
- }
- }
-
- } else if (nodeName.equals ("secondaryArticles")) {
- // <Parameter articleName="NameOfArticle">
- // ...
- // </Parameter>
- NodeList parameters = children.item (j).getChildNodes();
- for (int k = 0; k < parameters.getLength(); k++) {
- if (parameters.item (k).getNodeName().equals ("Parameter")) {
- MobySecondaryData data = new MobySecondaryData ((Element)parameters.item (k));
- service.addInput (data);
- }
- }
- }
- }
- results [i] = service;
- }
- return results;
- }
-
- // protect against null values
- protected String getFirstValue (Node child) {
- Node node = child.getFirstChild();
- if (node == null) return "";
- String value = node.getNodeValue();
- if (value == null) return "";
- return value;
- }
-
- protected String getFirstValue (NodeList children) {
- if (children.item(0) != null && children.item(0).hasChildNodes()) {
- children.item(0).normalize();
- return getFirstValue (children.item(0));
- }
- return "";
- }
-
- /**************************************************************************
- *
- * Implementing SimpleCache interface.
- *
- * Why to have an interface for such trivial thing? Well, because
- * I needed to overwrite the caching mechanism in the subclasses
- * so I needed to have all caching functions as separate methods -
- * that's why I have collect them in an interface.
- *
- *************************************************************************/
- private Hashtable<String,Object> cache; // this is the cache itself
- private boolean useCache; // this signal that we are actually caching things
-
- // not used here
- public String createId (String rootName,
- String semanticType, String syntaxType,
- long lastModified,
- Properties props) {
- return ""; // not used here
- }
-
- // check existence of a cached object
- public boolean existsInCache (String id) {
- synchronized (cache) {
- if (useCache) return cache.containsKey (id);
- else return false;
- }
- }
-
- // retrieve from cache
- public Object getContents (String id) {
- synchronized (cache) {
- if (useCache) return cache.get (id);
- else return null;
- }
- }
-
- // cache an object
- public void setContents (String id, java.lang.Object data) {
- synchronized (cache) {
- if (useCache) cache.put (id, data);
- }
- }
-
- // in this implementation, it clears the whole cache, regardless
- // what 'id' is passed
- public void removeFromCache (String id) {
- cache.clear();
- }
-
- /**************************************************************************
- *
- * And the other methods related to caching (but not part of the
- * SimpleCache interface).
- *
- **************************************************************************/
-
- /**************************************************************************
- * By default, caching is enabled to reduce network traffic.
- * Setting this to false will clear the cache, and not cache any
- * further calls unless it is set to true again. <p>
- *
- * @param shouldCache whether retrieveXXX call results should be
- * cached in case they are called again (i.e. don't request
- * MobyCentral every time)
- **************************************************************************/
- public void setCacheMode (boolean shouldCache) {
- useCache = shouldCache;
- if (! useCache)
- removeFromCache (null);
- }
-
- /**************************************************************************
- * Find if caching is currently enabled.
- *
- * @return true if caching is enabled
- **************************************************************************/
- public boolean getCacheMode(){
- return useCache;
- }
-
- /**************************************************************************
- * Parses and imports the following XML.
- * <pre>
- * <serviceNames>
- * <serviceName name="serviceName" authURI='authority.info.here'/>
- * ...
- * ...
- * </serviceNames>
- * </pre>
- *
- * @deprecated Replaced by {@link
- * #getServiceNamesByAuthority}. The reason is that this method
- * returns a random result if there are more services with the
- * same name but belonging to different authorities. <p>
- *
- *************************************************************************/
- public Map<String,String> getServiceNames()
- throws MobyException {
-
- String result = (String)doCall ("retrieveServiceNames",
- new Object[] {});
- // parse returned XML
- Map<String,String> results = new TreeMap<String,String> (getStringComparator());
- Document document = loadDocument (new ByteArrayInputStream (result.getBytes()));
- NodeList list = document.getElementsByTagName ("serviceName");
- for (int i = 0; i < list.getLength(); i++) {
- Element elem = (Element)list.item (i);
- results.put (elem.getAttribute ("name"),
- elem.getAttribute ("authURI"));
- }
-
- return results;
- }
-
- /**************************************************************************
- * Parses and imports the following XML.
- * <pre>
- * <serviceNames>
- * <serviceName name="serviceName" lsid="..." authURI='authority.info.here'/>
- * ...
- * ...
- * </serviceNames>
- * </pre>
- *
- * @return a Map which has authorities as keys, and String arrays
- * with service names as a values.
- *************************************************************************/
- public Map getServiceNamesByAuthority()
- throws MobyException {
- String result = getServiceNamesByAuthorityAsXML();
- return createServicesByAuthorityFromXML (result, true);
- }
-
- /**************************************************************************
- * Similar to {@link #getServiceNamesByAuthority} but the
- * resulting Map contains slightly more. <p>
- *
- * @return a Map which has authorities as keys, and arrays of
- * MobyServices as a values. Each MobyService is filled with its
- * name, authority and LSID.
- *************************************************************************/
- public Map getServicesByAuthority()
- throws MobyException {
- String result = getServiceNamesByAuthorityAsXML();
- return createServicesByAuthorityFromXML (result, false);
- }
-
- //
- protected String getServiceNamesByAuthorityAsXML()
- throws MobyException {
- return (String)doCall ("retrieveServiceNames",
- new Object[] {});
- }
-
- // if onlyNames == true
- // Map: authority name -> String[]
- // (filled with service namea)
- // else
- // Map: authority name -> MobyService[]
- // (filled with service name, authority and lsid)
- protected Map createServicesByAuthorityFromXML (String result,
- boolean onlyNames)
- throws MobyException {
-
- // parse returned XML
- Map results = new TreeMap (getStringComparator());
- Document document = loadDocument (new ByteArrayInputStream (result.getBytes()));
- NodeList list = document.getElementsByTagName ("serviceName");
- for (int i = 0; i < list.getLength(); i++) {
- Element elem = (Element)list.item (i);
- String name = elem.getAttribute ("name");
- String auth = elem.getAttribute ("authURI");
- Vector<Object> v =
- (results.containsKey (auth) ? (Vector)results.get (auth) : new Vector<Object>());
- if (onlyNames) {
- v.addElement (name);
- } else {
- MobyService ms = new MobyService (name);
- ms.setAuthority (auth);
- ms.setLSID (elem.getAttribute ("lsid"));
- v.addElement (ms);
- }
- results.put (auth, v);
- }
-
- // change values of type Vector to MobyService[] or String[]
- for (Iterator it = results.entrySet().iterator(); it.hasNext(); ) {
- Map.Entry entry = (Map.Entry)it.next();
- Vector v = (Vector)entry.getValue();
- if (onlyNames) {
- String[] sNames = new String [v.size()];
- v.copyInto (sNames);
- entry.setValue (sNames);
- } else {
- MobyService[] mss = new MobyService [v.size()];
- v.copyInto (mss);
- entry.setValue (mss);
- }
- }
-
- return results;
- }
-
- /**************************************************************************
- * Parses and imports the following XML.
- * <pre>
- * <serviceProviders>
- * <serviceProvider name="authority.URI.here"/>
- * ...
- * ...
- * </serviceProviders>
- * </pre>
- *************************************************************************/
- public String[] getProviders()
- throws MobyException {
-
- String cacheId = "retrieveServiceProviders";
- String[] cachedResults = (String[])getContents (cacheId);
- if (cachedResults != null)
- return cachedResults;
-
- String result = (String)doCall ("retrieveServiceProviders",
- new Object[] {});
-
- // parse returned XML
- Document document = loadDocument (new ByteArrayInputStream (result.getBytes()));
- NodeList list = document.getElementsByTagName ("serviceProvider");
- String[] results = new String [list.getLength()];
- for (int i = 0; i < list.getLength(); i++)
- results[i] = ((Element)list.item (i)).getAttribute ("name");
-
- // Add this data to the cache in case we get called again
- setContents (cacheId, results);
-
- return results;
- }
-
- /**************************************************************************
- * Parses and imports the following XML.
- * <pre>
- * <serviceTypes>
- * <serviceType name="serviceName" lsid="...">
- * <Description><![CDATA[free text description here]]></Description>
- * <contactEmail>...</contactEmail>
- * <authURI>...</authURI>
- * </serviceType>
- * ...
- * ...
- * </serviceTypes>
- * </pre>
- *************************************************************************/
- public Map getServiceTypes()
- throws MobyException {
- String result = getServiceTypesAsXML();
- Map results = new TreeMap (getStringComparator());
- MobyServiceType[] types = createServiceTypesFromXML (result);
- for (int i = 0; i < types.length; i++) {
- results.put (types[i].getName(),
- types[i].getDescription());
- }
- return results;
- }
-
- //
- protected String getServiceTypesAsXML()
- throws MobyException {
- return (String)doCall ("retrieveServiceTypes",
- new Object[] {});
- }
-
- // but be aware that the created MobyServiceTypes are not complete
- // - they do not have the relationship information; that's why
- // this method is not public; the full service types are available
- // from CentralDigest implementations
- protected MobyServiceType[] createServiceTypesFromXML (String result)
- throws MobyException {
-
- // parse returned XML
- Document document = loadDocument (new ByteArrayInputStream (result.getBytes()));
- NodeList list = document.getElementsByTagName ("serviceType");
- if (list == null || list.getLength() == 0)
- return new MobyServiceType[] {};
- MobyServiceType[] results = new MobyServiceType [list.getLength()];
- for (int i = 0; i < list.getLength(); i++) {
- Element elem = (Element)list.item (i);
- MobyServiceType st = new MobyServiceType (elem.getAttribute ("name"));
- st.setLSID (elem.getAttribute ("lsid"));
- st.setDescription (getFirstValue (elem.getElementsByTagName ("Description")));
- st.setEmailContact (getFirstValue (elem.getElementsByTagName ("contactEmail")));
- st.setAuthority (getFirstValue (elem.getElementsByTagName ("authURI")));
- results[i] = st;
- }
- java.util.Arrays.sort (results);
- return results;
- }
-
- /**************************************************************************
- * Parses and imports the following XML.
- * <pre>
- * <Namespaces>
- * <Namespace name="namespace" lsid="...">
- * <Description><![CDATA[free text description here]]></Description>
- * <contactEmail>...</contactEmail>
- * <authURI>...</authURI>
- * </Namespace>
- * ...
- * ...
- * </Namespaces>
- * </pre>
- *************************************************************************/
- public MobyNamespace[] getFullNamespaces()
- throws MobyException {
-
- String result = getNamespacesAsXML();
- return createNamespacesFromXML (result);
- }
-
- //
- protected String getNamespacesAsXML()
- throws MobyException {
- return (String)doCall ("retrieveNamespaces",
- new Object[] {});
- }
-
- //
- protected MobyNamespace[] createNamespacesFromXML (String result)
- throws MobyException {
-
- // parse returned XML
- Document document = loadDocument (new ByteArrayInputStream (result.getBytes()));
- NodeList list = document.getDocumentElement().getElementsByTagName ("Namespace");
- if (list == null || list.getLength() == 0) {
- return new MobyNamespace[] {};
- }
- MobyNamespace[] results = new MobyNamespace [list.getLength()];
- for (int i = 0; i < list.getLength(); i++) {
- Element elem = (Element)list.item (i);
- MobyNamespace nm = new MobyNamespace (elem.getAttribute ("name"));
- nm.setLSID (elem.getAttribute ("lsid"));
- nm.setDescription (getFirstValue (elem.getElementsByTagName ("Description")));
- nm.setEmailContact (getFirstValue (elem.getElementsByTagName ("contactEmail")));
- nm.setAuthority (getFirstValue (elem.getElementsByTagName ("authURI")));
- results[i] = nm;
- }
-
- java.util.Arrays.sort (results);
- return results;
- }
-
- /**************************************************************************
- *
- * @deprecated Replaced by {@link #getFullNamespaces} that gives
- * more information for the same price. <p>
- *************************************************************************/
- public Map getNamespaces()
- throws MobyException {
-
- Map results = new TreeMap (getStringComparator());
- MobyNamespace[] namespaces = getFullNamespaces();
- for (int i = 0; i < namespaces.length; i++) {
- results.put (namespaces[i].getName(),
- namespaces[i].getDescription());
- }
- return results;
- }
-
- /**************************************************************************
- * Parses and imports the following XML.
- * <pre>
- * <objectNames>
- * <Object name="objectName" lsid="...">
- * <Description><![CDATA[free text description here]]></Description>
- * </Object>
- * ...
- * ...
- * </objectNames>
- * </pre>
- *************************************************************************/
- public Map getDataTypeNames()
- throws MobyException {
- String result = getDataTypeNamesAsXML();
- return createDataTypeNamesFromXML (result, true);
- }
-
- //
- protected String getDataTypeNamesAsXML()
- throws MobyException {
- return (String)doCall ("retrieveObjectNames",
- new Object[] {});
- }
-
- // if onlyNames == true
- // Map: data type name -> description (String)
- // else
- // Map: data type name -> MobyDataType[]
- // (filled with name, description, and lsid)
- protected Map createDataTypeNamesFromXML (String result,
- boolean onlyNames)
- throws MobyException {
-
- // parse returned XML
- Map results = new TreeMap (getStringComparator());
- Document document = loadDocument (new ByteArrayInputStream (result.getBytes()));
- NodeList list = document.getElementsByTagName ("Object");
- for (int i = 0; i < list.getLength(); i++) {
- Element elem = (Element)list.item (i);
- String name = elem.getAttribute ("name");
- if (name == null)
- continue; // ignore no-named data types
- String desc = "";
- NodeList children = elem.getChildNodes();
- for (int j = 0; j < children.getLength(); j++) {
- if (children.item (j).getNodeName().equals ("Description")) {
- desc = getFirstValue (children.item (j));
- break;
- }
- }
- if (onlyNames) {
- results.put (name, desc);
- } else {
- MobyDataType dt = new MobyDataType (name);
- dt.setDescription (desc);
- dt.setLSID (elem.getAttribute ("lsid"));
- results.put (name, dt);
- }
- }
- return results;
- }
-
-
- /**************************************************************************
- * Parses and imports the following XML. An example:
- *
- * <pre>
- * <retrieveObjectDefinition>
- * <objectType lsid="...">go_term</objectType>
- * <Description><![CDATA[A very lightweight object holding a GO term name and its definition]]></Description>
- * <authURI>http://www.illuminae.com</authURI>
- * <contactEmail>markw at illuminae.com</contactEmail>
- * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:isa'>
- * <objectType articleName=''>urn:lsid:biomoby.org:objectclass:object</objectType>
- * </Relationship>
- * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:hasa'>
- * <objectType articleName='Term'>urn:lsid:biomoby.org:objectclass:string</objectType>
- * <objectType articleName='Definition'>urn:lsid:biomoby.org:objectclass:string</objectType>
- * </Relationship>
- * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:has'>
- * <objectType articleName='Problems'>urn:lsid:biomoby.org:objectclass:string</objectType>
- * <objectType articleName='Issues'>urn:lsid:biomoby.org:objectclass:string</objectType>
- * </Relationship>
- * </retrieveObjectDefinition>
- * </pre>
- *************************************************************************/
- public MobyDataType getDataType (String dataTypeName)
- throws MobyException, NoSuccessException {
-
- String result = getDataTypeAsXML (dataTypeName);
- return createDataTypeFromXML (result, dataTypeName);
- }
-
- public MobyDataType[] getDataTypes()
- throws MobyException, NoSuccessException {
- Map<String,String> datatypeMap = getDataTypeNames();
- MobyDataType[] datatypes = new MobyDataType[datatypeMap.size()];
- int i = 0;
- for(String dataTypeName: datatypeMap.keySet()){
- datatypes[i++] = getDataType(dataTypeName);
- }
- return datatypes;
- }
-
- protected String getDataTypeAsXML (String dataTypeName)
- throws MobyException, NoSuccessException {
-
- return (String)doCall ("retrieveObjectDefinition",
- new Object[] {
- "<retrieveObjectDefinition>" +
- "<objectType>" + dataTypeName + "</objectType>" +
- "</retrieveObjectDefinition>"
- });
- }
-
- protected MobyDataType createDataTypeFromXML (String xmlSource, String dataTypeName)
- throws MobyException, NoSuccessException {
-
- // parse returned XML
- Document document = loadDocument (new ByteArrayInputStream (xmlSource.getBytes()));
- NodeList list = document.getElementsByTagName ("retrieveObjectDefinition");
- if (list == null || list.getLength() == 0)
- throw new NoSuccessException ("Data Type name was not found.",
- dataTypeName);
- MobyDataType data = null;
- Element elem = (Element)list.item (0);
- NodeList children = elem.getChildNodes();
-
- // first find the "real" (LSID-ized) data type name
- for (int j = 0; j < children.getLength(); j++) {
- String nodeName = children.item (j).getNodeName();
- if (nodeName.equals ("objectType")) {
- data = new MobyDataType (getFirstValue (children.item (j)));
- data.setLSID ( ((Element)children.item (j) ).getAttribute ("lsid"));
- break;
- }
- }
-
- // if not found (unprobable) use the name given by the caller
- if (data == null)
- data = new MobyDataType (dataTypeName);
-
- // now fill the data type object with the rest of attributes
- for (int j = 0; j < children.getLength(); j++) {
- String nodeName = children.item (j).getNodeName();
- if (nodeName.equals ("Description")) {
- data.setDescription (getFirstValue (children.item (j)));
- } else if (nodeName.equals ("authURI")) {
- data.setAuthority (getFirstValue (children.item (j)));
- } else if (nodeName.equals ("contactEmail")) {
- data.setEmailContact (getFirstValue (children.item (j)));
- } else if (nodeName.equals ("Relationship")) {
- String relationshipType = ((Element)children.item (j)).getAttribute ("relationshipType");
- if (relationshipType.endsWith ("isa")) {
-
- NodeList parents = children.item (j).getChildNodes();
- for (int k = 0; k < parents.getLength(); k++) {
- if (parents.item (k).getNodeName().equals ("objectType")) {
- data.addParentName (getFirstValue (parents.item (k)));
- }
- }
- } else if (relationshipType.endsWith ("hasa")) {
-
- NodeList belows = children.item (j).getChildNodes();
- for (int k = 0; k < belows.getLength(); k++) {
- if (belows.item (k).getNodeName().equals ("objectType")) {
- data.addChild ( ((Element)belows.item (k)).getAttribute ("articleName"),
- getFirstValue (belows.item (k)),
- Central.iHASA );
- }
- }
- } else if (relationshipType.endsWith ("has")) {
-
- NodeList belows = children.item (j).getChildNodes();
- for (int k = 0; k < belows.getLength(); k++) {
- if (belows.item (k).getNodeName().equals ("objectType")) {
- data.addChild ( ((Element)belows.item (k)).getAttribute ("articleName"),
- belows.item (k).getFirstChild().getNodeValue(),
- Central.iHAS );
- }
- }
- }
- }
- }
- return data;
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- public String getServiceWSDL (String serviceName)
- throws MobyException, NoSuccessException {
-
- Map names = getServiceNames();
-
- for (Iterator it = names.entrySet().iterator(); it.hasNext(); ) {
- Map.Entry entry = (Map.Entry)it.next();
- if ( ((String)entry.getKey()).equals (serviceName) )
- return getServiceWSDL (serviceName, (String)entry.getValue());
- }
-
- throw new NoSuccessException ("Service not found.", serviceName);
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- public String getServiceWSDL (String serviceName, String authority)
- throws MobyException, NoSuccessException {
-
- String cacheId = "getServiceWSDL" + serviceName + ":" + authority;
- String cachedResults = (String)getContents (cacheId);
- if (cachedResults != null)
- return cachedResults;
-
- String result =
- (String)doCall ("retrieveService",
- new Object[] {
- "<retrieveService>" +
- "<Service authURI=\"" + authority + "\" serviceName=\"" + serviceName + "\"/>" +
- "</retrieveService>"
- });
-
- // parse returned XML
- Document document = loadDocument (new ByteArrayInputStream (result.getBytes()));
- Element service = document.getDocumentElement();
- Node wsdl = service.getFirstChild();
- if (wsdl == null)
- throw new NoSuccessException ("Service not found OR WSDL is not available.",
- serviceName + " (" + authority + ")");
-
- String results = wsdl.getNodeValue();
- setContents (cacheId, results);
- return results;
- }
-
- /*************************************************************************
- *
- *************************************************************************/
- public String getRegisterDataTypeXML (MobyDataType dataType) {
-
- // build the ISA tag (expressing hierarchy of data types)
- String[] names = dataType.getParentNames();
- StringBuffer buf = new StringBuffer();
- for (int i = 0; i < names.length; i++) {
- buf.append ("<objectType>");
- buf.append (names[i]);
- buf.append ("</objectType>");
- buf.append ("\n");
- }
-
- // build the HASA/HAS tags (expressing containments of data types)
- MobyRelationship[] children = dataType.getChildren();
- StringBuffer buf2 = new StringBuffer(); // for HASA
- StringBuffer buf3 = new StringBuffer(); // for HAS
- for (int i = 0; i < children.length; i++) {
- if (children[i].getRelationshipType() == Central.iHASA) {
- buf2.append ("<objectType articleName=\"");
- buf2.append (children[i].getName());
- buf2.append ("\">");
- buf2.append (children[i].getDataTypeName());
- buf2.append ("</objectType>");
- } else if (children[i].getRelationshipType() == Central.iHAS) {
- buf3.append ("<objectType articleName=\"");
- buf3.append (children[i].getName());
- buf3.append ("\">");
- buf3.append (children[i].getDataTypeName());
- buf3.append ("</objectType>");
- }
- }
-
- return
- "<registerObjectClass>" +
- "<objectType>" + dataType.getName() + "</objectType>" +
- "<Description><![CDATA[" + dataType.getDescription() + "]]>" +
- "</Description>" +
- "<Relationship relationshipType=\"ISA\">" + new String (buf) +
- "</Relationship>" +
- "<Relationship relationshipType=\"HASA\">" + new String (buf2) +
- "</Relationship>" +
- "<Relationship relationshipType=\"HAS\">" + new String (buf3) +
- "</Relationship>" +
- "<authURI>" + dataType.getAuthority() + "</authURI>" +
- "<contactEmail>" + dataType.getEmailContact() + "</contactEmail>" +
- "</registerObjectClass>";
- }
-
- /*************************************************************************
- *
- *************************************************************************/
- public void registerDataType (MobyDataType dataType)
- throws MobyException, NoSuccessException, PendingCurationException {
-
- String result =
- (String)doCall ("registerObjectClass",
- new Object[] { getRegisterDataTypeXML (dataType) });
- dataType.setId (checkRegistration (result, dataType)[0]);
- }
-
- /*************************************************************************
- * B
- *************************************************************************/
- public void unregisterDataType (MobyDataType dataType)
- throws MobyException, NoSuccessException, PendingCurationException {
- String result =
- (String)doCall ("deregisterObjectClass",
- new Object[] {
- "<deregisterObjectClass>" +
- "<objectType>" + dataType.getName() + "</objectType>" +
- "</deregisterObjectClass>"
- });
- checkRegistration (result, dataType);
- }
-
- /*************************************************************************
- *
- *************************************************************************/
- public String getRegisterServiceTypeXML (MobyServiceType serviceType) {
-
- // build the ISA tag (expressing hierarchy of service types)
- String[] names = serviceType.getParentNames();
- StringBuffer buf = new StringBuffer();
- for (int i = 0; i < names.length; i++) {
- buf.append ("<serviceType>");
- buf.append (names[i]);
- buf.append ("</serviceType>");
- buf.append ("\n");
- }
-
- return
- "<registerServiceType>" +
- "<serviceType>" + serviceType.getName() + "</serviceType>" +
- "<contactEmail>" + serviceType.getEmailContact() + "</contactEmail>" +
- "<authURI>" + serviceType.getAuthority() + "</authURI>" +
- "<Description><![CDATA[" + serviceType.getDescription() + "]]>" +
- "</Description>" +
- "<Relationship relationshipType=\"ISA\">" + new String (buf) +
- "</Relationship>" +
- "</registerServiceType>";
- }
-
- /*************************************************************************
- *
- *************************************************************************/
- public void registerServiceType (MobyServiceType serviceType)
- throws MobyException, NoSuccessException, PendingCurationException {
-
- String result =
- (String)doCall ("registerServiceType",
- new Object[] { getRegisterServiceTypeXML (serviceType) });
- serviceType.setId (checkRegistration (result, serviceType)[0]);
- }
-
- /*************************************************************************
- *
- *************************************************************************/
- public void unregisterServiceType (MobyServiceType serviceType)
- throws MobyException, NoSuccessException, PendingCurationException {
- String result =
- (String)doCall ("deregisterServiceType",
- new Object[] {
- "<deregisterServiceType>" +
- "<serviceType>" + serviceType.getName() + "</serviceType>" +
- "</deregisterServiceType>"
- });
- checkRegistration (result, serviceType);
- }
-
- /*************************************************************************
- *
- *************************************************************************/
- public String getRegisterNamespaceXML (MobyNamespace namespace) {
- return
- "<registerNamespace>" +
- "<namespaceType>" + namespace.getName() + "</namespaceType>" +
- "<contactEmail>" + namespace.getEmailContact() + "</contactEmail>" +
- "<authURI>" + namespace.getAuthority() + "</authURI>" +
- "<Description><![CDATA[" + namespace.getDescription() + "]]>" +
- "</Description>" +
- "</registerNamespace>";
- }
-
- /*************************************************************************
- *
- *************************************************************************/
- public void registerNamespace (MobyNamespace namespace)
- throws MobyException, NoSuccessException, PendingCurationException {
- String result =
- (String)doCall ("registerNamespace",
- new Object[] { getRegisterNamespaceXML (namespace) });
- namespace.setId (checkRegistration (result, namespace)[0]);
- }
-
- /*************************************************************************
- *
- *************************************************************************/
- public void unregisterNamespace (MobyNamespace namespace)
- throws MobyException, NoSuccessException, PendingCurationException {
- String result =
- (String)doCall ("deregisterNamespace",
- new Object[] {
- "<deregisterNamespace>" +
- "<namespaceType>" + namespace.getName() + "</namespaceType>" +
- "</deregisterNamespace>"
- });
- checkRegistration (result, namespace);
- }
-
- /*************************************************************************
- *
- *************************************************************************/
- public String getRegisterServiceXML (MobyService service) {
- return
- "<registerService>" +
- "<Category>" + service.getCategory() + "</Category>" +
- "<serviceName>" + service.getName() + "</serviceName>" +
- "<serviceType>" + service.getType() + "</serviceType>" +
- "<serviceLSID>" + (service.getLSID() == null ? "" : service.getLSID().trim() )+ "</serviceLSID>" +
- "<authURI>" + service.getAuthority() + "</authURI>" +
- "<signatureURL>" + escapeXML (service.getSignatureURL()) + "</signatureURL>" +
- "<URL>" + escapeXML (service.getURL()) + "</URL>" +
- "<contactEmail>" + service.getEmailContact() + "</contactEmail>" +
- "<authoritativeService>" + (service.isAuthoritative() ? "1" : "0") + "</authoritativeService>" +
- "<Description><![CDATA[" + service.getDescription() + "]]>" +
- "</Description>" +
- buildPrimaryInputTag (service) +
- buildSecondaryInputTag (service) +
- buildOutputTag (service) +
- "</registerService>";
- }
-
- /*************************************************************************
- *
- *************************************************************************/
- public void registerService (MobyService service)
- throws MobyException, NoSuccessException, PendingCurationException {
-
- String result =
- (String)doCall ("registerService",
- new Object[] { getRegisterServiceXML (service) });
- String[] registered = checkRegistration (result, service);
- service.setId (registered [0]);
- service.setRDF (registered [1]);
- String pathToRDF = service.getPathToRDF();
- if ( ! pathToRDF.equals ("") ) {
- File fileRDF = new File (pathToRDF);
- try {
- PrintStream fileout = new PrintStream (new FileOutputStream (fileRDF));
- fileout.println (registered [1]);
- fileout.close();
- } catch (IOException e) {
- StringBuffer buf = new StringBuffer (100);
- buf.append ("Failed to save RDF in '");
- buf.append (fileRDF.getAbsolutePath() + "'. ");
- buf.append (e.toString());
- try {
- File tmpFile = File.createTempFile (service.getName() + "-", ".rdf");
- PrintStream fileout = new PrintStream (new FileOutputStream (tmpFile));
- fileout.println (registered [1]);
- fileout.close();
- buf.append ("\nReturned RDF file was therefore stored in: ");
- buf.append (tmpFile.getAbsolutePath());
- } catch (IOException e2) {
- buf.append ("\nEven saving in a temporary file failed: ");
- buf.append (e2.toString());
- }
- throw new MobyException (buf.toString());
- }
- }
- }
-
- /*************************************************************************
- *
- *************************************************************************/
- public void unregisterService (MobyService service)
- throws MobyException, NoSuccessException, PendingCurationException {
- String result =
- (String)doCall ("deregisterService",
- new Object[] {
- "<deregisterService>" +
- "<authURI>" + service.getAuthority() + "</authURI>" +
- "<serviceName>" + service.getName() + "</serviceName>" +
- "</deregisterService>"
- });
- checkRegistration (result, service);
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- public MobyService[] findService (String serviceType)
- throws MobyException {
- if (serviceType == null)
- return new MobyService[] {};
- MobyService pattern = new MobyService ("dummy");
- pattern.setCategory ("");
- pattern.setType (serviceType);
- return findService (pattern, null);
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- public MobyService[] findService (String[] keywords)
- throws MobyException {
- if (keywords == null)
- return new MobyService[] {};
- return findService (null, keywords);
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- public MobyService[] findService (MobyService pattern)
- throws MobyException {
- if (pattern == null)
- return new MobyService[] {};
- return findService (pattern, null);
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- public MobyService[] findService (MobyService pattern, String[] keywords)
- throws MobyException {
- return findService (pattern, keywords, true, true);
- }
-
- /**************************************************************************
- * All 'findService' methods end up here.
- *************************************************************************/
- public MobyService[] findService (MobyService pattern, String[] keywords,
- boolean includeChildrenServiceTypes,
- boolean includeParentDataTypes)
- throws MobyException {
- if (pattern == null) {
- pattern = new MobyService ("dummy");
- pattern.setCategory ("");
- }
-
- String result =
- getServicesAsXML (pattern, keywords, includeChildrenServiceTypes, includeParentDataTypes);
- MobyService[] services = extractServices (result);
- return services;
- }
-
- // ...actually all 'findService' methods end up here
- protected String getServicesAsXML (MobyService pattern, String[] keywords,
- boolean includeChildrenServiceTypes,
- boolean includeParentDataTypes)
- throws MobyException {
- String[] query = new String[] {
- "<findService>" +
- buildQueryObject (pattern, keywords,
- includeParentDataTypes,
- includeChildrenServiceTypes,
- false) +
- "</findService>"
- };
- return (String)doCall ("findService", query);
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- public String call (String methodName, String inputXML)
- throws MobyException {
- Object result;
- if (inputXML == null || inputXML.equals (""))
- result = doCall (methodName, new Object[] { });
- else
- result = doCall (methodName, new Object[] { inputXML });
- return (String)result;
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- protected static String resultToString (Object result)
- throws MobyException {
- if (result == null)
- throw new MobyException ("Returned result is null.");
- if (result instanceof String)
- return (String)result;
- if (result instanceof String[]) {
- String[] tmp = (String[])result;
- StringBuffer buf = new StringBuffer();
- for (int i = 0; i < tmp.length; i++)
- buf.append (tmp[i]);
- return new String (buf);
- }
- if (result instanceof byte[])
- return new String ((byte[])result);
-
- throw new MobyException ("Unknown type of result: " + result.getClass().getName());
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- public boolean setDebug (boolean enabled) {
- boolean oldMode = debug;
- debug = enabled;
- return oldMode;
- }
-
- /**************************************************************************
- * Parses and imports the following XML.
- * <pre>
- * <Relationships>
- * <Relationship relationshipType='urn:lsid:biomoby.org:servicerelation:isa'>
- * <serviceType>urn:lsid:biomoby.org:servicetype:analysis</serviceType>
- * <serviceType>urn:lsid:biomoby.org:servicetype:service</serviceType>
- * </Relationship>
- * </Relationships>
- * </pre>
- *************************************************************************/
- public String[] getServiceTypeRelationships (String serviceTypeName,
- boolean expand)
- throws MobyException {
- String result = getServiceTypeRelationshipsAsXML (serviceTypeName, expand);
- return createServiceTypeRelationshipsFromXML (result);
- }
-
- //
- protected String getServiceTypeRelationshipsAsXML (String serviceTypeName,
- boolean expand)
- throws MobyException {
- return
- (String)doCall ("Relationships",
- new Object[] {
- "<Relationship>" +
- "<serviceType>" + serviceTypeName + "</serviceType>" +
- "<relationshipType>" + Central.ISA + "</relationshipType>" +
- "<expandRelationship>" + (expand ? "1" : "0") + "</expandRelationship>" +
- "</Relationship>"
- });
- }
-
- //
- protected String[] createServiceTypeRelationshipsFromXML (String result)
- throws MobyException {
-
- // parse returned XML
- Vector<String> v = new Vector<String>();
- Document document = loadDocument (new ByteArrayInputStream (result.getBytes()));
- NodeList list = document.getElementsByTagName ("Relationship");
- for (int i = 0; i < list.getLength(); i++) {
- Element elem = (Element)list.item (i);
- NodeList children = elem.getChildNodes();
- for (int j = 0; j < children.getLength(); j++) {
- if (children.item (j).getNodeName().equals ("serviceType")) {
- v.addElement (getFirstValue (children.item (j)));
- }
- }
- }
- String[] results = new String [v.size()];
- v.copyInto (results);
- return results;
- }
-
- /**************************************************************************
- * Parses and imports the following XML.
- * <pre>
- *<Relationships>
- * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:isa'>
- * <objectType>urn:lsid:biomoby.org:objectclass:virtualsequence</objectType>
- * <objectType>urn:lsid:biomoby.org:objectclass:object</objectType>
- * </Relationship>
- * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:hasa'>
- * <objectType>urn:lsid:biomoby.org:objectclass:string</objectType>
- * <objectType>urn:lsid:biomoby.org:objectclass:integer</objectType>
- * </Relationship>
- *</Relationships>
- * </pre>
- *
- * Added at Sun Feb 19 19:32:31 PHT 2006: it recognizes also an
- * attributes 'lsid' and 'articleName' in <objectType> element.
- *************************************************************************/
- public Map getDataTypeRelationships (String dataTypeName)
- throws MobyException {
-
- String cacheId = "getDataTypeRelationships_" + dataTypeName;
- Map cachedResults = (Map)getContents (cacheId);
- if (cachedResults != null)
- return cachedResults;
-
- String result =
- (String)doCall ("Relationships",
- new Object[] {
- "<Relationships>" +
- "<objectType>" + dataTypeName + "</objectType>" +
- "<relationshipType>" + Central.ISA + "</relationshipType>" +
- "<relationshipType>" + Central.HASA + "</relationshipType>" +
- "<relationshipType>" + Central.HAS + "</relationshipType>" +
- "<expandRelationship>1</expandRelationship>" +
- "</Relationships>"
- });
-
- // parse returned XML
- Map results = new HashMap();
- Document document = loadDocument (new ByteArrayInputStream (result.getBytes()));
- NodeList list = document.getElementsByTagName ("Relationship");
-
- for (int i = 0; i < list.getLength(); i++) {
- Element elem = (Element)list.item (i);
- String relType = elem.getAttribute ("relationshipType");
- NodeList children = elem.getChildNodes();
- Vector<String> v = new Vector<String>();
- for (int j = 0; j < children.getLength(); j++) {
- if (children.item (j).getNodeName().equals ("objectType")) {
- v.addElement (getFirstValue (children.item (j)));
- }
- }
- String[] names = new String [v.size()];
- v.copyInto (names);
- results.put (relType, names);
- }
-
- setContents (cacheId, results);
- return results;
- }
-
- /**************************************************************************
- * Parses and imports the following XML.
- * <pre>
- *<Relationships>
- * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:isa'>
- * <objectType>urn:lsid:biomoby.org:objectclass:virtualsequence</objectType>
- * <objectType>urn:lsid:biomoby.org:objectclass:object</objectType>
- * </Relationship>
- *</Relationships>
- * </pre>
- *************************************************************************/
- public String[] getDataTypeRelationships (String dataTypeName,
- String relationshipType)
- throws MobyException {
-
- String cacheId = "getDataTypeRelationships_" + dataTypeName + ":" + relationshipType;
- String[] cachedResults = (String[])getContents (cacheId);
- if (cachedResults != null)
- return cachedResults;
-
- String result =
- (String)doCall ("Relationships",
- new Object[] {
- "<Relationships>" +
- "<objectType>" + dataTypeName + "</objectType>" +
- "<relationshipType>" + relationshipType + "</relationshipType>" +
- "<expandRelationship>1</expandRelationship>" +
- "</Relationships>"
- });
-
- // parse returned XML
- Vector<String> v = new Vector<String>();
- Document document = loadDocument (new ByteArrayInputStream (result.getBytes()));
- NodeList list = document.getElementsByTagName ("Relationship");
-
- // it should always be just one element in this list
- for (int i = 0; i < list.getLength(); i++) {
- Element elem = (Element)list.item (i);
- NodeList children = elem.getChildNodes();
- for (int j = 0; j < children.getLength(); j++) {
- if (children.item (j).getNodeName().equals ("objectType")) {
- v.addElement (getFirstValue (children.item (j)));
- }
- }
- }
- String[] results = new String [v.size()];
- v.copyInto (results);
-
- setContents (cacheId, results);
- return results;
- }
-
-// /**************************************************************************
-// *
-// *************************************************************************/
-// public MobyRelationship[] getRelationships (String dataTypeName)
-// throws MobyException {
-// return null;
-// }
-
-
- /**************************************************************************
- *
- *************************************************************************/
- public String getRegistryEndpoint() {
- return endpoint.toString();
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- public String getRegistryNamespace() {
- return uri;
- }
-
- /**************************************************************************
- * Parses and imports the following XML.
- * <pre>
- * <resourceURLs>
- * <Resource name="Service" url="..." />
- * <Resource name="Object" url="..." />
- * <Resource name="Namespace" url="..." />
- * <Resource name="ServiceInstance" url="..." />
- * <Resource name="Full" url="..." />
- * </resourceURLs>
- * </pre>
- *************************************************************************/
- public MobyResourceRef[] getResourceRefs()
- throws MobyException {
-
- String cacheId = "retrieveResourceURLs";
- MobyResourceRef[] cachedResults = (MobyResourceRef[])getContents (cacheId);
- if (cachedResults != null)
- return cachedResults;
-
- String result = (String)doCall ("retrieveResourceURLs",
- new Object[] {});
-
- // parse returned XML
- Vector<MobyResourceRef> v = new Vector<MobyResourceRef>();
- Document document = loadDocument (new ByteArrayInputStream (result.getBytes()));
- NodeList list = document.getElementsByTagName ("Resource");
- for (int i = 0; i < list.getLength(); i++) {
- Element elem = (Element)list.item (i);
- try {
- v.addElement
- (new MobyResourceRef (elem.getAttribute ("name"),
- new URL ((String)elem.getAttribute ("url")),
- elem.getAttribute ("type")));
- } catch (MalformedURLException e2) {
- if (debug)
- System.err.println ("Bad URL: " + elem.getAttribute ("url"));
- }
- }
-
- MobyResourceRef[] results = new MobyResourceRef [v.size()];
- v.copyInto (results);
-
- // Add this data to the cache in case we get called again
- setContents (cacheId, results);
-
- return results;
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- public InputStream getResource (String resourceName)
- throws MobyException {
-
- MobyResourceRef[] resourceRefs = getResourceRefs();
- for (int i = 0; i < resourceRefs.length; i++) {
- if (resourceName.equalsIgnoreCase (resourceRefs[i].getResourceName())) {
- return Utils.getInputStream (resourceRefs[i].getResourceLocation());
- }
- }
- throw new MobyException ("No resource found for '" + resourceName + "'.");
- }
-
- /**************************************************************************
- * Return a case-insensitive comparator of Strings. It is used to
- * create various TreeMaps where keys are strings.
- *************************************************************************/
- protected static Comparator getStringComparator() {
- return new Comparator() {
- public int compare (Object o1, Object o2) {
- return ((String)o1).compareToIgnoreCase ((String)o2);
- }
- };
- }
-
- // cache URL/URI so we only check once
- private static String CHECKED_URL = null;
- private static String CHECKED_URI = null;
-
- /**
- * Using this method to get a Central object will ensure that other parts of the org.biomoby.shared
- * class hierarchy that implicitly check the registry will use the same cache. Otherwise, methods
- * such as MobyNamespace.getNamespace() must be passed a Central object parameter as well.
- *
- * @return a CentralImpl using the default Central URI, and currently a class implementing a caching mechanism
- */
- public static CentralImpl getDefaultCentral() throws MobyException{
- return getDefaultCentral(null);
- }
-
- public static CentralImpl getDefaultCentral(Registry reg) throws MobyException{
- if(reg == null && defaultCentrals.containsKey("")){
- return defaultCentrals.get("");
- }
- else if(reg != null && defaultCentrals.containsKey(reg.getEndpoint())){
- return defaultCentrals.get(reg.getEndpoint());
- }
-
- String className = DEFAULT_CENTRAL_IMPL_CLASSNAME;
- ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
- URL resURL = classLoader.getResource("META-INF/"+CENTRAL_IMPL_RESOURCE_NAME);
- if(resURL != null){
- System.err.println("Loading "+resURL);
- try{
- LineNumberReader reader = new LineNumberReader(new InputStreamReader(resURL.openStream()));
- for(String line = reader.readLine(); line != null; line = reader.readLine()){
- if(!line.trim().startsWith("#")){
- className = line.trim();
- break;
- }
- }
- } catch(Exception e){
- logger.log(Level.WARNING,
- "Error reading " + resURL,
- e);
- }
- }
- try{
- System.err.println("Central class is "+className);
- Class clazz = Class.forName(className);
- if(reg == null){ // should use default nullary c-tor
- defaultCentrals.put("", (CentralImpl) clazz.newInstance());
- }
- else{ // should have (String endpoint, String namespace) c-tor
- for(Constructor ctor: clazz.getDeclaredConstructors()){
- Class[] params = ctor.getParameterTypes();
- if(params.length == 2 && params[0].getName().equals("java.lang.String") &&
- params[1].getName().equals("java.lang.String") ){
- defaultCentrals.put(reg.getEndpoint(),
- (CentralImpl) ctor.newInstance(reg.getEndpoint(), reg.getNamespace()));
- break;
- }
- }
- if(!defaultCentrals.containsKey(reg.getEndpoint())){
- logger.log(Level.WARNING,
- "Could not find required (String endpoint, String namespace)" +
- "constructor for class " + className);
- }
- }
- } catch(Exception e){
- logger.log(Level.WARNING,
- "Could not load class " + className,
- e);
- if(reg == null){
- defaultCentrals.put("", new CentralImpl()); //fallback to this class, no caching, etc.
- }
- else{
- defaultCentrals.put(reg.getEndpoint(),
- new CentralImpl(reg.getEndpoint(), reg.getNamespace()));
- }
- }
-
- return defaultCentrals.get(reg == null ? "" : reg.getEndpoint());
- }
-
- /**
- *
- * @return a String representing the Default mobycentral endpoint. If the
- * system property 'moby.check.default' exists and is set to true,
- * then the URL http://biomoby.org/mobycentral is queried and the
- * default central endpoint is returned, otherwise DEFAULT_ENDPOINT
- * is returned.
- */
- public static String getDefaultURL() {
- boolean check = false;
- try {
- check = Boolean.getBoolean("moby.check.default");
- } catch (Exception e) {
-
- }
-
- if (check) {
- // return the last checked url if we have done this before
- if (CHECKED_URL != null && CHECKED_URL.trim() != "") {
- return CHECKED_URL;
- }
-
- // create a HttpClient object
- HttpClient client = new HttpClient();
- // set up the Head method
- HeadMethod method = new HeadMethod("http://biomoby.org/mobycentral");
- // do not follow redirects or we will get a 411 error
- method.setFollowRedirects(false);
- // retry 3 times
- method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,new DefaultHttpMethodRetryHandler(3, false));
- // set the user agent ... should probably make this something more reasonable
- method.getParams().setParameter(HttpMethodParams.USER_AGENT,"jMoby/1.0");
- try {
- // Execute the method.
- int statusCode = client.executeMethod(method);
-
- if (statusCode != HttpStatus.SC_MOVED_PERMANENTLY) {
- System.err.println("Method failed: "
- + method.getStatusLine());
- } else {
- try {
- String location = method.getResponseHeader("location").getValue();
- CHECKED_URL = location;
- try {
- CHECKED_URI = "http://" + (new URL(CHECKED_URL).getAuthority()) + "/MOBY/Central";
- } catch (MalformedURLException murle ) {
- CHECKED_URI = DEFAULT_NAMESPACE;
- }
- return CHECKED_URL;
- } catch (NullPointerException npe) {
- return DEFAULT_ENDPOINT;
- }
- }
- } catch (HttpException e) {
- System.err.println("Fatal protocol violation: "
- + e.getMessage());
- e.printStackTrace();
- } catch (IOException e) {
- System.err.println("Fatal transport error: " + e.getMessage());
- e.printStackTrace();
- } finally {
- // Release the connection.
- method.releaseConnection();
- }
-
- } else {
- return DEFAULT_ENDPOINT;
- }
- return DEFAULT_ENDPOINT;
- }
-
- /**
- *
- * @return a String representing the default mobycentral uri. If the
- * system property 'moby.check.default' exists and is set to true,
- * then the URL http://biomoby.org/mobycentral is queried and the
- * default central namespace is returned, otherwise DEFAULT_NAMESPACE
- * is returned.
- */
- public static String getDefaultURI() {
- boolean check = false;
- try {
- check = Boolean.getBoolean("moby.check.default");
- } catch (Exception e) {
-
- }
- if (check) {
- if (CHECKED_URI != null && CHECKED_URI.trim() != "") {
- return CHECKED_URI;
- }
- // need to check ...
- getDefaultURL();
- return CHECKED_URI;
- } else {
- return DEFAULT_NAMESPACE;
- }
- }
- /**************************************************************************
- * Convert non-suitable characters in a XML string into their
- * entity references. <p>
- *
- * <em>Adapted from jDom.</em>
- *
- * @param str input to be converted
- * @return If there were any non-suitable characters, return a new
- * string with those characters escaped, otherwise return the
- * unmodified input string
- *
- *************************************************************************/
- public String escapeXML (String str) {
- StringBuffer buffer = null;
- char ch;
- String entity;
- for (int i = 0; i < str.length(); i++) {
- ch = str.charAt (i);
- switch (ch) {
- case '<' :
- entity = "<";
- break;
- case '>' :
- entity = ">";
- break;
- case '&' :
- entity = "&";
- break;
- default :
- entity = null;
- break;
- }
- if (buffer == null) {
- if (entity != null) {
- // An entity occurred, so we'll have to use StringBuffer
- // (allocate room for it plus a few more entities).
- buffer = new StringBuffer (str.length() + 20);
- // Copy previous skipped characters and fall through
- // to pickup current character
- buffer.append (str.substring (0, i));
- buffer.append (entity);
- }
- } else {
- if (entity == null) {
- buffer.append (ch);
- } else {
- buffer.append (entity);
- }
- }
- }
-
- // If there were any entities, return the escaped characters
- // that we put in the StringBuffer. Otherwise, just return
- // the unmodified input string.
- return (buffer == null) ? str : buffer.toString();
- }
-
- /*************************************************************************
- * Format an exception.
- *************************************************************************/
- public static String formatFault (AxisFault e, String endpoint, QName method) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- formatFault (e, new PrintStream (baos), endpoint, method);
- return baos.toString();
- }
-
- /*************************************************************************
- * Format an exception.
- *************************************************************************/
- public static void formatFault (AxisFault e, PrintStream out,
- String endpoint, QName method) {
-
- out.println ("===ERROR===");
- out.println ("Fault details:");
- // for some obvious errors I do not print all details (with a lenghty trace stack)
- String faultString = e.getFaultString();
- if ( (! faultString.startsWith ("java.net.ConnectException")) &&
- (faultString.indexOf ("Could not find class for the service named:") == -1)
- ) {
- org.w3c.dom.Element[] details = e.getFaultDetails();
- for (int i = 0; i < details.length; i++) {
- String s = details[i].toString().replaceAll ("<", "<");
- s = s.replaceAll (">", ">");
- out.println (s);
- }
- }
- out.println ("Fault string: " + faultString);
- out.println ("Fault code: " + e.getFaultCode());
- out.println ("Fault actor: " + e.getFaultActor());
- if (endpoint != null || method != null)
- out.println ("When calling:");
- if (endpoint != null)
- out.println ("\t" + endpoint);
- if (method != null)
- out.println ("\t" + method);
- out.println ("===========");
- }
-
-
-}
+// CentralImpl.java
+// A default client to the Moby Central service.
+//
+// senger at ebi.ac.uk
+// February 2003
+//
+
+package org.biomoby.client;
+
+import org.biomoby.registry.meta.Registry;
+import org.biomoby.shared.Central;
+import org.biomoby.shared.MobyData;
+import org.biomoby.shared.MobyDataType;
+import org.biomoby.shared.MobyException;
+import org.biomoby.shared.MobyNamespace;
+import org.biomoby.shared.MobyPrimaryDataSet;
+import org.biomoby.shared.MobyPrimaryDataSimple;
+import org.biomoby.shared.MobyRelationship;
+import org.biomoby.shared.MobySecondaryData;
+import org.biomoby.shared.MobyService;
+import org.biomoby.shared.MobyServiceType;
+import org.biomoby.shared.NoSuccessException;
+import org.biomoby.shared.PendingCurationException;
+import org.biomoby.shared.MobyResourceRef;
+import org.biomoby.shared.Utils;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.namespace.QName;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.axis.AxisFault;
+import org.apache.axis.client.Call;
+import org.apache.axis.client.Service;
+import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.HeadMethod;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.PrintStream;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Vector;
+import java.util.TreeMap;
+import java.util.Comparator;
+import java.util.zip.GZIPInputStream;
+import java.util.logging.*;
+
+/**
+ * A default implementation of the interface {@link org.biomoby.shared.Central Central} allowing access to a Moby
+ * registry.
+ * <p>
+ * This class is supposed to be used by all other clients that wish to communicate with the Moby Registry, but do not
+ * want to know about all XML details that are necessary for talking with the Moby Central directly. This is an example
+ * of a client program:
+ *
+ * <pre>
+ * import org.biomoby.shared.Central;
+ * import org.biomoby.shared.MobyException;
+ * import org.biomoby.client.CentralImpl;
+ * import java.util.Map;
+ * import java.util.Iterator;
+ *
+ * public class Test {
+ *
+ * public static void main( String[] args ) throws MobyException {
+ *
+ * Central worker = new CentralImpl();
+ * Map authorities = worker.getServiceNamesByAuthority();
+ *
+ * for ( Iterator it = authorities.entrySet().iterator(); it.hasNext(); ) {
+ * Map.Entry entry = ( Map.Entry ) it.next();
+ * System.out.println( entry.getKey() );
+ * String[] names = ( String[] ) entry.getValue();
+ * for ( int i = 0; i < names.length; i++ )
+ * System.out.println( "\t" + names[ i ] );
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * @author <A HREF="mailto:senger at ebi.ac.uk">Martin Senger</A>
+ * @version $Id$
+ */
+
+public class CentralImpl implements Central, SimpleCache {
+
+ private URL endpoint;
+ private String uri;
+ protected boolean debug = false;
+
+ /** Common central used to if getDefaultCentral() is called */
+ protected static Map< String, CentralImpl > defaultCentrals = new HashMap< String, CentralImpl >();
+
+ /** Default location (endpoint) of a Moby registry. */
+ public static final String DEFAULT_ENDPOINT = "http://moby.ucalgary.ca/moby/MOBY-Central.pl";
+
+ /** Default namespace used by the contacted Moby registry. */
+ public static final String DEFAULT_NAMESPACE = "http://moby.ucalgary.ca/MOBY/Central";
+
+ /**
+ * The META-INF resource file that will be checked to determine what default class should be instantiated in order
+ * to create a Central Implementation when getDefaultCentral() is called.
+ */
+ public static final String CENTRAL_IMPL_RESOURCE_NAME = "org.biomoby.shared.CentralDefaultImpl";
+ /** The class to use for getDefaultCentral if all else fails */
+ public static final String DEFAULT_CENTRAL_IMPL_CLASSNAME = "org.biomoby.client.CentralDigestCachedImpl";
+ private static Logger logger = Logger.getLogger( "org.biomoby.client.CentralImpl" );
+
+ /**
+ * Thread local that gives each thread its own DocumentBuilderFactory (since it is not thread-safe). Code taken from
+ * Apache's JaxpUtils.
+ */
+ public static ThreadLocal DOCUMENT_BUILDER_FACTORIES = new ThreadLocal() {
+ protected synchronized Object initialValue() {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware( true );
+ return dbf;
+ }
+ };
+
+ /*******************************************************************************************************************
+ * Default constructor. It connects to a default Moby registry (as defined in {@link #DEFAULT_ENDPOINT}) using a
+ * default namespace (as defined int {@link #DEFAULT_NAMESPACE}).
+ ******************************************************************************************************************/
+ public CentralImpl() throws MobyException {
+ this( DEFAULT_ENDPOINT, DEFAULT_NAMESPACE );
+ }
+
+ /*******************************************************************************************************************
+ * Constructor allowing to specify which Moby Registry to use.
+ *
+ * @throws MobyException
+ * if 'endpoint' is not a valid URL, or if no DOM parser is available
+ ******************************************************************************************************************/
+ public CentralImpl( String endpoint ) throws MobyException {
+ this( endpoint, DEFAULT_NAMESPACE );
+ }
+
+ /*******************************************************************************************************************
+ * Constructor allowing to specify which Moby Registry and what namespace to use. If any of the parameters is null,
+ * its default value is used instead.
+ * <p>
+ *
+ * @throws MobyException
+ * if 'endpoint' is not a valid URL, or if no DOM parser was found
+ ******************************************************************************************************************/
+ public CentralImpl( String endpoint, String namespace ) throws MobyException {
+
+ if ( endpoint == null || "".equals( endpoint.trim() ) ) endpoint = DEFAULT_ENDPOINT;
+ if ( namespace == null || "".equals( namespace.trim() ) ) namespace = DEFAULT_NAMESPACE;
+
+ try {
+ this.endpoint = new URL( endpoint );
+ }
+ catch ( MalformedURLException e ) {
+ throw new MobyException( "Bad URL: " + endpoint );
+ }
+ this.uri = namespace;
+
+ cache = new Hashtable< String, Object >();
+ useCache = true;
+ }
+
+ /*******************************************************************************************************************
+ * Loads a DOM Document from an InputStream. Uses thread-safe mechanism.
+ ******************************************************************************************************************/
+ public static Document loadDocument( InputStream input ) throws MobyException {
+ try {
+ DocumentBuilderFactory dbf = ( DocumentBuilderFactory ) DOCUMENT_BUILDER_FACTORIES.get();
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ return ( db.parse( input ) );
+ }
+ catch ( Exception e ) {
+ throw new MobyException( "Problem with reading XML input: " + e.toString(), e );
+ }
+ }
+
+ /*******************************************************************************************************************
+ * Call 'method' with 'parameters' and return its result.
+ ******************************************************************************************************************/
+ protected Object doCall( String method, Object[] parameters ) throws MobyException {
+
+ Call call = null;
+ try {
+ Service service = new Service();
+ call = ( Call ) service.createCall();
+ call.setTargetEndpointAddress( endpoint );
+ call.setTimeout( new Integer( 0 ) );
+
+ String user = System.getProperty( "user" );
+ String password = System.getProperty( "password" );
+
+ if ( user != null && password != null ) {
+ call.setProperty( Call.USERNAME_PROPERTY, user );
+ call.setProperty( Call.PASSWORD_PROPERTY, password );
+ }
+
+ call.setSOAPActionURI( uri + "#" + method );
+
+ if ( debug ) {
+ System.err.println( "METHOD CALL: " + method );
+ System.err.println( "------------" );
+ if ( parameters.length > 0 ) System.err.println( parameters[ 0 ] + "\n" );
+ System.err.println( "------------\n" );
+
+ Object result = call.invoke( uri, method, parameters );
+
+ System.err.println( "METHOD RETURN:" );
+ System.err.println( "------------" );
+ if ( result != null ) System.err.println( result + "\n" );
+ System.err.println( "------------\n" );
+
+ return resultToString( result );
+
+ }
+ else {
+ return resultToString( call.invoke( uri, method, parameters ) );
+ }
+
+ }
+ catch ( AxisFault e ) {
+ throw new MobyException( formatFault( e, endpoint.toString(), ( call == null ? null : call
+ .getOperationName() ) ), e );
+ // (endpoint.toString()+(call == null ? "" : call.getOperationName()), e);
+
+ }
+ catch ( Exception e ) {
+ throw new MobyException( e.toString(), e );
+ // e.printStackTrace();
+ }
+ }
+
+ /*******************************************************************************************************************
+ * Parse the given XML sniplet to find tag 'success'. If it has value '1' look further for tag 'id' and return it
+ * back (or return an empty string if ID is not there). Otherwise raise an exception with the 'culprit' and with the
+ * message from the tag 'message'.
+ * <p>
+ *
+ * The return value is a two-element long array. The first element is the ID (given by BioMobe registry), and the
+ * second element is RDF corresponding with the registered object (BioMoby returns this only for service instances,
+ * so for other objects this will be null).
+ * <p>
+ *
+ * This is how the XML is supposed to look: <MOBYRegistration> <success> <!-- 1 | 0 | -1 --> </success> <id> <!--
+ * some id number for your registration --> </id> <message> <![CDATA[message here]]> </message> <RDF> <!-- RDF of
+ * your service instance here (if applicable) --> </RDF> </MOBYRegistration>
+ *
+ * Success takes the value "1" to indicate success, "0" to indicate failure, and "-1" to indicate "Pending
+ * Curation".
+ ******************************************************************************************************************/
+ protected String[] checkRegistration( String xml, Object culprit ) throws MobyException, NoSuccessException,
+ PendingCurationException {
+
+ String id = "", success = "0", message = "", rdf = "";
+
+ // parse returned XML
+ Document document = loadDocument( new ByteArrayInputStream( xml.getBytes() ) );
+ Element root = document.getDocumentElement();
+
+ NodeList children = root.getChildNodes();
+ for ( int i = 0; i < children.getLength(); i++ ) {
+ if ( children.item( i ).getNodeType() != Node.ELEMENT_NODE ) continue;
+ Element elem = ( Element ) children.item( i );
+ if ( elem.getNodeName().equals( "id" ) ) {
+ if ( elem.getFirstChild() != null ) id = elem.getFirstChild().getNodeValue();
+ }
+ else if ( elem.getNodeName().equals( "success" ) ) {
+ if ( elem.getFirstChild() != null ) success = elem.getFirstChild().getNodeValue();
+ }
+ else if ( elem.getNodeName().equals( "message" ) ) {
+ if ( elem.getFirstChild() != null ) message = elem.getFirstChild().getNodeValue();
+ }
+ else if ( elem.getNodeName().equals( "RDF" ) ) {
+ if ( elem.getFirstChild() != null ) rdf = elem.getFirstChild().getNodeValue();
+ }
+ }
+
+ if ( success.equals( "0" ) )
+ throw new NoSuccessException( message, culprit );
+ else if ( success.equals( "-1" ) ) throw new PendingCurationException();
+ return new String[] { id, rdf };
+ }
+
+ /*******************************************************************************************************************
+ * Return a piece of XML created from the definitions representing input data types and their usage in the given
+ * service. Only data considered primary are included. Note that the main job of converting to XML is done by
+ * instances of MobyPrimaryData.
+ *
+ * The returned XML looks like this: <Input> <!-- zero or more Primary (Simple and/or Complex) articles --> </Input>
+ ******************************************************************************************************************/
+ protected String buildPrimaryInputTag( MobyService service ) {
+ StringBuffer buf = new StringBuffer();
+ MobyData[] primaryInputs = service.getPrimaryInputs();
+ buf.append( "<Input>\n" );
+ for ( int i = 0; i < primaryInputs.length; i++ )
+ buf.append( primaryInputs[ i ].toXML() );
+ buf.append( "</Input>\n" );
+ return new String( buf );
+ }
+
+ /*******************************************************************************************************************
+ * Return a piece of XML created from the definitions representing input data types and their usage in the given
+ * service. Only data considered secondary are included. Note that the main job of converting to XML is done by
+ * instances of MobySecondaryData.
+ *
+ * The returned XML looks like this: <secondaryArticles> <!-- zero or more INPUT Secondary articles -->
+ * </secondaryArticles>
+ ******************************************************************************************************************/
+ protected String buildSecondaryInputTag( MobyService service ) {
+ StringBuffer buf = new StringBuffer();
+ MobyData[] secInputs = service.getSecondaryInputs();
+ buf.append( "<secondaryArticles>\n" );
+ for ( int i = 0; i < secInputs.length; i++ ) {
+ buf.append( secInputs[ i ].toXML() );
+ }
+ buf.append( "</secondaryArticles>\n" );
+ return new String( buf );
+ }
+
+ /*******************************************************************************************************************
+ * Return a piece of XML created from the definitions representing output data types and their usage in the given
+ * service. Only data considered primary are included. Note that the main job of converting to XML is done by
+ * instances of MobyPrimaryData.
+ *
+ * The returned XML looks like this: <Output> <!-- zero or more Primary (Simple and/or Complex) articles -->
+ * </Output>
+ *
+ ******************************************************************************************************************/
+ protected String buildOutputTag( MobyService service ) {
+ StringBuffer buf = new StringBuffer();
+ MobyData[] primaryOutputs = service.getPrimaryOutputs();
+ buf.append( "<Output>\n" );
+ for ( int i = 0; i < primaryOutputs.length; i++ )
+ buf.append( primaryOutputs[ i ].toXML() );
+ buf.append( "</Output>\n" );
+ return new String( buf );
+ }
+
+ /*******************************************************************************************************************
+ * Return a piece of XML represented a query object (an object used to find a service).
+ *
+ * The returned XML looks like this:
+ *
+ * <inputObjects> <Input> <!-- one or more Simple or Complex Primary articles --> </Input> </inputObjects>
+ * <outputObjects> <Output> <!-- one or more Simple or Complex Primary articles --> </Output> </outputObjects>
+ * <serviceType>ServiceTypeTerm</serviceType> <serviceName>ServiceName</serviceName> <Category>moby</Category>
+ * <authURI>http://desired.service.provider</authURI>; <expandObjects>1|0</expandObjects> <expandServices>1|0</expandServices>
+ * <authoritative>1|0</authoritative> <keywords> <keyword>something</keyword> .... .... </keywords>
+ ******************************************************************************************************************/
+ protected String buildQueryObject( MobyService service, String[] keywords, boolean expandObjects,
+ boolean expandServices, boolean authoritative ) {
+ if ( service == null ) {
+ service = new MobyService( "dummy" );
+ service.setCategory( "" );
+ }
+ StringBuffer buf = new StringBuffer();
+
+ buf.append( "<inputObjects>\n<Input>\n" );
+ MobyData[] pi = service.getPrimaryInputs();
+ if ( pi.length > 0 ) {
+ for ( int i = 0; i < pi.length; i++ )
+ buf.append( pi[ i ].toXML() );
+ }
+ buf.append( "</Input>\n</inputObjects>\n" );
+
+ buf.append( "<outputObjects>\n<Output>\n" );
+ MobyData[] po = service.getPrimaryOutputs();
+ if ( po.length > 0 ) {
+ for ( int i = 0; i < po.length; i++ )
+ buf.append( po[ i ].toXML() );
+ }
+ buf.append( "</Output>\n</outputObjects>\n" );
+
+ buf.append( "<serviceType>" + service.getType() + "</serviceType>\n" );
+
+ String name = service.getName();
+ if ( !name.equals( "" ) && !name.equals( "dummy" ) && !name.equals( MobyService.DUMMY_NAME ) )
+ buf.append( "<serviceName>" + service.getName() + "</serviceName>\n" );
+
+ String sigURL = service.getSignatureURL();
+ if ( !sigURL.equals( "" ) ) buf.append( "<signatureURL>" + sigURL + "</signatureURL>\n" );
+
+ buf.append( "<Category>" + service.getCategory() + "</Category>\n" );
+ buf.append( "<authURI>" + service.getAuthority() + "</authURI>\n" );
+
+ buf.append( "<expandObjects>" );
+ buf.append( expandObjects ? "1" : "0" );
+ buf.append( "</expandObjects>\n" );
+
+ buf.append( "<expandServices>" );
+ buf.append( expandServices ? "1" : "0" );
+ buf.append( "</expandServices>\n" );
+
+ buf.append( "<authoritative>" );
+ buf.append( authoritative ? "1" : "0" );
+ buf.append( "</authoritative>\n" );
+
+ buf.append( "<keywords>\n" );
+ if ( keywords != null && keywords.length > 0 ) {
+ for ( int i = 0; i < keywords.length; i++ ) {
+ buf.append( "<keyword>" );
+ buf.append( keywords[ i ] );
+ buf.append( "</keyword>\n" );
+ }
+ }
+ buf.append( "</keywords>\n" );
+
+ return new String( buf );
+ }
+
+ /*******************************************************************************************************************
+ * Extract one or more MobyService objects from the given XML piece. The XML should look like this:
+ *
+ * <pre>
+ * <Services>
+ * <Service authURI="authority.URI.here" lsid="..." serviceName="MyService">
+ * <serviceType>Service_Ontology_Term</serviceType>
+ * <Category>moby</Category> <!-- or 'cgi' or 'soap' -->
+ * <contactEmail>your at email.addy.here</contactEmail>
+ * <signatureURL>http://service.RDF.here</signatureURL>
+ * <URL>http://service.endpoint.here/scriptname</URL>
+ * <authoritative>1</authoritative>
+ * <Input>
+ * <!-- one or more Simple and/or Complex Primary articles -->
+ * </Input>
+ * <Output>
+ * <!-- one or more Simple and/or Complex Primary articles -->
+ * </Output>
+ * <secondaryArticles>
+ * <!-- one or more Secondary articles -->
+ * </secondaryArticles>
+ * <Description><![CDATA[free text description here]]></Description>
+ * </Service>
+ * ... <!-- one or more Service blocks may be returned -->
+ * ...
+ * ...
+ * </Services>
+ * </pre>
+ *
+ * @throws MobyException
+ * if the XML document is invalid
+ ******************************************************************************************************************/
+ public MobyService[] extractServices( String xml ) throws MobyException {
+
+ Document document = loadDocument( new ByteArrayInputStream( xml.getBytes() ) );
+ NodeList list = document.getElementsByTagName( "Service" );
+ MobyService[] results = new MobyService[ list.getLength() ];
+ for ( int i = 0; i < list.getLength(); i++ ) {
+ Element elem = ( Element ) list.item( i );
+ MobyService service = new MobyService( elem.getAttribute( "serviceName" ) );
+ service.setAuthority( elem.getAttribute( "authURI" ) );
+ service.setLSID( elem.getAttribute( "lsid" ) );
+ NodeList children = elem.getChildNodes();
+ for ( int j = 0; j < children.getLength(); j++ ) {
+ String nodeName = children.item( j ).getNodeName();
+ if ( nodeName.equals( "Description" ) ) {
+ service.setDescription( getFirstValue( children.item( j ) ) );
+ }
+ else if ( nodeName.equals( "Category" ) ) {
+ service.setCategory( getFirstValue( children.item( j ) ) );
+ }
+ else if ( nodeName.equals( "URL" ) ) {
+ service.setURL( getFirstValue( children.item( j ) ) );
+ }
+ else if ( nodeName.equals( "signatureURL" ) ) {
+ service.setSignatureURL( getFirstValue( children.item( j ) ) );
+ }
+ else if ( nodeName.equals( "contactEmail" ) ) {
+ service.setEmailContact( getFirstValue( children.item( j ) ) );
+ }
+ else if ( nodeName.equals( "serviceType" ) ) {
+ service.setType( getFirstValue( children.item( j ) ) );
+ MobyServiceType mst = new MobyServiceType( service.getType() );
+ NamedNodeMap map = ( children.item( j ).getAttributes() );
+ if ( map != null ) {
+ Node node = map.getNamedItemNS( children.item( j ).getNamespaceURI(), "lsid" );
+ if ( node != null ) mst.setLSID( node.getNodeValue() );
+ }
+ service.setServiceType( mst );
+ }
+ else if ( nodeName.equals( "authoritative" ) ) {
+ String authoritative = getFirstValue( children.item( j ) );
+ service.setAuthoritative( authoritative.equals( "1" ) ? true : false );
+ }
+ else if ( nodeName.equals( "Input" ) ) {
+ // <Input>
+ // <!-- one or more Simple and/or Complex Primary articles -->
+ // <Simple articleName="NameOfArticle">
+ // ...
+ // </Simple>
+ // <Collection articleName="NameOfArticle">
+ // <Simple>......</Simple>
+ // <Simple>......</Simple>
+ // </Collection>
+ // </Input>
+ NodeList inputs = children.item( j ).getChildNodes();
+ for ( int k = 0; k < inputs.getLength(); k++ ) {
+ if ( inputs.item( k ).getNodeName().equals( "Simple" ) ) {
+ MobyPrimaryDataSimple data = new MobyPrimaryDataSimple( ( Element ) inputs.item( k ) );
+ service.addInput( data );
+ }
+ else if ( inputs.item( k ).getNodeName().equals( "Collection" ) ) {
+ MobyPrimaryDataSet data = new MobyPrimaryDataSet( ( Element ) inputs.item( k ) );
+ service.addInput( data );
+ }
+ }
+ }
+ else if ( nodeName.equals( "Output" ) ) {
+ // <Output>
+ // <!-- one or more Simple and/or Complex Primary articles -->
+ // </Output>
+ NodeList inputs = children.item( j ).getChildNodes();
+ for ( int k = 0; k < inputs.getLength(); k++ ) {
+ if ( inputs.item( k ).getNodeName().equals( "Simple" ) ) {
+ MobyPrimaryDataSimple data = new MobyPrimaryDataSimple( ( Element ) inputs.item( k ) );
+ service.addOutput( data );
+ }
+ else if ( inputs.item( k ).getNodeName().equals( "Collection" ) ) {
+ MobyPrimaryDataSet data = new MobyPrimaryDataSet( ( Element ) inputs.item( k ) );
+ service.addOutput( data );
+ }
+ }
+
+ }
+ else if ( nodeName.equals( "secondaryArticles" ) ) {
+ // <Parameter articleName="NameOfArticle">
+ // ...
+ // </Parameter>
+ NodeList parameters = children.item( j ).getChildNodes();
+ for ( int k = 0; k < parameters.getLength(); k++ ) {
+ if ( parameters.item( k ).getNodeName().equals( "Parameter" ) ) {
+ MobySecondaryData data = new MobySecondaryData( ( Element ) parameters.item( k ) );
+ service.addInput( data );
+ }
+ }
+ }
+ }
+ results[ i ] = service;
+ }
+ return results;
+ }
+
+ // protect against null values
+ protected String getFirstValue( Node child ) {
+ Node node = child.getFirstChild();
+ if ( node == null ) return "";
+ String value = node.getNodeValue();
+ if ( value == null ) return "";
+ return value;
+ }
+
+ protected String getFirstValue( NodeList children ) {
+ if ( children.item( 0 ) != null && children.item( 0 ).hasChildNodes() ) {
+ children.item( 0 ).normalize();
+ return getFirstValue( children.item( 0 ) );
+ }
+ return "";
+ }
+
+ /*******************************************************************************************************************
+ *
+ * Implementing SimpleCache interface.
+ *
+ * Why to have an interface for such trivial thing? Well, because I needed to overwrite the caching mechanism in the
+ * subclasses so I needed to have all caching functions as separate methods - that's why I have collect them in an
+ * interface.
+ *
+ ******************************************************************************************************************/
+ private Hashtable< String, Object > cache; // this is the cache itself
+ private boolean useCache; // this signal that we are actually caching things
+
+ // not used here
+ public String createId( String rootName, String semanticType, String syntaxType, long lastModified, Properties props ) {
+ return ""; // not used here
+ }
+
+ // check existence of a cached object
+ public boolean existsInCache( String id ) {
+ synchronized ( cache ) {
+ if ( useCache )
+ return cache.containsKey( id );
+ else
+ return false;
+ }
+ }
+
+ // retrieve from cache
+ public Object getContents( String id ) {
+ synchronized ( cache ) {
+ if ( useCache )
+ return cache.get( id );
+ else
+ return null;
+ }
+ }
+
+ // cache an object
+ public void setContents( String id, java.lang.Object data ) {
+ synchronized ( cache ) {
+ if ( useCache ) cache.put( id, data );
+ }
+ }
+
+ // in this implementation, it clears the whole cache, regardless
+ // what 'id' is passed
+ public void removeFromCache( String id ) {
+ cache.clear();
+ }
+
+ /*******************************************************************************************************************
+ *
+ * And the other methods related to caching (but not part of the SimpleCache interface).
+ *
+ ******************************************************************************************************************/
+
+ /*******************************************************************************************************************
+ * By default, caching is enabled to reduce network traffic. Setting this to false will clear the cache, and not
+ * cache any further calls unless it is set to true again.
+ * <p>
+ *
+ * @param shouldCache
+ * whether retrieveXXX call results should be cached in case they are called again (i.e. don't request
+ * MobyCentral every time)
+ ******************************************************************************************************************/
+ public void setCacheMode( boolean shouldCache ) {
+ useCache = shouldCache;
+ if ( !useCache ) removeFromCache( null );
+ }
+
+ /*******************************************************************************************************************
+ * Find if caching is currently enabled.
+ *
+ * @return true if caching is enabled
+ ******************************************************************************************************************/
+ public boolean getCacheMode() {
+ return useCache;
+ }
+
+ /*******************************************************************************************************************
+ * Parses and imports the following XML.
+ *
+ * <pre>
+ * <serviceNames>
+ * <serviceName name="serviceName" authURI='authority.info.here'/>
+ * ...
+ * ...
+ * </serviceNames>
+ * </pre>
+ *
+ * @deprecated Replaced by {@link #getServiceNamesByAuthority}. The reason is that this method returns a random
+ * result if there are more services with the same name but belonging to different authorities.
+ * <p>
+ *
+ ******************************************************************************************************************/
+ public Map< String, String > getServiceNames() throws MobyException {
+
+ String result = ( String ) doCall( "retrieveServiceNames", new Object[] {} );
+ // parse returned XML
+ Map< String, String > results = new TreeMap< String, String >( getStringComparator() );
+ Document document = loadDocument( new ByteArrayInputStream( result.getBytes() ) );
+ NodeList list = document.getElementsByTagName( "serviceName" );
+ for ( int i = 0; i < list.getLength(); i++ ) {
+ Element elem = ( Element ) list.item( i );
+ results.put( elem.getAttribute( "name" ), elem.getAttribute( "authURI" ) );
+ }
+
+ return results;
+ }
+
+ /*******************************************************************************************************************
+ * Parses and imports the following XML.
+ *
+ * <pre>
+ * <serviceNames>
+ * <serviceName name="serviceName" lsid="..." authURI='authority.info.here'/>
+ * ...
+ * ...
+ * </serviceNames>
+ * </pre>
+ *
+ * @return a Map which has authorities as keys, and String arrays with service names as a values.
+ ******************************************************************************************************************/
+ public Map getServiceNamesByAuthority() throws MobyException {
+ String result = getServiceNamesByAuthorityAsXML();
+ return createServicesByAuthorityFromXML( result, true );
+ }
+
+ /*******************************************************************************************************************
+ * Similar to {@link #getServiceNamesByAuthority} but the resulting Map contains slightly more.
+ * <p>
+ *
+ * @return a Map which has authorities as keys, and arrays of MobyServices as a values. Each MobyService is filled
+ * with its name, authority and LSID.
+ ******************************************************************************************************************/
+ public Map getServicesByAuthority() throws MobyException {
+ String result = getServiceNamesByAuthorityAsXML();
+ return createServicesByAuthorityFromXML( result, false );
+ }
+
+ //
+ protected String getServiceNamesByAuthorityAsXML() throws MobyException {
+ return ( String ) doCall( "retrieveServiceNames", new Object[] {} );
+ }
+
+ // if onlyNames == true
+ // Map: authority name -> String[]
+ // (filled with service namea)
+ // else
+ // Map: authority name -> MobyService[]
+ // (filled with service name, authority and lsid)
+ protected Map createServicesByAuthorityFromXML( String result, boolean onlyNames ) throws MobyException {
+
+ // parse returned XML
+ Map results = new TreeMap( getStringComparator() );
+ Document document = loadDocument( new ByteArrayInputStream( result.getBytes() ) );
+ NodeList list = document.getElementsByTagName( "serviceName" );
+ for ( int i = 0; i < list.getLength(); i++ ) {
+ Element elem = ( Element ) list.item( i );
+ String name = elem.getAttribute( "name" );
+ String auth = elem.getAttribute( "authURI" );
+ Vector< Object > v = ( results.containsKey( auth ) ? ( Vector ) results.get( auth )
+ : new Vector< Object >() );
+ if ( onlyNames ) {
+ v.addElement( name );
+ }
+ else {
+ MobyService ms = new MobyService( name );
+ ms.setAuthority( auth );
+ ms.setLSID( elem.getAttribute( "lsid" ) );
+ v.addElement( ms );
+ }
+ results.put( auth, v );
+ }
+
+ // change values of type Vector to MobyService[] or String[]
+ for ( Iterator it = results.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry entry = ( Map.Entry ) it.next();
+ Vector v = ( Vector ) entry.getValue();
+ if ( onlyNames ) {
+ String[] sNames = new String[ v.size() ];
+ v.copyInto( sNames );
+ entry.setValue( sNames );
+ }
+ else {
+ MobyService[] mss = new MobyService[ v.size() ];
+ v.copyInto( mss );
+ entry.setValue( mss );
+ }
+ }
+
+ return results;
+ }
+
+ /*******************************************************************************************************************
+ * Parses and imports the following XML.
+ *
+ * <pre>
+ * <serviceProviders>
+ * <serviceProvider name="authority.URI.here"/>
+ * ...
+ * ...
+ * </serviceProviders>
+ * </pre>
+ ******************************************************************************************************************/
+ public String[] getProviders() throws MobyException {
+
+ String cacheId = "retrieveServiceProviders";
+ String[] cachedResults = ( String[] ) getContents( cacheId );
+ if ( cachedResults != null ) return cachedResults;
+
+ String result = ( String ) doCall( "retrieveServiceProviders", new Object[] {} );
+
+ // parse returned XML
+ Document document = loadDocument( new ByteArrayInputStream( result.getBytes() ) );
+ NodeList list = document.getElementsByTagName( "serviceProvider" );
+ String[] results = new String[ list.getLength() ];
+ for ( int i = 0; i < list.getLength(); i++ )
+ results[ i ] = ( ( Element ) list.item( i ) ).getAttribute( "name" );
+
+ // Add this data to the cache in case we get called again
+ setContents( cacheId, results );
+
+ return results;
+ }
+
+ /*******************************************************************************************************************
+ * Parses and imports the following XML.
+ *
+ * <pre>
+ * <serviceTypes>
+ * <serviceType name="serviceName" lsid="...">
+ * <Description><![CDATA[free text description here]]></Description>
+ * <contactEmail>...</contactEmail>
+ * <authURI>...</authURI>
+ * </serviceType>
+ * ...
+ * ...
+ * </serviceTypes>
+ * </pre>
+ ******************************************************************************************************************/
+ public Map getServiceTypes() throws MobyException {
+ String result = getServiceTypesAsXML();
+ Map results = new TreeMap( getStringComparator() );
+ MobyServiceType[] types = createServiceTypesFromXML( result );
+ for ( int i = 0; i < types.length; i++ ) {
+ results.put( types[ i ].getName(), types[ i ].getDescription() );
+ }
+ return results;
+ }
+
+ //
+ protected String getServiceTypesAsXML() throws MobyException {
+ return ( String ) doCall( "retrieveServiceTypes", new Object[] {} );
+ }
+
+ // but be aware that the created MobyServiceTypes are not complete
+ // - they do not have the relationship information; that's why
+ // this method is not public; the full service types are available
+ // from CentralDigest implementations
+ protected MobyServiceType[] createServiceTypesFromXML( String result ) throws MobyException {
+
+ // parse returned XML
+ Document document = loadDocument( new ByteArrayInputStream( result.getBytes() ) );
+ NodeList list = document.getElementsByTagName( "serviceType" );
+ if ( list == null || list.getLength() == 0 ) return new MobyServiceType[] {};
+ MobyServiceType[] results = new MobyServiceType[ list.getLength() ];
+ for ( int i = 0; i < list.getLength(); i++ ) {
+ Element elem = ( Element ) list.item( i );
+ MobyServiceType st = new MobyServiceType( elem.getAttribute( "name" ) );
+ st.setLSID( elem.getAttribute( "lsid" ) );
+ st.setDescription( getFirstValue( elem.getElementsByTagName( "Description" ) ) );
+ st.setEmailContact( getFirstValue( elem.getElementsByTagName( "contactEmail" ) ) );
+ st.setAuthority( getFirstValue( elem.getElementsByTagName( "authURI" ) ) );
+ results[ i ] = st;
+ }
+ java.util.Arrays.sort( results );
+ return results;
+ }
+
+ /*******************************************************************************************************************
+ * Parses and imports the following XML.
+ *
+ * <pre>
+ * <Namespaces>
+ * <Namespace name="namespace" lsid="...">
+ * <Description><![CDATA[free text description here]]></Description>
+ * <contactEmail>...</contactEmail>
+ * <authURI>...</authURI>
+ * </Namespace>
+ * ...
+ * ...
+ * </Namespaces>
+ * </pre>
+ ******************************************************************************************************************/
+ public MobyNamespace[] getFullNamespaces() throws MobyException {
+
+ String result = getNamespacesAsXML();
+ return createNamespacesFromXML( result );
+ }
+
+ //
+ protected String getNamespacesAsXML() throws MobyException {
+ return ( String ) doCall( "retrieveNamespaces", new Object[] {} );
+ }
+
+ //
+ protected MobyNamespace[] createNamespacesFromXML( String result ) throws MobyException {
+
+ // parse returned XML
+ Document document = loadDocument( new ByteArrayInputStream( result.getBytes() ) );
+ NodeList list = document.getDocumentElement().getElementsByTagName( "Namespace" );
+ if ( list == null || list.getLength() == 0 ) {
+ return new MobyNamespace[] {};
+ }
+ MobyNamespace[] results = new MobyNamespace[ list.getLength() ];
+ for ( int i = 0; i < list.getLength(); i++ ) {
+ Element elem = ( Element ) list.item( i );
+ MobyNamespace nm = new MobyNamespace( elem.getAttribute( "name" ) );
+ nm.setLSID( elem.getAttribute( "lsid" ) );
+ nm.setDescription( getFirstValue( elem.getElementsByTagName( "Description" ) ) );
+ nm.setEmailContact( getFirstValue( elem.getElementsByTagName( "contactEmail" ) ) );
+ nm.setAuthority( getFirstValue( elem.getElementsByTagName( "authURI" ) ) );
+ results[ i ] = nm;
+ }
+
+ java.util.Arrays.sort( results );
+ return results;
+ }
+
+ /*******************************************************************************************************************
+ *
+ * @deprecated Replaced by {@link #getFullNamespaces} that gives more information for the same price.
+ * <p>
+ ******************************************************************************************************************/
+ public Map getNamespaces() throws MobyException {
+
+ Map results = new TreeMap( getStringComparator() );
+ MobyNamespace[] namespaces = getFullNamespaces();
+ for ( int i = 0; i < namespaces.length; i++ ) {
+ results.put( namespaces[ i ].getName(), namespaces[ i ].getDescription() );
+ }
+ return results;
+ }
+
+ /*******************************************************************************************************************
+ * Parses and imports the following XML.
+ *
+ * <pre>
+ * <objectNames>
+ * <Object name="objectName" lsid="...">
+ * <Description><![CDATA[free text description here]]></Description>
+ * </Object>
+ * ...
+ * ...
+ * </objectNames>
+ * </pre>
+ ******************************************************************************************************************/
+ public Map getDataTypeNames() throws MobyException {
+ String result = getDataTypeNamesAsXML();
+ return createDataTypeNamesFromXML( result, true );
+ }
+
+ //
+ protected String getDataTypeNamesAsXML() throws MobyException {
+ return ( String ) doCall( "retrieveObjectNames", new Object[] {} );
+ }
+
+ // if onlyNames == true
+ // Map: data type name -> description (String)
+ // else
+ // Map: data type name -> MobyDataType[]
+ // (filled with name, description, and lsid)
+ protected Map createDataTypeNamesFromXML( String result, boolean onlyNames ) throws MobyException {
+
+ // parse returned XML
+ Map results = new TreeMap( getStringComparator() );
+ Document document = loadDocument( new ByteArrayInputStream( result.getBytes() ) );
+ NodeList list = document.getElementsByTagName( "Object" );
+ for ( int i = 0; i < list.getLength(); i++ ) {
+ Element elem = ( Element ) list.item( i );
+ String name = elem.getAttribute( "name" );
+ if ( name == null ) continue; // ignore no-named data types
+ String desc = "";
+ NodeList children = elem.getChildNodes();
+ for ( int j = 0; j < children.getLength(); j++ ) {
+ if ( children.item( j ).getNodeName().equals( "Description" ) ) {
+ desc = getFirstValue( children.item( j ) );
+ break;
+ }
+ }
+ if ( onlyNames ) {
+ results.put( name, desc );
+ }
+ else {
+ MobyDataType dt = new MobyDataType( name );
+ dt.setDescription( desc );
+ dt.setLSID( elem.getAttribute( "lsid" ) );
+ results.put( name, dt );
+ }
+ }
+ return results;
+ }
+
+ /*******************************************************************************************************************
+ * Parses and imports the following XML. An example:
+ *
+ * <pre>
+ * <retrieveObjectDefinition>
+ * <objectType lsid="...">go_term</objectType>
+ * <Description><![CDATA[A very lightweight object holding a GO term name and its definition]]></Description>
+ * <authURI>http://www.illuminae.com</authURI>
+ * <contactEmail>markw at illuminae.com</contactEmail>
+ * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:isa'>
+ * <objectType articleName=''>urn:lsid:biomoby.org:objectclass:object</objectType>
+ * </Relationship>
+ * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:hasa'>
+ * <objectType articleName='Term'>urn:lsid:biomoby.org:objectclass:string</objectType>
+ * <objectType articleName='Definition'>urn:lsid:biomoby.org:objectclass:string</objectType>
+ * </Relationship>
+ * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:has'>
+ * <objectType articleName='Problems'>urn:lsid:biomoby.org:objectclass:string</objectType>
+ * <objectType articleName='Issues'>urn:lsid:biomoby.org:objectclass:string</objectType>
+ * </Relationship>
+ * </retrieveObjectDefinition>
+ * </pre>
+ ******************************************************************************************************************/
+ public MobyDataType getDataType( String dataTypeName ) throws MobyException, NoSuccessException {
+
+ String result = getDataTypeAsXML( dataTypeName );
+ return createDataTypeFromXML( result, dataTypeName );
+ }
+
+ public MobyDataType[] getDataTypes() throws MobyException, NoSuccessException {
+ Map< String, String > datatypeMap = getDataTypeNames();
+ MobyDataType[] datatypes = new MobyDataType[ datatypeMap.size() ];
+ int i = 0;
+ for ( String dataTypeName : datatypeMap.keySet() ) {
+ datatypes[ i++ ] = getDataType( dataTypeName );
+ }
+ return datatypes;
+ }
+
+ protected String getDataTypeAsXML( String dataTypeName ) throws MobyException, NoSuccessException {
+
+ return ( String ) doCall( "retrieveObjectDefinition", new Object[] { "<retrieveObjectDefinition>"
+ + "<objectType>" + dataTypeName + "</objectType>" + "</retrieveObjectDefinition>" } );
+ }
+
+ protected MobyDataType createDataTypeFromXML( String xmlSource, String dataTypeName ) throws MobyException,
+ NoSuccessException {
+
+ // parse returned XML
+ Document document = loadDocument( new ByteArrayInputStream( xmlSource.getBytes() ) );
+ NodeList list = document.getElementsByTagName( "retrieveObjectDefinition" );
+ if ( list == null || list.getLength() == 0 )
+ throw new NoSuccessException( "Data Type name was not found.", dataTypeName );
+ MobyDataType data = null;
+ Element elem = ( Element ) list.item( 0 );
+ NodeList children = elem.getChildNodes();
+
+ // first find the "real" (LSID-ized) data type name
+ for ( int j = 0; j < children.getLength(); j++ ) {
+ String nodeName = children.item( j ).getNodeName();
+ if ( nodeName.equals( "objectType" ) ) {
+ data = new MobyDataType( getFirstValue( children.item( j ) ) );
+ data.setLSID( ( ( Element ) children.item( j ) ).getAttribute( "lsid" ) );
+ break;
+ }
+ }
+
+ // if not found (unprobable) use the name given by the caller
+ if ( data == null ) data = new MobyDataType( dataTypeName );
+
+ // now fill the data type object with the rest of attributes
+ for ( int j = 0; j < children.getLength(); j++ ) {
+ String nodeName = children.item( j ).getNodeName();
+ if ( nodeName.equals( "Description" ) ) {
+ data.setDescription( getFirstValue( children.item( j ) ) );
+ }
+ else if ( nodeName.equals( "authURI" ) ) {
+ data.setAuthority( getFirstValue( children.item( j ) ) );
+ }
+ else if ( nodeName.equals( "contactEmail" ) ) {
+ data.setEmailContact( getFirstValue( children.item( j ) ) );
+ }
+ else if ( nodeName.equals( "Relationship" ) ) {
+ String relationshipType = ( ( Element ) children.item( j ) ).getAttribute( "relationshipType" );
+ if ( relationshipType.endsWith( "isa" ) ) {
+
+ NodeList parents = children.item( j ).getChildNodes();
+ for ( int k = 0; k < parents.getLength(); k++ ) {
+ if ( parents.item( k ).getNodeName().equals( "objectType" ) ) {
+ data.addParentName( getFirstValue( parents.item( k ) ) );
+ }
+ }
+ }
+ else if ( relationshipType.endsWith( "hasa" ) ) {
+
+ NodeList belows = children.item( j ).getChildNodes();
+ for ( int k = 0; k < belows.getLength(); k++ ) {
+ if ( belows.item( k ).getNodeName().equals( "objectType" ) ) {
+ data.addChild( ( ( Element ) belows.item( k ) ).getAttribute( "articleName" ),
+ getFirstValue( belows.item( k ) ), Central.iHASA );
+ }
+ }
+ }
+ else if ( relationshipType.endsWith( "has" ) ) {
+
+ NodeList belows = children.item( j ).getChildNodes();
+ for ( int k = 0; k < belows.getLength(); k++ ) {
+ if ( belows.item( k ).getNodeName().equals( "objectType" ) ) {
+ data.addChild( ( ( Element ) belows.item( k ) ).getAttribute( "articleName" ), belows.item(
+ k ).getFirstChild().getNodeValue(), Central.iHAS );
+ }
+ }
+ }
+ }
+ }
+ return data;
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public String getServiceWSDL( String serviceName ) throws MobyException, NoSuccessException {
+
+ Map names = getServiceNames();
+
+ for ( Iterator it = names.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry entry = ( Map.Entry ) it.next();
+ if ( ( ( String ) entry.getKey() ).equals( serviceName ) )
+ return getServiceWSDL( serviceName, ( String ) entry.getValue() );
+ }
+
+ throw new NoSuccessException( "Service not found.", serviceName );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public String getServiceWSDL( String serviceName, String authority ) throws MobyException, NoSuccessException {
+
+ String cacheId = "getServiceWSDL" + serviceName + ":" + authority;
+ String cachedResults = ( String ) getContents( cacheId );
+ if ( cachedResults != null ) return cachedResults;
+
+ String result = ( String ) doCall( "retrieveService",
+ new Object[] { "<retrieveService>" + "<Service authURI=\"" + authority + "\" serviceName=\""
+ + serviceName + "\"/>" + "</retrieveService>" } );
+
+ // parse returned XML
+ Document document = loadDocument( new ByteArrayInputStream( result.getBytes() ) );
+ Element service = document.getDocumentElement();
+ Node wsdl = service.getFirstChild();
+ if ( wsdl == null )
+ throw new NoSuccessException( "Service not found OR WSDL is not available.", serviceName + " (" + authority
+ + ")" );
+
+ String results = wsdl.getNodeValue();
+ setContents( cacheId, results );
+ return results;
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public String getRegisterDataTypeXML( MobyDataType dataType ) {
+
+ // build the ISA tag (expressing hierarchy of data types)
+ String[] names = dataType.getParentNames();
+ StringBuffer buf = new StringBuffer();
+ for ( int i = 0; i < names.length; i++ ) {
+ buf.append( "<objectType>" );
+ buf.append( names[ i ] );
+ buf.append( "</objectType>" );
+ buf.append( "\n" );
+ }
+
+ // build the HASA/HAS tags (expressing containments of data types)
+ MobyRelationship[] children = dataType.getChildren();
+ StringBuffer buf2 = new StringBuffer(); // for HASA
+ StringBuffer buf3 = new StringBuffer(); // for HAS
+ for ( int i = 0; i < children.length; i++ ) {
+ if ( children[ i ].getRelationshipType() == Central.iHASA ) {
+ buf2.append( "<objectType articleName=\"" );
+ buf2.append( children[ i ].getName() );
+ buf2.append( "\">" );
+ buf2.append( children[ i ].getDataTypeName() );
+ buf2.append( "</objectType>" );
+ }
+ else if ( children[ i ].getRelationshipType() == Central.iHAS ) {
+ buf3.append( "<objectType articleName=\"" );
+ buf3.append( children[ i ].getName() );
+ buf3.append( "\">" );
+ buf3.append( children[ i ].getDataTypeName() );
+ buf3.append( "</objectType>" );
+ }
+ }
+
+ return "<registerObjectClass>" + "<objectType>" + dataType.getName() + "</objectType>"
+ + "<Description><![CDATA[" + dataType.getDescription() + "]]>" + "</Description>"
+ + "<Relationship relationshipType=\"ISA\">" + new String( buf ) + "</Relationship>"
+ + "<Relationship relationshipType=\"HASA\">" + new String( buf2 ) + "</Relationship>"
+ + "<Relationship relationshipType=\"HAS\">" + new String( buf3 ) + "</Relationship>" + "<authURI>"
+ + dataType.getAuthority() + "</authURI>" + "<contactEmail>" + dataType.getEmailContact()
+ + "</contactEmail>" + "</registerObjectClass>";
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public void registerDataType( MobyDataType dataType ) throws MobyException, NoSuccessException,
+ PendingCurationException {
+
+ String result = ( String ) doCall( "registerObjectClass", new Object[] { getRegisterDataTypeXML( dataType ) } );
+ dataType.setId( checkRegistration( result, dataType )[ 0 ] );
+ }
+
+ /*******************************************************************************************************************
+ * B
+ ******************************************************************************************************************/
+ public void unregisterDataType( MobyDataType dataType ) throws MobyException, NoSuccessException,
+ PendingCurationException {
+ String result = ( String ) doCall( "deregisterObjectClass", new Object[] { "<deregisterObjectClass>"
+ + "<objectType>" + dataType.getName() + "</objectType>" + "</deregisterObjectClass>" } );
+ checkRegistration( result, dataType );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public String getRegisterServiceTypeXML( MobyServiceType serviceType ) {
+
+ // build the ISA tag (expressing hierarchy of service types)
+ String[] names = serviceType.getParentNames();
+ StringBuffer buf = new StringBuffer();
+ for ( int i = 0; i < names.length; i++ ) {
+ buf.append( "<serviceType>" );
+ buf.append( names[ i ] );
+ buf.append( "</serviceType>" );
+ buf.append( "\n" );
+ }
+
+ return "<registerServiceType>" + "<serviceType>" + serviceType.getName() + "</serviceType>" + "<contactEmail>"
+ + serviceType.getEmailContact() + "</contactEmail>" + "<authURI>" + serviceType.getAuthority()
+ + "</authURI>" + "<Description><![CDATA[" + serviceType.getDescription() + "]]>" + "</Description>"
+ + "<Relationship relationshipType=\"ISA\">" + new String( buf ) + "</Relationship>"
+ + "</registerServiceType>";
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public void registerServiceType( MobyServiceType serviceType ) throws MobyException, NoSuccessException,
+ PendingCurationException {
+
+ String result = ( String ) doCall( "registerServiceType",
+ new Object[] { getRegisterServiceTypeXML( serviceType ) } );
+ serviceType.setId( checkRegistration( result, serviceType )[ 0 ] );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public void unregisterServiceType( MobyServiceType serviceType ) throws MobyException, NoSuccessException,
+ PendingCurationException {
+ String result = ( String ) doCall( "deregisterServiceType", new Object[] { "<deregisterServiceType>"
+ + "<serviceType>" + serviceType.getName() + "</serviceType>" + "</deregisterServiceType>" } );
+ checkRegistration( result, serviceType );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public String getRegisterNamespaceXML( MobyNamespace namespace ) {
+ return "<registerNamespace>" + "<namespaceType>" + namespace.getName() + "</namespaceType>" + "<contactEmail>"
+ + namespace.getEmailContact() + "</contactEmail>" + "<authURI>" + namespace.getAuthority()
+ + "</authURI>" + "<Description><![CDATA[" + namespace.getDescription() + "]]>" + "</Description>"
+ + "</registerNamespace>";
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public void registerNamespace( MobyNamespace namespace ) throws MobyException, NoSuccessException,
+ PendingCurationException {
+ String result = ( String ) doCall( "registerNamespace", new Object[] { getRegisterNamespaceXML( namespace ) } );
+ namespace.setId( checkRegistration( result, namespace )[ 0 ] );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public void unregisterNamespace( MobyNamespace namespace ) throws MobyException, NoSuccessException,
+ PendingCurationException {
+ String result = ( String ) doCall( "deregisterNamespace", new Object[] { "<deregisterNamespace>"
+ + "<namespaceType>" + namespace.getName() + "</namespaceType>" + "</deregisterNamespace>" } );
+ checkRegistration( result, namespace );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public String getRegisterServiceXML( MobyService service ) {
+ return "<registerService>" + "<Category>" + service.getCategory() + "</Category>" + "<serviceName>"
+ + service.getName() + "</serviceName>" + "<serviceType>" + service.getType() + "</serviceType>"
+ + "<serviceLSID>" + ( service.getLSID() == null ? "" : service.getLSID().trim() ) + "</serviceLSID>"
+ + "<authURI>" + service.getAuthority() + "</authURI>" + "<signatureURL>"
+ + escapeXML( service.getSignatureURL() ) + "</signatureURL>" + "<URL>" + escapeXML( service.getURL() )
+ + "</URL>" + "<contactEmail>" + service.getEmailContact() + "</contactEmail>"
+ + "<authoritativeService>" + ( service.isAuthoritative() ? "1" : "0" ) + "</authoritativeService>"
+ + "<Description><![CDATA[" + service.getDescription() + "]]>" + "</Description>"
+ + buildPrimaryInputTag( service ) + buildSecondaryInputTag( service ) + buildOutputTag( service )
+ + "</registerService>";
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public void registerService( MobyService service ) throws MobyException, NoSuccessException,
+ PendingCurationException {
+
+ String result = ( String ) doCall( "registerService", new Object[] { getRegisterServiceXML( service ) } );
+ String[] registered = checkRegistration( result, service );
+ service.setId( registered[ 0 ] );
+ service.setRDF( registered[ 1 ] );
+ String pathToRDF = service.getPathToRDF();
+ if ( !pathToRDF.equals( "" ) ) {
+ File fileRDF = new File( pathToRDF );
+ try {
+ PrintStream fileout = new PrintStream( new FileOutputStream( fileRDF ) );
+ fileout.println( registered[ 1 ] );
+ fileout.close();
+ }
+ catch ( IOException e ) {
+ StringBuffer buf = new StringBuffer( 100 );
+ buf.append( "Failed to save RDF in '" );
+ buf.append( fileRDF.getAbsolutePath() + "'. " );
+ buf.append( e.toString() );
+ try {
+ File tmpFile = File.createTempFile( service.getName() + "-", ".rdf" );
+ PrintStream fileout = new PrintStream( new FileOutputStream( tmpFile ) );
+ fileout.println( registered[ 1 ] );
+ fileout.close();
+ buf.append( "\nReturned RDF file was therefore stored in: " );
+ buf.append( tmpFile.getAbsolutePath() );
+ }
+ catch ( IOException e2 ) {
+ buf.append( "\nEven saving in a temporary file failed: " );
+ buf.append( e2.toString() );
+ }
+ throw new MobyException( buf.toString() );
+ }
+ }
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public void unregisterService( MobyService service ) throws MobyException, NoSuccessException,
+ PendingCurationException {
+ String result = ( String ) doCall( "deregisterService", new Object[] { "<deregisterService>" + "<authURI>"
+ + service.getAuthority() + "</authURI>" + "<serviceName>" + service.getName() + "</serviceName>"
+ + "</deregisterService>" } );
+ checkRegistration( result, service );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public MobyService[] findService( String serviceType ) throws MobyException {
+ if ( serviceType == null ) return new MobyService[] {};
+ MobyService pattern = new MobyService( "dummy" );
+ pattern.setCategory( "" );
+ pattern.setType( serviceType );
+ return findService( pattern, null );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public MobyService[] findService( String[] keywords ) throws MobyException {
+ if ( keywords == null ) return new MobyService[] {};
+ return findService( null, keywords );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public MobyService[] findService( MobyService pattern ) throws MobyException {
+ if ( pattern == null ) return new MobyService[] {};
+ return findService( pattern, null );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public MobyService[] findService( MobyService pattern, String[] keywords ) throws MobyException {
+ return findService( pattern, keywords, true, true );
+ }
+
+ /*******************************************************************************************************************
+ * All 'findService' methods end up here.
+ ******************************************************************************************************************/
+ public MobyService[] findService( MobyService pattern, String[] keywords, boolean includeChildrenServiceTypes,
+ boolean includeParentDataTypes ) throws MobyException {
+ if ( pattern == null ) {
+ pattern = new MobyService( "dummy" );
+ pattern.setCategory( "" );
+ }
+
+ String result = getServicesAsXML( pattern, keywords, includeChildrenServiceTypes, includeParentDataTypes );
+ MobyService[] services = extractServices( result );
+ return services;
+ }
+
+ // ...actually all 'findService' methods end up here
+ protected String getServicesAsXML( MobyService pattern, String[] keywords, boolean includeChildrenServiceTypes,
+ boolean includeParentDataTypes ) throws MobyException {
+ String[] query = new String[] { "<findService>"
+ + buildQueryObject( pattern, keywords, includeParentDataTypes, includeChildrenServiceTypes, false )
+ + "</findService>" };
+ return ( String ) doCall( "findService", query );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public String call( String methodName, String inputXML ) throws MobyException {
+ Object result;
+ if ( inputXML == null || inputXML.equals( "" ) )
+ result = doCall( methodName, new Object[] {} );
+ else
+ result = doCall( methodName, new Object[] { inputXML } );
+ return ( String ) result;
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ protected static String resultToString( Object result ) throws MobyException {
+ if ( result == null ) throw new MobyException( "Returned result is null." );
+ if ( result instanceof String ) return ( String ) result;
+ if ( result instanceof String[] ) {
+ String[] tmp = ( String[] ) result;
+ StringBuffer buf = new StringBuffer();
+ for ( int i = 0; i < tmp.length; i++ )
+ buf.append( tmp[ i ] );
+ return new String( buf );
+ }
+ if ( result instanceof byte[] ) return new String( ( byte[] ) result );
+
+ throw new MobyException( "Unknown type of result: " + result.getClass().getName() );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public boolean setDebug( boolean enabled ) {
+ boolean oldMode = debug;
+ debug = enabled;
+ return oldMode;
+ }
+
+ /*******************************************************************************************************************
+ * Parses and imports the following XML.
+ *
+ * <pre>
+ * <Relationships>
+ * <Relationship relationshipType='urn:lsid:biomoby.org:servicerelation:isa'>
+ * <serviceType>urn:lsid:biomoby.org:servicetype:analysis</serviceType>
+ * <serviceType>urn:lsid:biomoby.org:servicetype:service</serviceType>
+ * </Relationship>
+ * </Relationships>
+ * </pre>
+ ******************************************************************************************************************/
+ public String[] getServiceTypeRelationships( String serviceTypeName, boolean expand ) throws MobyException {
+ String result = getServiceTypeRelationshipsAsXML( serviceTypeName, expand );
+ return createServiceTypeRelationshipsFromXML( result );
+ }
+
+ //
+ protected String getServiceTypeRelationshipsAsXML( String serviceTypeName, boolean expand ) throws MobyException {
+ return ( String ) doCall( "Relationships", new Object[] { "<Relationship>" + "<serviceType>" + serviceTypeName
+ + "</serviceType>" + "<relationshipType>" + Central.ISA + "</relationshipType>"
+ + "<expandRelationship>" + ( expand ? "1" : "0" ) + "</expandRelationship>" + "</Relationship>" } );
+ }
+
+ //
+ protected String[] createServiceTypeRelationshipsFromXML( String result ) throws MobyException {
+
+ // parse returned XML
+ Vector< String > v = new Vector< String >();
+ Document document = loadDocument( new ByteArrayInputStream( result.getBytes() ) );
+ NodeList list = document.getElementsByTagName( "Relationship" );
+ for ( int i = 0; i < list.getLength(); i++ ) {
+ Element elem = ( Element ) list.item( i );
+ NodeList children = elem.getChildNodes();
+ for ( int j = 0; j < children.getLength(); j++ ) {
+ if ( children.item( j ).getNodeName().equals( "serviceType" ) ) {
+ v.addElement( getFirstValue( children.item( j ) ) );
+ }
+ }
+ }
+ String[] results = new String[ v.size() ];
+ v.copyInto( results );
+ return results;
+ }
+
+ /*******************************************************************************************************************
+ * Parses and imports the following XML.
+ *
+ * <pre>
+ * lt;Relationships>
+ * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:isa'>
+ * <objectType>urn:lsid:biomoby.org:objectclass:virtualsequence</objectType>
+ * <objectType>urn:lsid:biomoby.org:objectclass:object</objectType>
+ * </Relationship>
+ * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:hasa'>
+ * <objectType>urn:lsid:biomoby.org:objectclass:string</objectType>
+ * <objectType>urn:lsid:biomoby.org:objectclass:integer</objectType>
+ * </Relationship>
+ * lt;/Relationships>
+ * </pre>
+ *
+ * Added at Sun Feb 19 19:32:31 PHT 2006: it recognizes also an attributes 'lsid' and 'articleName' in
+ * <objectType> element.
+ ******************************************************************************************************************/
+ public Map getDataTypeRelationships( String dataTypeName ) throws MobyException {
+
+ String cacheId = "getDataTypeRelationships_" + dataTypeName;
+ Map cachedResults = ( Map ) getContents( cacheId );
+ if ( cachedResults != null ) return cachedResults;
+
+ String result = ( String ) doCall( "Relationships", new Object[] { "<Relationships>" + "<objectType>"
+ + dataTypeName + "</objectType>" + "<relationshipType>" + Central.ISA + "</relationshipType>"
+ + "<relationshipType>" + Central.HASA + "</relationshipType>" + "<relationshipType>" + Central.HAS
+ + "</relationshipType>" + "<expandRelationship>1</expandRelationship>" + "</Relationships>" } );
+
+ // parse returned XML
+ Map results = new HashMap();
+ Document document = loadDocument( new ByteArrayInputStream( result.getBytes() ) );
+ NodeList list = document.getElementsByTagName( "Relationship" );
+
+ for ( int i = 0; i < list.getLength(); i++ ) {
+ Element elem = ( Element ) list.item( i );
+ String relType = elem.getAttribute( "relationshipType" );
+ NodeList children = elem.getChildNodes();
+ Vector< String > v = new Vector< String >();
+ for ( int j = 0; j < children.getLength(); j++ ) {
+ if ( children.item( j ).getNodeName().equals( "objectType" ) ) {
+ v.addElement( getFirstValue( children.item( j ) ) );
+ }
+ }
+ String[] names = new String[ v.size() ];
+ v.copyInto( names );
+ results.put( relType, names );
+ }
+
+ setContents( cacheId, results );
+ return results;
+ }
+
+ /*******************************************************************************************************************
+ * Parses and imports the following XML.
+ *
+ * <pre>
+ * lt;Relationships>
+ * <Relationship relationshipType='urn:lsid:biomoby.org:objectrelation:isa'>
+ * <objectType>urn:lsid:biomoby.org:objectclass:virtualsequence</objectType>
+ * <objectType>urn:lsid:biomoby.org:objectclass:object</objectType>
+ * </Relationship>
+ * lt;/Relationships>
+ * </pre>
+ ******************************************************************************************************************/
+ public String[] getDataTypeRelationships( String dataTypeName, String relationshipType ) throws MobyException {
+
+ String cacheId = "getDataTypeRelationships_" + dataTypeName + ":" + relationshipType;
+ String[] cachedResults = ( String[] ) getContents( cacheId );
+ if ( cachedResults != null ) return cachedResults;
+
+ String result = ( String ) doCall( "Relationships", new Object[] { "<Relationships>" + "<objectType>"
+ + dataTypeName + "</objectType>" + "<relationshipType>" + relationshipType + "</relationshipType>"
+ + "<expandRelationship>1</expandRelationship>" + "</Relationships>" } );
+
+ // parse returned XML
+ Vector< String > v = new Vector< String >();
+ Document document = loadDocument( new ByteArrayInputStream( result.getBytes() ) );
+ NodeList list = document.getElementsByTagName( "Relationship" );
+
+ // it should always be just one element in this list
+ for ( int i = 0; i < list.getLength(); i++ ) {
+ Element elem = ( Element ) list.item( i );
+ NodeList children = elem.getChildNodes();
+ for ( int j = 0; j < children.getLength(); j++ ) {
+ if ( children.item( j ).getNodeName().equals( "objectType" ) ) {
+ v.addElement( getFirstValue( children.item( j ) ) );
+ }
+ }
+ }
+ String[] results = new String[ v.size() ];
+ v.copyInto( results );
+
+ setContents( cacheId, results );
+ return results;
+ }
+
+ // /**************************************************************************
+ // *
+ // *************************************************************************/
+ // public MobyRelationship[] getRelationships (String dataTypeName)
+ // throws MobyException {
+ // return null;
+ // }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public String getRegistryEndpoint() {
+ return endpoint.toString();
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public String getRegistryNamespace() {
+ return uri;
+ }
+
+ /*******************************************************************************************************************
+ * Parses and imports the following XML.
+ *
+ * <pre>
+ * <resourceURLs>
+ * <Resource name="Service" url="..." />
+ * <Resource name="Object" url="..." />
+ * <Resource name="Namespace" url="..." />
+ * <Resource name="ServiceInstance" url="..." />
+ * <Resource name="Full" url="..." />
+ * </resourceURLs>
+ * </pre>
+ ******************************************************************************************************************/
+ public MobyResourceRef[] getResourceRefs() throws MobyException {
+
+ String cacheId = "retrieveResourceURLs";
+ MobyResourceRef[] cachedResults = ( MobyResourceRef[] ) getContents( cacheId );
+ if ( cachedResults != null ) return cachedResults;
+
+ String result = ( String ) doCall( "retrieveResourceURLs", new Object[] {} );
+
+ // parse returned XML
+ Vector< MobyResourceRef > v = new Vector< MobyResourceRef >();
+ Document document = loadDocument( new ByteArrayInputStream( result.getBytes() ) );
+ NodeList list = document.getElementsByTagName( "Resource" );
+ for ( int i = 0; i < list.getLength(); i++ ) {
+ Element elem = ( Element ) list.item( i );
+ try {
+ v.addElement( new MobyResourceRef( elem.getAttribute( "name" ), new URL( ( String ) elem
+ .getAttribute( "url" ) ), elem.getAttribute( "type" ) ) );
+ }
+ catch ( MalformedURLException e2 ) {
+ if ( debug ) System.err.println( "Bad URL: " + elem.getAttribute( "url" ) );
+ }
+ }
+
+ MobyResourceRef[] results = new MobyResourceRef[ v.size() ];
+ v.copyInto( results );
+
+ // Add this data to the cache in case we get called again
+ setContents( cacheId, results );
+
+ return results;
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ public InputStream getResource( String resourceName ) throws MobyException {
+
+ MobyResourceRef[] resourceRefs = getResourceRefs();
+ for ( int i = 0; i < resourceRefs.length; i++ ) {
+ if ( resourceName.equalsIgnoreCase( resourceRefs[ i ].getResourceName() ) ) {
+ return Utils.getInputStream( resourceRefs[ i ].getResourceLocation() );
+ }
+ }
+ throw new MobyException( "No resource found for '" + resourceName + "'." );
+ }
+
+ /*******************************************************************************************************************
+ * Return a case-insensitive comparator of Strings. It is used to create various TreeMaps where keys are strings.
+ ******************************************************************************************************************/
+ protected static Comparator getStringComparator() {
+ return new Comparator() {
+ public int compare( Object o1, Object o2 ) {
+ return ( ( String ) o1 ).compareToIgnoreCase( ( String ) o2 );
+ }
+ };
+ }
+
+ // cache URL/URI so we only check once
+ private static String CHECKED_URL = null;
+ private static String CHECKED_URI = null;
+
+ /**
+ * Using this method to get a Central object will ensure that other parts of the org.biomoby.shared class hierarchy
+ * that implicitly check the registry will use the same cache. Otherwise, methods such as
+ * MobyNamespace.getNamespace() must be passed a Central object parameter as well.
+ *
+ * @return a CentralImpl using the default Central URI, and currently a class implementing a caching mechanism
+ */
+ public static CentralImpl getDefaultCentral() throws MobyException {
+ return getDefaultCentral( null );
+ }
+
+ public static CentralImpl getDefaultCentral( Registry reg ) throws MobyException {
+ if ( reg == null && defaultCentrals.containsKey( "" ) ) {
+ return defaultCentrals.get( "" );
+ }
+ else if ( reg != null && defaultCentrals.containsKey( reg.getEndpoint() ) ) {
+ return defaultCentrals.get( reg.getEndpoint() );
+ }
+
+ String className = DEFAULT_CENTRAL_IMPL_CLASSNAME;
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ URL resURL = classLoader.getResource( "META-INF/" + CENTRAL_IMPL_RESOURCE_NAME );
+ if ( resURL != null ) {
+ System.err.println( "Loading " + resURL );
+ try {
+ LineNumberReader reader = new LineNumberReader( new InputStreamReader( resURL.openStream() ) );
+ for ( String line = reader.readLine(); line != null; line = reader.readLine() ) {
+ if ( !line.trim().startsWith( "#" ) ) {
+ className = line.trim();
+ break;
+ }
+ }
+ }
+ catch ( Exception e ) {
+ logger.log( Level.WARNING, "Error reading " + resURL, e );
+ }
+ }
+ try {
+ System.err.println( "Central class is " + className );
+ Class clazz = Class.forName( className );
+ if ( reg == null ) { // should use default nullary c-tor
+ defaultCentrals.put( "", ( CentralImpl ) clazz.newInstance() );
+ }
+ else { // should have (String endpoint, String namespace) c-tor
+ for ( Constructor ctor : clazz.getDeclaredConstructors() ) {
+ Class[] params = ctor.getParameterTypes();
+ if ( params.length == 2 && params[ 0 ].getName().equals( "java.lang.String" )
+ && params[ 1 ].getName().equals( "java.lang.String" ) ) {
+ defaultCentrals.put( reg.getEndpoint(), ( CentralImpl ) ctor.newInstance( reg.getEndpoint(),
+ reg.getNamespace() ) );
+ break;
+ }
+ }
+ if ( !defaultCentrals.containsKey( reg.getEndpoint() ) ) {
+ logger.log( Level.WARNING, "Could not find required (String endpoint, String namespace)"
+ + "constructor for class " + className );
+ }
+ }
+ }
+ catch ( Exception e ) {
+ logger.log( Level.WARNING, "Could not load class " + className, e );
+ if ( reg == null ) {
+ defaultCentrals.put( "", new CentralImpl() ); // fallback to this class, no caching, etc.
+ }
+ else {
+ defaultCentrals.put( reg.getEndpoint(), new CentralImpl( reg.getEndpoint(), reg.getNamespace() ) );
+ }
+ }
+
+ return defaultCentrals.get( reg == null ? "" : reg.getEndpoint() );
+ }
+
+ /**
+ *
+ * @return a String representing the Default mobycentral endpoint. If the system property 'moby.check.default'
+ * exists and is set to true, then the URL http://biomoby.org/mobycentral is queried and the default central
+ * endpoint is returned, otherwise DEFAULT_ENDPOINT is returned.
+ */
+ public static String getDefaultURL() {
+ boolean check = false;
+ try {
+ check = Boolean.getBoolean( "moby.check.default" );
+ }
+ catch ( Exception e ) {
+
+ }
+
+ if ( check ) {
+ // return the last checked url if we have done this before
+ if ( CHECKED_URL != null && CHECKED_URL.trim() != "" ) {
+ return CHECKED_URL;
+ }
+
+ // create a HttpClient object
+ HttpClient client = new HttpClient();
+ // set up the Head method
+ HeadMethod method = new HeadMethod( "http://biomoby.org/mobycentral" );
+ // do not follow redirects or we will get a 411 error
+ method.setFollowRedirects( false );
+ // retry 3 times
+ method.getParams().setParameter( HttpMethodParams.RETRY_HANDLER,
+ new DefaultHttpMethodRetryHandler( 3, false ) );
+ // set the user agent ... should probably make this something more reasonable
+ method.getParams().setParameter( HttpMethodParams.USER_AGENT, "jMoby/1.0" );
+ try {
+ // Execute the method.
+ int statusCode = client.executeMethod( method );
+
+ if ( statusCode != HttpStatus.SC_MOVED_PERMANENTLY ) {
+ System.err.println( "Method failed: " + method.getStatusLine() );
+ }
+ else {
+ try {
+ String location = method.getResponseHeader( "location" ).getValue();
+ CHECKED_URL = location;
+ try {
+ CHECKED_URI = "http://" + ( new URL( CHECKED_URL ).getAuthority() ) + "/MOBY/Central";
+ }
+ catch ( MalformedURLException murle ) {
+ CHECKED_URI = DEFAULT_NAMESPACE;
+ }
+ return CHECKED_URL;
+ }
+ catch ( NullPointerException npe ) {
+ return DEFAULT_ENDPOINT;
+ }
+ }
+ }
+ catch ( HttpException e ) {
+ System.err.println( "Fatal protocol violation: " + e.getMessage() );
+ e.printStackTrace();
+ }
+ catch ( IOException e ) {
+ System.err.println( "Fatal transport error: " + e.getMessage() );
+ e.printStackTrace();
+ }
+ finally {
+ // Release the connection.
+ method.releaseConnection();
+ }
+
+ }
+ else {
+ return DEFAULT_ENDPOINT;
+ }
+ return DEFAULT_ENDPOINT;
+ }
+
+ /**
+ *
+ * @return a String representing the default mobycentral uri. If the system property 'moby.check.default' exists and
+ * is set to true, then the URL http://biomoby.org/mobycentral is queried and the default central namespace
+ * is returned, otherwise DEFAULT_NAMESPACE is returned.
+ */
+ public static String getDefaultURI() {
+ boolean check = false;
+ try {
+ check = Boolean.getBoolean( "moby.check.default" );
+ }
+ catch ( Exception e ) {
+
+ }
+ if ( check ) {
+ if ( CHECKED_URI != null && CHECKED_URI.trim() != "" ) {
+ return CHECKED_URI;
+ }
+ // need to check ...
+ getDefaultURL();
+ return CHECKED_URI;
+ }
+ else {
+ return DEFAULT_NAMESPACE;
+ }
+ }
+
+ /*******************************************************************************************************************
+ * Convert non-suitable characters in a XML string into their entity references.
+ * <p>
+ *
+ * <em>Adapted from jDom.</em>
+ *
+ * @param str
+ * input to be converted
+ * @return If there were any non-suitable characters, return a new string with those characters escaped, otherwise
+ * return the unmodified input string
+ *
+ ******************************************************************************************************************/
+ public String escapeXML( String str ) {
+ StringBuffer buffer = null;
+ char ch;
+ String entity;
+ for ( int i = 0; i < str.length(); i++ ) {
+ ch = str.charAt( i );
+ switch ( ch ) {
+ case '<':
+ entity = "<";
+ break;
+ case '>':
+ entity = ">";
+ break;
+ case '&':
+ entity = "&";
+ break;
+ default:
+ entity = null;
+ break;
+ }
+ if ( buffer == null ) {
+ if ( entity != null ) {
+ // An entity occurred, so we'll have to use StringBuffer
+ // (allocate room for it plus a few more entities).
+ buffer = new StringBuffer( str.length() + 20 );
+ // Copy previous skipped characters and fall through
+ // to pickup current character
+ buffer.append( str.substring( 0, i ) );
+ buffer.append( entity );
+ }
+ }
+ else {
+ if ( entity == null ) {
+ buffer.append( ch );
+ }
+ else {
+ buffer.append( entity );
+ }
+ }
+ }
+
+ // If there were any entities, return the escaped characters
+ // that we put in the StringBuffer. Otherwise, just return
+ // the unmodified input string.
+ return ( buffer == null ) ? str : buffer.toString();
+ }
+
+ /*******************************************************************************************************************
+ * Format an exception.
+ ******************************************************************************************************************/
+ public static String formatFault( AxisFault e, String endpoint, QName method ) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ formatFault( e, new PrintStream( baos ), endpoint, method );
+ return baos.toString();
+ }
+
+ /*******************************************************************************************************************
+ * Format an exception.
+ ******************************************************************************************************************/
+ public static void formatFault( AxisFault e, PrintStream out, String endpoint, QName method ) {
+
+ out.println( "===ERROR===" );
+ out.println( "Fault details:" );
+ // for some obvious errors I do not print all details (with a lenghty trace stack)
+ String faultString = e.getFaultString();
+ if ( ( !faultString.startsWith( "java.net.ConnectException" ) )
+ && ( faultString.indexOf( "Could not find class for the service named:" ) == -1 ) ) {
+ org.w3c.dom.Element[] details = e.getFaultDetails();
+ for ( int i = 0; i < details.length; i++ ) {
+ String s = details[ i ].toString().replaceAll( "<", "<" );
+ s = s.replaceAll( ">", ">" );
+ out.println( s );
+ }
+ }
+ out.println( "Fault string: " + faultString );
+ out.println( "Fault code: " + e.getFaultCode() );
+ out.println( "Fault actor: " + e.getFaultActor() );
+ if ( endpoint != null || method != null ) out.println( "When calling:" );
+ if ( endpoint != null ) out.println( "\t" + endpoint );
+ if ( method != null ) out.println( "\t" + method );
+ out.println( "===========" );
+ }
+
+}
===================================================================
RCS file: /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/BaseClient.java,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -r1.12 -r1.13
--- /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/BaseClient.java 2007/05/29 03:51:46 1.12
+++ /home/repository/moby/moby-live/Java/src/main/org/biomoby/client/BaseClient.java 2008/11/26 08:53:43 1.13
@@ -1,481 +1,429 @@
-// BaseClient.java
-//
-// Created: August 2005
-//
-// This file is a component of the BioMoby project.
-// Copyright Martin Senger (martin.senger at gmail.com).
-//
-
-package org.biomoby.client;
-
-import org.biomoby.shared.MobyException;
-import org.biomoby.shared.MobyService;
-import org.biomoby.shared.parser.JDOMUtils;
-import org.biomoby.shared.Central;
-import org.biomoby.client.CentralImpl;
-import org.biomoby.shared.parser.MobyPackage;
-import org.biomoby.shared.parser.MobyJob;
-import org.biomoby.shared.parser.ServiceException;
-import org.biomoby.shared.parser.MobyTags;
-
-import org.tulsoft.tools.soap.axis.AxisCall;
-import org.tulsoft.shared.GException;
-
-import org.apache.commons.lang.StringUtils;
-
-import org.jdom.input.SAXBuilder;
-import org.jdom.Document;
-import org.jdom.Element;
-import org.jdom.Namespace;
-
-import java.net.URL;
-import java.net.MalformedURLException;
-import java.io.StringReader;
-
-/**
- * This is a base class for Biomoby clients. It takes care about
- * converting user input into Biomoby XML, sending it in a SOAP
- * message to a Biomoby service, waiting for the response and parsing
- * it from Biomoby XML. It also divides requests and responses into
- * so-called <em>jobs</em> - each of them corresponds to a Biomoby
- * query (a Biomoby single network request may contain more
- * queries/jobs). <p>
- *
- * Any client can override various methods - but the ones she/he
- * <em>must</em> override in a subclass are those telling what service
- * to call ({@link #getServiceLocator}), what data to put in a request
- * ({@link #fillRequest(MobyJob,MobyPackage) fillRequest}), and what
- * to do with a response ({@link #useResponse(MobyJob,MobyPackage)
- * useResponse}. <p>
- *
- * @author <A HREF="mailto:martin.senger at gmail.com">Martin Senger</A>
- * @version $Id$
- */
-
-abstract public class BaseClient {
-
- private static org.apache.commons.logging.Log log =
- org.apache.commons.logging.LogFactory.getLog (BaseClient.class);
-
- /**************************************************************************
- *
- *************************************************************************/
- static protected boolean notEmpty (String value) {
- return StringUtils.isNotBlank (value);
- }
- static protected boolean isEmpty (String value) {
- return StringUtils.isBlank (value);
- }
-
- /**************************************************************************
- * The main method that packs input data, invokes a BioMoby
- * service and uses its response. Use this method if the input
- * data should have just one job (which is a usual case) -
- * otherwise use method {@link #process(int)}. <p>
- *
- * @throws MobyException if (a) a sub-class throws it during the
- * filling data or using response, or (b) a Biomoby service
- * invocation fails
- *************************************************************************/
- public void process()
- throws MobyException {
- process (1);
- }
-
- /**************************************************************************
- * The main method that packs input data, invokes a BioMoby
- * service and uses its response. The input data may consist from
- * more than one job (query) - the 'jobCount' is a suggestion how
- * many jobs will be included, but this can be changed by the
- * implementing sub-class. <p>
- *
- * Usually a client developer does not need to overwrite this
- * method. She or he makes the real input data filling in the
- * {@link #fillRequest} method, and uses the response in the
- * {@link #useResponse} method. <p>
- *
- * @throws MobyException if (a) a sub-class throws it during the
- * filling data or using response, or (b) a Biomoby service
- * invocation fails
- *************************************************************************/
- public void process (int jobCount)
- throws MobyException {
-
- String xmlResponse = null;
-
- // input: raw-level processing
- String xmlInput = fillRequest();
- if (xmlInput != null) {
- if ( (xmlInput = interceptRequest (xmlInput)) == null )
- return;
- }
-
- // input: usual processing (i.e. collect XML in iterations)
- else {
- MobyPackage mobyInput = new MobyPackage();
- if (! fillRequest (mobyInput, jobCount)) return;
- xmlInput = mobyInput.toXML();
- if ( (xmlInput = interceptRequest (xmlInput)) == null )
- return;
- }
-
- // calling service
- xmlResponse = callRemoteService (xmlInput);
-
- // output: raw-level processing
- if (! useResponse (xmlResponse)) return;
-
- // output: usual processing (again by iterations)
- MobyPackage mobyResponse = MobyPackage.createFromXML (xmlResponse);
- useResponse (mobyResponse);
- }
-
- public String interceptRequest (String xmlInput)
- throws MobyException {
- return xmlInput;
- }
-
- /**************************************************************************
- * Create raw XML input. Override this method if you have already
- * an input XML, or you want to create it yourself. <p>
- *
- * @return a full XML input for a Biomoby service (in this case no
- * other <tt>fillRequest</tt> methods will called); return null if
- * no XML was created and a usual process to gather it will be used
- *
- * @throws MobyException if an XML cannot be created
- *************************************************************************/
- public String fillRequest()
- throws MobyException {
- return null;
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- protected String filterMobyResponseType (Object result)
- throws MobyException {
- if (result instanceof String)
- return (String)result;
- else if (result instanceof byte[])
- return new String ((byte[])result);
- else
- throw new MobyException
- ("The Biomoby data should be sent/received either as type String or base64/byte[]. " +
- "But they are of type '" + result.getClass().getName() + "'.");
- }
-
- /**************************************************************************
- *
- *************************************************************************/
- protected String callBiomobyService (MobyServiceLocator locator,
- String xmlInput)
- throws MobyException {
-
-
- MobyService service = locator.getService();
- String serviceName = service.getName();
- boolean asBytes = locator.isAsBytes();
- String serviceEndpoint = service.getURL();
- int timeout = locator.getSuggestedTimeout();
-
- try {
- URL target = new URL (serviceEndpoint);
- AxisCall call = new AxisCall (target, timeout);
- call.getCall().setSOAPActionURI (MobyService.BIOMOBY_SERVICE_URI + "#" + serviceName);
- return filterMobyResponseType
- (call.doCall (MobyService.BIOMOBY_SERVICE_URI,
- serviceName,
- new Object[] { sendingFilter (xmlInput, asBytes) }));
- } catch (MalformedURLException e) {
- throw new MobyException ("Service endpoint '" + serviceEndpoint +
- "' is not a valid URL.");
- } catch (GException e) {
- throw new MobyException (e.getMessage(), e);
- }
- }
-
- //
- protected Object sendingFilter (String input, boolean asBytes) {
- if (asBytes) {
- log.debug ("Data sent as a byte array");
- return input.getBytes();
- } else {
- return input;
- }
- }
-
- /**************************************************************************
- * Call a SOAP-based BioMoby service. In order to find what
- * service to call and what are its characteristics (such as its
- * endpoint) it will call method {@link #getServiceLocator} that
- * should be implemented by a sub-class. <p>
- *
- * Once it has the service locator, this class does one of the
- * following, in this order: <ul>
- *
- * <li> The locator must contain at least a service name. If it
- * does not, an exception is raised.
- *
- * <li> If the locator contains a service endpoint, a call is
- * made to this endpoint, using also the service name as a
- * method name.
- *
- * <li> If the locator has a registry endpoint, an enquiry to
- * the registry is made to find an endpoint of a service
- * corresponding with the given service name. Once found, the
- * service is called.
- *
- * <li> The same as the previous one but using a default
- * registry.
- *
- * @param xmlInput data will be sent to the called Biomoby service
- *
- * @return service response (still in XML)
- *
- * @throws MobyException (a) if service call (or a call to
- * registry; for example because the registry does not know given
- * service) fails, or (b) if the used {@link MobyServiceLocator}
- * does not contain a service name.
- *
- *************************************************************************/
- public String callRemoteService (String xmlInput)
- throws MobyException {
-
- // 1) service name is a must
- MobyServiceLocator locator = getServiceLocator();
- MobyService service = locator.getService();
- if (service == null)
- throw new MobyException ("MobyService locator returned an empty service object.\n" +
- "I do not know what service to call...");
- String serviceName = service.getName();
- if (isEmpty (serviceName) ||
- MobyService.DUMMY_NAME.equals (serviceName))
- throw new MobyException ("MobyService locator returned an empty service name.\n" +
- "I do not know what service to call...");
-
- // 2) try service endpoint
- String serviceEndpoint = service.getURL();
- if (notEmpty (serviceEndpoint))
- return callBiomobyService (locator, xmlInput);
-
- // 3) find service endpoint in a Biomoby registry
- Central worker = new CentralImpl (locator.getRegistryEndpoint(),
- locator.getRegistryNamespace());
- MobyService[] services = worker.findService (service);
- if (services == null || services.length == 0)
- throw new MobyException ("Service " + service.toShortString() +
- " is not known in Biomoby registry: \n" +
- "\t" + worker.getRegistryEndpoint() + "\n" +
- "\t" + worker.getRegistryNamespace());
- // ...and call the service
- serviceEndpoint = services[0].getURL();
- if (notEmpty (serviceEndpoint)) {
- service.setURL (serviceEndpoint);
- return callBiomobyService (locator, xmlInput);
- }
-
- // what else can I do?
- throw new MobyException ("Registry has not returned any URL for service " +
- service.toShortString());
- }
-
- /**************************************************************************
- * Fill the whole 'mobyInput' - put there any number of jobs
- * (queries) as you wish (you do not need to follow the 'jobCount'
- * hint suggesting how many jobs should be put there). <p>
- *
- * Usually there is not need to overwrite this method. It serves
- * as an inter-mediator between the main {@link #process} method
- * and the individual request fillings (done by a sub-class in
- * method {@link #fillRequest(MobyJob,MobyPackage)}). <p>
- *
- * @return false if you wish to cancel whole request (nothing will
- * be sent to a Biomoby service); otherwise return true.
- *
- * @param mobyInput is an empty shell that you are supposed to
- * fill with the input data for a Biomoby service execution
- *
- * @param jobCount is only a suggestion how many requests/job
- * should be created (it comes from the {@link #process} method) -
- * but it does not need to be obeyed
- *
- *************************************************************************/
- public boolean fillRequest (MobyPackage mobyInput, int jobCount)
- throws MobyException {
-
- if (jobCount < 0) jobCount = 0;
- for (int i = 0; i < jobCount; i++) {
- MobyJob request = new MobyJob ("job_" + i);
- if (! fillRequest (request, mobyInput))
- break;
- mobyInput.addJob (request);
- }
- return (mobyInput.size() > 0);
- }
-
- /**************************************************************************
- * A raw-level processing. Use it if you need access to raw XML
- * coming from a service. <p>
- *
- * @param xmlResponse is a raw XML response returned from a
- * BioMoby service
- *
- * @return false if the response should be considered fully
- * processed (in this case no other 'useResponse' will be called);
- * true indicates that normal processing of the response will
- * follow; by default, this class (<tt>BaseClient</tt>) returns true
- *
- * @throws MobyException if you are not satisfied with a response
- * data, or from whatever reasons; it also throws this exception
- * if the 'mobyResponse' is broken
- *************************************************************************/
- public boolean useResponse (String xmlResponse)
- throws MobyException {
- return true;
- }
-
- /**************************************************************************
- * A high-level processing. Use it if you need access to all jobs
- * (queries) - that returned from a service - in the same time.
- * Otherwise use the processing on the job level (method {@link
- * #useResponse(MobyJob,MobyPackage)}. <p>
- *
- * @param mobyResponse is a full response returned from a BioMoby
- * service
- *
- * @throws MobyException if you are not satisfied with a response
- * data, or from whatever reasons; it also throws this exception
- * if the 'mobyResponse' is broken
- *************************************************************************/
- public void useResponse (MobyPackage mobyResponse)
- throws MobyException {
-
- // iterate over all input jobs
- for (int i = 0; i < mobyResponse.size(); i++) {
- if (! useResponse (mobyResponse.getJob (i),
- mobyResponse))
- return;
- }
- }
-
- /**************************************************************************
- * Extracts errors from a raw service response. It is iseful when
- * one does not want to create a whole <tt>MobyPackage</tt> from a
- * response, but just find whether a response is good or bad. <p>
- *
- * @param xmlResponse is a full response returned from a BioMoby
- * service
- *
- * @return a slightly formatted list of exceptions (of severity
- * <em>error</em>) extracted from the response; or null if there
- * are no errors there
- *************************************************************************/
- public String errorsInResponse (String xmlResponse) {
- try {
- StringBuffer buf = new StringBuffer();
- SAXBuilder builder = new SAXBuilder();
- Document doc =
- builder.build (new StringReader (xmlResponse));
- Element root = doc.getRootElement();
- Element mobyContent = JDOMUtils.getChild (root, MobyTags.MOBYCONTENT);
- Element serviceNotes = JDOMUtils.getChild (mobyContent, MobyTags.SERVICENOTES);
- ServiceException[] exceptions =
- ServiceException.extractExceptions (serviceNotes);
- for (int i = 0; i < exceptions.length; i++) {
- if (exceptions[i].getSeverity() != ServiceException.ERROR)
- continue;
- if (buf.length() > 0)
- buf.append ("\n");
- buf.append (exceptions[i].getErrorCodeAsString());
- buf.append (": ");
- buf.append (exceptions[i].getMessage());
- }
- return (buf.length() == 0 ? null : new String (buf));
-
- } catch (Exception e) {
- return e.toString();
- }
- }
-
-
- //
- // Abstract methods
- //
-
- /**************************************************************************
- * Return characteristics of a BioMoby service that will be
- * called, and that reveal where to find such service. <p>
- *
- * @see #callRemoteService
- *
- * @return an object defining a location of a BioMoby service
- * @throws MobyException if service locator cannot be
- * returned/created (e.g. because there is not enough information
- * about what service to call)
- *************************************************************************/
- abstract public MobyServiceLocator getServiceLocator()
- throws MobyException;
-
- /**************************************************************************
- * Crate data (fill them into 'request') for one Moby job
- * (query). The request will be sent within given 'inputContext' -
- * but it is not yet there (because you may wish not to put it
- * there - see the return value), and it is not the task of this
- * method to put it there (just fill the 'request'). <p>
- *
- * This is a method that should be implemented by a client
- * developer, and it is the place where the client's business
- * logic sits. <p>
- *
- * @return true if this request should be included into the input
- * data (package) that will be sent to a biomoby service; or
- * return false if you do not wish to create any more requests for
- * this particular package (also this 'request' will not be used)
- *
- * @param request is an object that you are supposed to fill with
- * input data for one service invocation; it already has a name
- * (so called 'query id' in the BioMoby speak) but you are free to change it
- *
- * @param inputContext is an envelope where all requests will be
- * stored and sent to a Biomoby service; you do not need to do
- * anything with it here unless you wish; note that all already
- * created requests are there, but not the one you are just
- * creating in this method
- *
- * @throws MobyException if you need so (from whatever reason in
- * your business logic); if thrown then nothing will be sent to a
- * Biomoby service
- *
- *************************************************************************/
- abstract public boolean fillRequest (MobyJob request, MobyPackage inputContext)
- throws MobyException;
-
- /**************************************************************************
- * Process a single job returned from a BioMoby service. <p>
- *
- * This is a method that should be implemented by a client
- * developer, and it is the place where the client's business
- * logic using the response sits. <p>
- *
- * @param response is an object that you are supposed to use
- *
- * @param responseContext is an envelope where the full response
- * (all its jobs) is located; you do not need to do anything with
- * it here unless you wish (e.g. it gives you knowledge about how
- * many jobs are in the full response, or it gives you access to
- * the so-called 'service notes')
- *
- * @return false if you do not wish to get any more
- * responses/jobs; otherwise return true
- *
- * @throws MobyException if you are not satisfied with a response
- * data, or from whatever reasons; it also throws this exception
- * if the 'response' is broken
- *
- *************************************************************************/
- abstract public boolean useResponse (MobyJob response,
- MobyPackage responseContext)
- throws MobyException;
-
-}
+// BaseClient.java
+//
+// Created: August 2005
+//
+// This file is a component of the BioMoby project.
+// Copyright Martin Senger (martin.senger at gmail.com).
+//
+
+package org.biomoby.client;
+
+import java.io.StringReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.axis.client.Call;
+import org.apache.commons.lang.StringUtils;
+import org.biomoby.shared.Central;
+import org.biomoby.shared.MobyException;
+import org.biomoby.shared.MobyService;
+import org.biomoby.shared.parser.JDOMUtils;
+import org.biomoby.shared.parser.MobyJob;
+import org.biomoby.shared.parser.MobyPackage;
+import org.biomoby.shared.parser.MobyTags;
+import org.biomoby.shared.parser.ServiceException;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.input.SAXBuilder;
+import org.tulsoft.shared.GException;
+import org.tulsoft.tools.soap.axis.AxisCall;
+
+/**
+ * This is a base class for Biomoby clients. It takes care about converting user input into Biomoby XML, sending it in a
+ * SOAP message to a Biomoby service, waiting for the response and parsing it from Biomoby XML. It also divides requests
+ * and responses into so-called <em>jobs</em> - each of them corresponds to a Biomoby query (a Biomoby single network
+ * request may contain more queries/jobs).
+ * <p>
+ *
+ * Any client can override various methods - but the ones she/he <em>must</em> override in a subclass are those
+ * telling what service to call ({@link #getServiceLocator}), what data to put in a request ({@link #fillRequest(MobyJob,MobyPackage) fillRequest}),
+ * and what to do with a response ({@link #useResponse(MobyJob,MobyPackage) useResponse}.
+ * <p>
+ *
+ * @author <A HREF="mailto:martin.senger at gmail.com">Martin Senger</A>
+ * @version $Id$
+ */
+
+abstract public class BaseClient {
+ private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog( BaseClient.class );
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ static protected boolean notEmpty(String value) {
+ return StringUtils.isNotBlank( value );
+ }
+ static protected boolean isEmpty(String value) {
+ return StringUtils.isBlank( value );
+ }
+
+ /*******************************************************************************************************************
+ * The main method that packs input data, invokes a BioMoby service and uses its response. Use this method if the
+ * input data should have just one job (which is a usual case) - otherwise use method {@link #process(int)}.
+ * <p>
+ *
+ * @throws MobyException if (a) a sub-class throws it during the filling data or using response, or (b) a Biomoby
+ * service invocation fails
+ ******************************************************************************************************************/
+ public void process() throws MobyException {
+ process( 1 );
+ }
+
+ /*******************************************************************************************************************
+ * The main method that packs input data, invokes a BioMoby service and uses its response. The input data may
+ * consist from more than one job (query) - the 'jobCount' is a suggestion how many jobs will be included, but this
+ * can be changed by the implementing sub-class.
+ * <p>
+ *
+ * Usually a client developer does not need to overwrite this method. She or he makes the real input data filling in
+ * the {@link #fillRequest} method, and uses the response in the {@link #useResponse} method.
+ * <p>
+ *
+ * @throws MobyException if (a) a sub-class throws it during the filling data or using response, or (b) a Biomoby
+ * service invocation fails
+ ******************************************************************************************************************/
+ public void process(int jobCount) throws MobyException {
+
+ String xmlResponse = null;
+
+ // input: raw-level processing
+ String xmlInput = fillRequest();
+ if ( xmlInput != null ) {
+ if ( ( xmlInput = interceptRequest( xmlInput ) ) == null )
+ return;
+ }
+
+ // input: usual processing (i.e. collect XML in iterations)
+ else {
+ MobyPackage mobyInput = new MobyPackage();
+ if ( !fillRequest( mobyInput, jobCount ) )
+ return;
+ xmlInput = mobyInput.toXML();
+ if ( ( xmlInput = interceptRequest( xmlInput ) ) == null )
+ return;
+ }
+
+ // calling service
+ xmlResponse = callRemoteService( xmlInput );
+
+ // output: raw-level processing
+ if ( !useResponse( xmlResponse ) )
+ return;
+
+ // output: usual processing (again by iterations)
+ MobyPackage mobyResponse = MobyPackage.createFromXML( xmlResponse );
+ useResponse( mobyResponse );
+ }
+
+ public String interceptRequest(String xmlInput) throws MobyException {
+ return xmlInput;
+ }
+
+ /*******************************************************************************************************************
+ * Create raw XML input. Override this method if you have already an input XML, or you want to create it yourself.
+ * <p>
+ *
+ * @return a full XML input for a Biomoby service (in this case no other <tt>fillRequest</tt> methods will
+ * called); return null if no XML was created and a usual process to gather it will be used
+ *
+ * @throws MobyException if an XML cannot be created
+ ******************************************************************************************************************/
+ public String fillRequest() throws MobyException {
+ return null;
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ protected String filterMobyResponseType(Object result) throws MobyException {
+ if ( result instanceof String )
+ return ( String ) result;
+ else if ( result instanceof byte[] )
+ return new String( ( byte[] ) result );
+ else
+ throw new MobyException(
+ "The Biomoby data should be sent/received either as type String or base64/byte[]. "
+ + "But they are of type '" + result.getClass().getName() + "'." );
+ }
+
+ /*******************************************************************************************************************
+ *
+ ******************************************************************************************************************/
+ protected String callBiomobyService(MobyServiceLocator locator, String xmlInput) throws MobyException {
+
+ MobyService service = locator.getService();
+ String serviceName = service.getName();
+ boolean asBytes = locator.isAsBytes();
+ String serviceEndpoint = service.getURL();
+ int timeout = locator.getSuggestedTimeout();
+
+ try {
+ URL target = new URL( serviceEndpoint );
+ AxisCall call = new AxisCall( target, timeout );
+ // to allow service calls which need authentication
+ setAuthentication( call.getCall() );
+ call.getCall().setSOAPActionURI( MobyService.BIOMOBY_SERVICE_URI + "#" + serviceName );
+
+ return filterMobyResponseType( call.doCall( MobyService.BIOMOBY_SERVICE_URI, serviceName,
+ new Object[]{sendingFilter( xmlInput, asBytes )} ) );
+ }
+ catch ( MalformedURLException e ) {
+ throw new MobyException( "Service endpoint '" + serviceEndpoint + "' is not a valid URL." );
+ }
+ catch ( GException e ) {
+ throw new MobyException( e.getMessage(), e );
+ }
+ }
+
+ /**
+ * Sets the authentication for the call. The default implementation does not do anything !
+ *
+ * @param call
+ */
+ protected void setAuthentication(Call call) {
+ return;
+ }
+
+ //
+ protected Object sendingFilter(String input, boolean asBytes) {
+ if ( asBytes ) {
+ log.debug( "Data sent as a byte array" );
+ return input.getBytes();
+ }
+ else {
+ return input;
+ }
+ }
+
+ /*******************************************************************************************************************
+ * Call a SOAP-based BioMoby service. In order to find what service to call and what are its characteristics (such
+ * as its endpoint) it will call method {@link #getServiceLocator} that should be implemented by a sub-class.
+ * <p>
+ *
+ * Once it has the service locator, this class does one of the following, in this order:
+ * <ul>
+ *
+ * <li> The locator must contain at least a service name. If it does not, an exception is raised.
+ *
+ * <li> If the locator contains a service endpoint, a call is made to this endpoint, using also the service name as
+ * a method name.
+ *
+ * <li> If the locator has a registry endpoint, an enquiry to the registry is made to find an endpoint of a service
+ * corresponding with the given service name. Once found, the service is called.
+ *
+ * <li> The same as the previous one but using a default registry.
+ *
+ * @param xmlInput data will be sent to the called Biomoby service
+ *
+ * @return service response (still in XML)
+ *
+ * @throws MobyException (a) if service call (or a call to registry; for example because the registry does not know
+ * given service) fails, or (b) if the used {@link MobyServiceLocator} does not contain a service name.
+ *
+ ******************************************************************************************************************/
+ public String callRemoteService(String xmlInput) throws MobyException {
+
+ // 1) service name is a must
+ MobyServiceLocator locator = getServiceLocator();
+ MobyService service = locator.getService();
+ if ( service == null )
+ throw new MobyException( "MobyService locator returned an empty service object.\n"
+ + "I do not know what service to call..." );
+ String serviceName = service.getName();
+ if ( isEmpty( serviceName ) || MobyService.DUMMY_NAME.equals( serviceName ) )
+ throw new MobyException( "MobyService locator returned an empty service name.\n"
+ + "I do not know what service to call..." );
+
+ // 2) try service endpoint
+ String serviceEndpoint = service.getURL();
+ if ( notEmpty( serviceEndpoint ) )
+ return callBiomobyService( locator, xmlInput );
+
+ // 3) find service endpoint in a Biomoby registry
+ Central worker = new CentralImpl( locator.getRegistryEndpoint(), locator.getRegistryNamespace() );
+ MobyService[] services = worker.findService( service );
+ if ( services == null || services.length == 0 )
+ throw new MobyException( "Service " + service.toShortString() + " is not known in Biomoby registry: \n"
+ + "\t" + worker.getRegistryEndpoint() + "\n" + "\t" + worker.getRegistryNamespace() );
+ // ...and call the service
+ serviceEndpoint = services[ 0 ].getURL();
+ if ( notEmpty( serviceEndpoint ) ) {
+ service.setURL( serviceEndpoint );
+ return callBiomobyService( locator, xmlInput );
+ }
+
+ // what else can I do?
+ throw new MobyException( "Registry has not returned any URL for service " + service.toShortString() );
+ }
+
+ /*******************************************************************************************************************
+ * Fill the whole 'mobyInput' - put there any number of jobs (queries) as you wish (you do not need to follow the
+ * 'jobCount' hint suggesting how many jobs should be put there).
+ * <p>
+ *
+ * Usually there is not need to overwrite this method. It serves as an inter-mediator between the main
+ * {@link #process} method and the individual request fillings (done by a sub-class in method
+ * {@link #fillRequest(MobyJob,MobyPackage)}).
+ * <p>
+ *
+ * @return false if you wish to cancel whole request (nothing will be sent to a Biomoby service); otherwise return
+ * true.
+ *
+ * @param mobyInput is an empty shell that you are supposed to fill with the input data for a Biomoby service
+ * execution
+ *
+ * @param jobCount is only a suggestion how many requests/job should be created (it comes from the {@link #process}
+ * method) - but it does not need to be obeyed
+ *
+ ******************************************************************************************************************/
+ public boolean fillRequest(MobyPackage mobyInput, int jobCount) throws MobyException {
+
+ if ( jobCount < 0 )
+ jobCount = 0;
+ for ( int i = 0; i < jobCount; i++ ) {
+ MobyJob request = new MobyJob( "job_" + i );
+ if ( !fillRequest( request, mobyInput ) )
+ break;
+ mobyInput.addJob( request );
+ }
+ return ( mobyInput.size() > 0 );
+ }
+
+ /*******************************************************************************************************************
+ * A raw-level processing. Use it if you need access to raw XML coming from a service.
+ * <p>
+ *
+ * @param xmlResponse is a raw XML response returned from a BioMoby service
+ *
+ * @return false if the response should be considered fully processed (in this case no other 'useResponse' will be
+ * called); true indicates that normal processing of the response will follow; by default, this class (<tt>BaseClient</tt>)
+ * returns true
+ *
+ * @throws MobyException if you are not satisfied with a response data, or from whatever reasons; it also throws
+ * this exception if the 'mobyResponse' is broken
+ ******************************************************************************************************************/
+ public boolean useResponse(String xmlResponse) throws MobyException {
+ return true;
+ }
+
+ /*******************************************************************************************************************
+ * A high-level processing. Use it if you need access to all jobs (queries) - that returned from a service - in the
+ * same time. Otherwise use the processing on the job level (method {@link #useResponse(MobyJob,MobyPackage)}.
+ * <p>
+ *
+ * @param mobyResponse is a full response returned from a BioMoby service
+ *
+ * @throws MobyException if you are not satisfied with a response data, or from whatever reasons; it also throws
+ * this exception if the 'mobyResponse' is broken
+ ******************************************************************************************************************/
+ public void useResponse(MobyPackage mobyResponse) throws MobyException {
+
+ // iterate over all input jobs
+ for ( int i = 0; i < mobyResponse.size(); i++ ) {
+ if ( !useResponse( mobyResponse.getJob( i ), mobyResponse ) )
+ return;
+ }
+ }
+
+ /*******************************************************************************************************************
+ * Extracts errors from a raw service response. It is iseful when one does not want to create a whole
+ * <tt>MobyPackage</tt> from a response, but just find whether a response is good or bad.
+ * <p>
+ *
+ * @param xmlResponse is a full response returned from a BioMoby service
+ *
+ * @return a slightly formatted list of exceptions (of severity <em>error</em>) extracted from the response; or
+ * null if there are no errors there
+ ******************************************************************************************************************/
+ public String errorsInResponse(String xmlResponse) {
+ try {
+ StringBuffer buf = new StringBuffer();
+ SAXBuilder builder = new SAXBuilder();
+ Document doc = builder.build( new StringReader( xmlResponse ) );
+ Element root = doc.getRootElement();
+ Element mobyContent = JDOMUtils.getChild( root, MobyTags.MOBYCONTENT );
+ Element serviceNotes = JDOMUtils.getChild( mobyContent, MobyTags.SERVICENOTES );
+ ServiceException[] exceptions = ServiceException.extractExceptions( serviceNotes );
+ for ( int i = 0; i < exceptions.length; i++ ) {
+ if ( exceptions[ i ].getSeverity() != ServiceException.ERROR )
+ continue;
+ if ( buf.length() > 0 )
+ buf.append( "\n" );
+ buf.append( exceptions[ i ].getErrorCodeAsString() );
+ buf.append( ": " );
+ buf.append( exceptions[ i ].getMessage() );
+ }
+ return ( buf.length() == 0 ? null : new String( buf ) );
+
+ }
+ catch ( Exception e ) {
+ return e.toString();
+ }
+ }
+
+ //
+ // Abstract methods
+ //
+
+ /*******************************************************************************************************************
+ * Return characteristics of a BioMoby service that will be called, and that reveal where to find such service.
+ * <p>
+ *
+ * @see #callRemoteService
+ *
+ * @return an object defining a location of a BioMoby service
+ * @throws MobyException if service locator cannot be returned/created (e.g. because there is not enough information
+ * about what service to call)
+ ******************************************************************************************************************/
+ abstract public MobyServiceLocator getServiceLocator() throws MobyException;
+
+ /*******************************************************************************************************************
+ * Crate data (fill them into 'request') for one Moby job (query). The request will be sent within given
+ * 'inputContext' - but it is not yet there (because you may wish not to put it there - see the return value), and
+ * it is not the task of this method to put it there (just fill the 'request').
+ * <p>
+ *
+ * This is a method that should be implemented by a client developer, and it is the place where the client's
+ * business logic sits.
+ * <p>
+ *
+ * @return true if this request should be included into the input data (package) that will be sent to a biomoby
+ * service; or return false if you do not wish to create any more requests for this particular package (also
+ * this 'request' will not be used)
+ *
+ * @param request is an object that you are supposed to fill with input data for one service invocation; it already
+ * has a name (so called 'query id' in the BioMoby speak) but you are free to change it
+ *
+ * @param inputContext is an envelope where all requests will be stored and sent to a Biomoby service; you do not
+ * need to do anything with it here unless you wish; note that all already created requests are there,
+ * but not the one you are just creating in this method
+ *
+ * @throws MobyException if you need so (from whatever reason in your business logic); if thrown then nothing will
+ * be sent to a Biomoby service
+ *
+ ******************************************************************************************************************/
+ abstract public boolean fillRequest(MobyJob request, MobyPackage inputContext) throws MobyException;
+
+ /*******************************************************************************************************************
+ * Process a single job returned from a BioMoby service.
+ * <p>
+ *
+ * This is a method that should be implemented by a client developer, and it is the place where the client's
+ * business logic using the response sits.
+ * <p>
+ *
+ * @param response is an object that you are supposed to use
+ *
+ * @param responseContext is an envelope where the full response (all its jobs) is located; you do not need to do
+ * anything with it here unless you wish (e.g. it gives you knowledge about how many jobs are in the full
+ * response, or it gives you access to the so-called 'service notes')
+ *
+ * @return false if you do not wish to get any more responses/jobs; otherwise return true
+ *
+ * @throws MobyException if you are not satisfied with a response data, or from whatever reasons; it also throws
+ * this exception if the 'response' is broken
+ *
+ ******************************************************************************************************************/
+ abstract public boolean useResponse(MobyJob response, MobyPackage responseContext) throws MobyException;
+
+}
More information about the MOBY-guts
mailing list