net.sf.taverna.t2.activities.soaplab.SoaplabActivity.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.taverna.t2.activities.soaplab.SoaplabActivity.java

Source

package net.sf.taverna.t2.activities.soaplab;
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceException;

import net.sf.taverna.t2.reference.ReferenceService;
import net.sf.taverna.t2.reference.ReferenceServiceException;
import net.sf.taverna.t2.reference.T2Reference;
import net.sf.taverna.t2.workflowmodel.OutputPort;
import net.sf.taverna.t2.workflowmodel.processor.activity.AbstractAsynchronousActivity;
import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityConfigurationException;
import net.sf.taverna.t2.workflowmodel.processor.activity.ActivityInputPort;
import net.sf.taverna.t2.workflowmodel.processor.activity.AsynchronousActivityCallback;

import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.log4j.Logger;

import com.fasterxml.jackson.databind.JsonNode;

/**
 * An Activity providing Soaplab functionality.
 *
 * @author David Withers
 */
public class SoaplabActivity extends AbstractAsynchronousActivity<JsonNode> {

    public static final String URI = "http://ns.taverna.org.uk/2010/activity/soaplab";

    private static final Logger logger = Logger.getLogger(SoaplabActivity.class);

    private static final int INVOCATION_TIMEOUT = 0;

    private JsonNode json;

    //   private Map<String, Class<?>> inputTypeMap = new HashMap<String, Class<?>>();

    public SoaplabActivity() {
    }

    @Override
    public void configure(JsonNode configurationBean) throws ActivityConfigurationException {
        this.json = configurationBean;
        //      generatePorts();
    }

    @Override
    public JsonNode getConfiguration() {
        return json;
    }

    @Override
    public void executeAsynch(final Map<String, T2Reference> data, final AsynchronousActivityCallback callback) {
        callback.requestRun(new Runnable() {

            @SuppressWarnings("unchecked")
            public void run() {
                ReferenceService referenceService = callback.getContext().getReferenceService();

                Map<String, T2Reference> outputData = new HashMap<String, T2Reference>();

                try {
                    // Copy the contents of the data set in the input map
                    // to a new Map object which just contains the raw data
                    // objects
                    Map<String, Object> soaplabInputMap = new HashMap<String, Object>();
                    for (Map.Entry<String, T2Reference> entry : data.entrySet()) {
                        Class<?> inputType = getInputType(entry.getKey());
                        logger.info("Resolving " + entry.getKey() + " to " + inputType);
                        soaplabInputMap.put(entry.getKey(), referenceService.renderIdentifier(entry.getValue(),
                                inputType, callback.getContext()));
                        logger.info("  Value = " + soaplabInputMap.get(entry.getKey()));
                    }

                    // Invoke the web service...
                    Call call = (Call) new Service().createCall();
                    call.setTimeout(new Integer(INVOCATION_TIMEOUT));
                    // TODO is there endpoint stored in the configuration as a
                    // String or a URL?
                    // URL soaplabWSDLURL = new
                    // URL(configurationBean.getEndpoint());
                    call.setTargetEndpointAddress(json.get("endpoint").textValue());

                    // Invoke the job and wait for it to complete
                    call.setOperationName(new QName("createAndRun"));
                    String jobID = (String) call.invoke(new Object[] { soaplabInputMap });
                    // Get the array of desired outputs to avoid pulling
                    // everything back
                    // TODO Decide how to get the bound ports for the processor
                    // OutputPort[] boundOutputs =
                    // this.proc.getBoundOutputPorts();
                    OutputPort[] boundOutputs = getOutputPorts().toArray(new OutputPort[0]);
                    String[] outputPortNames = new String[boundOutputs.length];
                    for (int i = 0; i < outputPortNames.length; i++) {
                        outputPortNames[i] = boundOutputs[i].getName();
                        logger.debug("Adding output : " + outputPortNames[i]);
                    }

                    if (!isPollingDefined()) {
                        // If we're not polling then use this behaviour
                        call.setOperationName(new QName("waitFor"));
                        call.invoke(new Object[] { jobID });
                    } else {
                        // Wait for the polling interval then request a status
                        // and do this until the status is terminal.
                        boolean polling = true;
                        // Number of milliseconds to wait before the first
                        // status request.
                        int pollingInterval = json.get("pollingInterval").intValue();
                        while (polling) {
                            try {
                                Thread.sleep(pollingInterval);
                            } catch (InterruptedException ie) {
                                // do nothing
                            }
                            call.setOperationName(new QName("getStatus"));
                            String statusString = (String) call.invoke(new Object[] { jobID });
                            logger.info("Polling, status is : " + statusString);
                            if (statusString.equals("RUNNING") || statusString.equals("CREATED")) {
                                pollingInterval = (int) ((double) pollingInterval
                                        * json.get("pollingBackoff").doubleValue());
                                if (pollingInterval > json.get("pollingIntervalMax").intValue()) {
                                    pollingInterval = json.get("pollingIntervalMax").intValue();
                                }
                            } else {
                                // Either completed with an error or success
                                polling = false;
                            }
                        }
                    }

                    // Get the status code
                    call.setOperationName(new QName("getStatus"));
                    String statusString = (String) call.invoke(new Object[] { jobID });
                    if (statusString.equals("TERMINATED_BY_ERROR")) {
                        // Get the report
                        call.setOperationName(new QName("getSomeResults"));
                        HashMap<String, String> temp = new HashMap<String, String>(
                                (Map) call.invoke(new Object[] { jobID, new String[] { "report" } }));
                        String reportText = temp.get("report");
                        callback.fail("Soaplab call returned an error : " + reportText);
                        return;
                    }

                    // Get the results required by downstream processors
                    call.setOperationName(new QName("getSomeResults"));
                    HashMap<String, Object> outputMap = new HashMap<String, Object>(
                            (Map) call.invoke(new Object[] { jobID, outputPortNames }));

                    // Tell soaplab that we don't need this session any more
                    call.setOperationName(new QName("destroy"));
                    call.invoke(new Object[] { jobID });

                    // Build the map of DataThing objects
                    for (Map.Entry<String, Object> entry : outputMap.entrySet()) {
                        String parameterName = entry.getKey();
                        Object outputObject = entry.getValue();
                        if (logger.isDebugEnabled())
                            logger.debug("Soaplab : parameter '" + parameterName + "' has type '"
                                    + outputObject.getClass().getName() + "'");

                        if (outputObject instanceof String[]) {
                            // outputThing = DataThingFactory
                            // .bake((String[]) outputObject);
                            outputData.put(parameterName, referenceService.register(Arrays.asList(outputObject), 1,
                                    true, callback.getContext()));
                        } else if (outputObject instanceof byte[][]) {
                            // Create a List of byte arrays, this will
                            // map to l('application/octet-stream') in
                            // the output document.
                            // outputThing = DataThingFactory
                            // .bake((byte[][]) outputObject);
                            List<byte[]> list = new ArrayList<byte[]>();
                            for (byte[] byteArray : (byte[][]) outputObject) {
                                list.add(byteArray);
                            }
                            outputData.put(parameterName,
                                    referenceService.register(list, 1, true, callback.getContext()));
                            // outputData.put(parameterName, dataFacade
                            // .register(Arrays.asList(outputObject)));
                        } else if (outputObject instanceof List) {
                            List<?> convertedList = convertList((List<?>) outputObject);
                            outputData.put(parameterName,
                                    referenceService.register(convertedList, 1, true, callback.getContext()));
                        } else {
                            // Fallthrough case, this mostly applies to
                            // output of type byte[] or string, both of which
                            // are handled perfectly sensibly by default.
                            outputData.put(parameterName,
                                    referenceService.register(outputObject, 0, true, callback.getContext()));
                        }
                    }

                    // success
                    callback.receiveResult(outputData, new int[0]);
                } catch (ReferenceServiceException e) {
                    callback.fail("Error accessing soaplab input/output data", e);
                } catch (IOException e) {
                    callback.fail("Failure calling soaplab", e);
                } catch (ServiceException e) {
                    callback.fail("Failure calling soaplab", e);
                }
            }

        });

    }

    public boolean isPollingDefined() {
        return json != null
                && (json.get("pollingInterval").intValue() != 0 || json.get("pollingBackoff").doubleValue() != 1.0
                        || json.get("pollingIntervalMax").intValue() != 0);
    }

    private List<?> convertList(List<?> theList) {
        if (theList.size() == 0) {
            return theList;
        }

        List<byte[]> listOfBytes = new ArrayList<byte[]>();
        for (Object element : theList) {
            if (element instanceof List) {
                List<?> list = ((List<?>) element);
                if (list.size() > 0 && (list.get(0) instanceof Byte)) {
                    byte[] bytes = new byte[list.size()];
                    for (int j = 0; j < list.size(); j++) {
                        bytes[j] = ((Byte) list.get(j)).byteValue();
                    }
                    listOfBytes.add(bytes);
                } else {
                    // If we can't cope here just return the original
                    // object
                    return theList;
                }
            } else {
                return theList;
            }
        }
        return listOfBytes;
    }

    private Class<?> getInputType(String portName) {
        Class<?> inputType = String.class;
        for (ActivityInputPort inputPort : getInputPorts()) {
            if (inputPort.getName().equals(portName)) {
                return inputPort.getTranslatedElementClass();
            }
        }
        return inputType;
    }

    //   @SuppressWarnings("unchecked")
    //   private void generatePorts() throws ActivityConfigurationException {
    //      // Wipe the existing port declarations
    //      // ports = new ArrayList();
    //      try {
    //         // Do web service type stuff[tm]
    //         Map<String, String>[] inputs = (Map<String, String>[]) Soap
    //               .callWebService(json.get("endpoint").textValue(),
    //                     "getInputSpec");
    //         // Iterate over the inputs
    //         for (int i = 0; i < inputs.length; i++) {
    //            Map<String, String> input_spec = inputs[i];
    //            String input_name = input_spec.get("name");
    //            String input_type = input_spec.get("type").toLowerCase();
    //            // Could get other properties such as defaults here
    //            // but at the moment we've got nowhere to put them
    //            // so we don't bother.
    //            if (input_type.equals("string")) {
    //               addInput(input_name, 0, true,
    //                     new ArrayList<Class<? extends ExternalReferenceSPI>>(), String.class);
    //               inputTypeMap.put(input_name, String.class);
    //            } else if (input_type.equals("string[]")) {
    //               addInput(input_name, 1, true,
    //                     new ArrayList<Class<? extends ExternalReferenceSPI>>(), String.class);
    //               inputTypeMap.put(input_name, String.class);
    //            } else if (input_type.equals("byte[]")) {
    //               addInput(input_name, 0, true,
    //                     new ArrayList<Class<? extends ExternalReferenceSPI>>(), byte[].class);
    //               inputTypeMap.put(input_name, byte[].class);
    //            } else if (input_type.equals("byte[][]")) {
    //               addInput(input_name, 1, true,
    //                     new ArrayList<Class<? extends ExternalReferenceSPI>>(), byte[].class);
    //               inputTypeMap.put(input_name, byte[].class);
    //            } else {
    //               // Count number of [] to get the arrays right
    //               int depth = (input_type.split("\\[\\]", -1).length) -1 ;
    //               logger.info("Soaplab input type '" + input_type
    //                     + "' unknown for input '" + input_name + "' in "
    //                     + json.get("endpoint").textValue()
    //                     + ", will attempt to add as String depth " + depth);
    //               addInput(input_name, depth, true,
    //                     new ArrayList<Class<? extends ExternalReferenceSPI>>(), String.class);
    //               inputTypeMap.put(input_name, String.class);
    //            }
    //         }
    //
    //         // Get outputs
    //         Map<String, String>[] results = (Map<String, String>[]) Soap
    //               .callWebService(json.get("endpoint").textValue(),
    //                     "getResultSpec");
    //         // Iterate over the outputs
    //         for (int i = 0; i < results.length; i++) {
    //            Map<String, String> output_spec = results[i];
    //            String output_name = output_spec.get("name");
    //            String output_type = output_spec.get("type").toLowerCase();
    //            // Check to see whether the output is either report or
    //            // detailed_status, in
    //            // which cases we ignore it, this is soaplab metadata rather
    //            // than application data.
    //            if ((!output_name.equalsIgnoreCase("detailed_status"))) {
    //
    //               // && (!output_name.equalsIgnoreCase("report"))) {
    //               if (output_type.equals("string")) {
    //                  addOutput(output_name, 0, "text/plain");
    //               } else if (output_type.equals("string[]")) {
    //                  addOutput(output_name, 1, "text/plain");
    //               } else if (output_type.equals("byte[]")) {
    //                  addOutput(output_name, 0, "application/octet-stream");
    //               } else if (output_type.equals("byte[][]")) {
    //                  addOutput(output_name, 1, "application/octet-stream");
    //               } else {
    //                  // Count number of [] to get the arrays right
    //                  int depth = (output_type.split("\\[\\]", -1).length) -1 ;
    //                  logger.info("Soaplab output type '" + output_type
    //                        + "' unknown for output '" + output_name + "' in "
    //                        + json.get("endpoint").textValue()
    //                        + ", will add as depth " + depth);
    //                  addOutput(output_name, depth, null);
    //               }
    //            }
    //         }
    //
    //      } catch (ServiceException se) {
    //         throw new ActivityConfigurationException(
    //               json.get("endpoint").textValue()
    //                     + ": Unable to create a new call to connect\n   to soaplab, error was : "
    //                     + se.getMessage());
    //      } catch (RemoteException re) {
    //         throw new ActivityConfigurationException(
    //               ": Unable to call the get spec method for\n   endpoint : "
    //                     + json.get("endpoint").textValue()
    //                     + "\n   Remote exception message "
    //                     + re.getMessage());
    //      } catch (NullPointerException npe) {
    //         // If we had a null pointer exception, go around again - this is a
    //         // bug somewhere between axis and soaplab
    //         // that occasionally causes NPEs to happen in the first call or two
    //         // to a given soaplab installation. It also
    //         // manifests in the Talisman soaplab clients.
    //         generatePorts();
    //      }
    //   }

    //   protected void addOutput(String portName, int portDepth, String type) {
    //      ActivityOutputPort port = edits.createActivityOutputPort(
    //            portName, portDepth, portDepth);
    //      MimeType mimeType = null;
    //      if (type != null) {
    //         mimeType = new MimeType();
    //         mimeType.setText(type);
    //      }
    //      try {
    //         edits.getAddAnnotationChainEdit(port, mimeType).doEdit();
    //      } catch (EditException e) {
    //         logger.debug("Error adding MimeType annotation to port", e);
    //      }
    //      outputPorts.add(port);
    //   }

}