| /******************************************************************************* |
| * Copyright (C) 2007 The University of Manchester |
| * |
| * Modifications to the initial code base are copyright of their |
| * respective authors, or their employers as appropriate. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public License |
| * as published by the Free Software Foundation; either version 2.1 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| ******************************************************************************/ |
| package net.sf.taverna.t2.activities.wsdl; |
| |
| import java.io.IOException; |
| import java.net.MalformedURLException; |
| import java.net.SocketTimeoutException; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.wsdl.WSDLException; |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| 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.security.credentialmanager.CredentialManager; |
| import net.sf.taverna.t2.workflowmodel.OutputPort; |
| import net.sf.taverna.t2.workflowmodel.health.RemoteHealthChecker; |
| 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.ActivityOutputPort; |
| import net.sf.taverna.t2.workflowmodel.processor.activity.AsynchronousActivityCallback; |
| import net.sf.taverna.wsdl.parser.TypeDescriptor; |
| import net.sf.taverna.wsdl.parser.UnknownOperationException; |
| import net.sf.taverna.wsdl.parser.WSDLParser; |
| |
| import org.apache.log4j.Logger; |
| import org.xml.sax.SAXException; |
| |
| import com.fasterxml.jackson.databind.JsonNode; |
| |
| /** |
| * An asynchronous Activity that is concerned with WSDL based web-services. |
| * <p> |
| * The activity is configured according to the WSDL location and the operation.<br> |
| * The ports are defined dynamically according to the WSDL specification, and in |
| * addition an output<br> |
| * port <em>attachmentList</em> is added to represent any attachements that are |
| * returned by the webservice. |
| * </p> |
| * |
| * @author Stuart Owen |
| * @author Stian Soiland-Reyes |
| */ |
| public class WSDLActivity extends |
| AbstractAsynchronousActivity<JsonNode> implements |
| InputPortTypeDescriptorActivity, OutputPortTypeDescriptorActivity { |
| |
| public static final String URI = "http://ns.taverna.org.uk/2010/activity/wsdl"; |
| |
| public static final String ENDPOINT_REFERENCE = "EndpointReference"; |
| private JsonNode configurationBean; |
| private WSDLParser parser; |
| // private Map<String, Integer> outputDepth = new HashMap<String, Integer>(); |
| private boolean isWsrfService = false; |
| // private String endpointReferenceInputPortName; |
| private CredentialManager credentialManager; |
| |
| public WSDLActivity(CredentialManager credentialManager) { |
| this.credentialManager = credentialManager; |
| } |
| |
| public boolean isWsrfService() { |
| return isWsrfService; |
| } |
| |
| private static Logger logger = Logger.getLogger(WSDLActivity.class); |
| |
| /** |
| * Configures the activity according to the information passed by the |
| * configuration bean.<br> |
| * During this process the WSDL is parsed to determine the input and output |
| * ports. |
| * |
| * @param bean |
| * the {@link WSDLActivityConfigurationBean} configuration bean |
| */ |
| @Override |
| public void configure(JsonNode bean) |
| throws ActivityConfigurationException { |
| this.configurationBean = bean; |
| try { |
| parseWSDL(); |
| } catch (Exception ex) { |
| throw new ActivityConfigurationException( |
| "Unable to parse the WSDL " + bean.get("operation").get("wsdl").textValue(), ex); |
| } |
| } |
| |
| @Override |
| public JsonNode getConfiguration() { |
| return configurationBean; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @seenet.sf.taverna.t2.activities.wsdl.InputPortTypeDescriptorActivity# |
| * getTypeDescriptorForInputPort(java.lang.String) |
| */ |
| public TypeDescriptor getTypeDescriptorForInputPort(String portName) |
| throws UnknownOperationException, IOException { |
| List<TypeDescriptor> inputDescriptors = parser |
| .getOperationInputParameters(configurationBean.get("operation").get("name").textValue()); |
| TypeDescriptor result = null; |
| for (TypeDescriptor descriptor : inputDescriptors) { |
| if (descriptor.getName().equals(portName)) { |
| result = descriptor; |
| break; |
| } |
| } |
| return result; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @seenet.sf.taverna.t2.activities.wsdl.InputPortTypeDescriptorActivity# |
| * getTypeDescriptorsForInputPorts() |
| */ |
| public Map<String, TypeDescriptor> getTypeDescriptorsForInputPorts() |
| throws UnknownOperationException, IOException { |
| Map<String, TypeDescriptor> descriptors = new HashMap<String, TypeDescriptor>(); |
| List<TypeDescriptor> inputDescriptors = parser |
| .getOperationInputParameters(configurationBean.get("operation").get("name").textValue()); |
| for (TypeDescriptor descriptor : inputDescriptors) { |
| descriptors.put(descriptor.getName(), descriptor); |
| } |
| return descriptors; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @seenet.sf.taverna.t2.activities.wsdl.OutputPortTypeDescriptorActivity# |
| * getTypeDescriptorForOutputPort(java.lang.String) |
| */ |
| public TypeDescriptor getTypeDescriptorForOutputPort(String portName) |
| throws UnknownOperationException, IOException { |
| TypeDescriptor result = null; |
| List<TypeDescriptor> outputDescriptors = parser |
| .getOperationOutputParameters(configurationBean.get("operation").get("name").textValue()); |
| for (TypeDescriptor descriptor : outputDescriptors) { |
| if (descriptor.getName().equals(portName)) { |
| result = descriptor; |
| break; |
| } |
| } |
| return result; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @seenet.sf.taverna.t2.activities.wsdl.OutputPortTypeDescriptorActivity# |
| * getTypeDescriptorsForOutputPorts() |
| */ |
| public Map<String, TypeDescriptor> getTypeDescriptorsForOutputPorts() |
| throws UnknownOperationException, IOException { |
| Map<String, TypeDescriptor> descriptors = new HashMap<String, TypeDescriptor>(); |
| List<TypeDescriptor> inputDescriptors = parser |
| .getOperationOutputParameters(configurationBean.get("operation").get("name").textValue()); |
| for (TypeDescriptor descriptor : inputDescriptors) { |
| descriptors.put(descriptor.getName(), descriptor); |
| } |
| return descriptors; |
| } |
| |
| private void parseWSDL() throws ParserConfigurationException, |
| WSDLException, IOException, SAXException, UnknownOperationException { |
| String wsdlLocation = configurationBean.get("operation").get("wsdl").textValue(); |
| URLConnection connection = null; |
| try { |
| URL wsdlURL = new URL(wsdlLocation); |
| connection = wsdlURL.openConnection(); |
| connection.setConnectTimeout(RemoteHealthChecker.getTimeoutInSeconds() * 1000); |
| connection.connect(); |
| } catch (MalformedURLException e) { |
| throw new IOException("Malformed URL", e); |
| } catch (SocketTimeoutException e) { |
| throw new IOException("Timeout", e); |
| } catch (IOException e) { |
| throw e; |
| } finally { |
| if ((connection != null) && (connection.getInputStream() != null)) { |
| connection.getInputStream().close(); |
| } |
| } |
| parser = new WSDLParser(wsdlLocation); |
| isWsrfService = parser.isWsrfService(); |
| } |
| |
| @Override |
| public void executeAsynch(final Map<String, T2Reference> data, |
| final AsynchronousActivityCallback callback) { |
| |
| callback.requestRun(new Runnable() { |
| |
| public void run() { |
| |
| ReferenceService referenceService = callback.getContext() |
| .getReferenceService(); |
| |
| Map<String, T2Reference> outputData = new HashMap<String, T2Reference>(); |
| Map<String, Object> invokerInputMap = new HashMap<String, Object>(); |
| String endpointReferenceInputPortName = getEndpointReferenceInputPortName(); |
| |
| try { |
| String endpointReference = null; |
| for (String key : data.keySet()) { |
| Object renderIdentifier = referenceService |
| .renderIdentifier(data.get(key), String.class, |
| callback.getContext()); |
| if (isWsrfService() |
| && key.equals(endpointReferenceInputPortName)) { |
| endpointReference = (String) renderIdentifier; |
| } else { |
| invokerInputMap.put(key, renderIdentifier); |
| } |
| } |
| List<String> outputNames = new ArrayList<String>(); |
| for (OutputPort port : getOutputPorts()) { |
| outputNames.add(port.getName()); |
| } |
| |
| T2WSDLSOAPInvoker invoker = new T2WSDLSOAPInvoker(parser, |
| configurationBean.get("operation").get("name").textValue(), outputNames, |
| endpointReference, credentialManager); |
| |
| Map<String, Object> invokerOutputMap = invoker.invoke( |
| invokerInputMap, configurationBean); |
| |
| for (String outputName : invokerOutputMap.keySet()) { |
| Object value = invokerOutputMap.get(outputName); |
| |
| if (value != null) { |
| Integer depth = getOutputPortDepth(outputName); |
| if (depth != null) { |
| outputData.put(outputName, referenceService |
| .register(value, depth, true, callback |
| .getContext())); |
| } else { |
| logger.info("Skipping unknown output port :" |
| + outputName); |
| // // TODO what should the depth be in this case? |
| // outputData.put(outputName, referenceService |
| // .register(value, 0, true, callback |
| // .getContext())); |
| } |
| } |
| } |
| callback.receiveResult(outputData, new int[0]); |
| } catch (ReferenceServiceException e) { |
| logger.error("Error finding the input data for " |
| + getConfiguration().get("operation"), e); |
| callback.fail("Unable to find input data", e); |
| return; |
| } catch (Exception e) { |
| logger.error("Error invoking WSDL service " |
| + getConfiguration().get("operation"), e); |
| callback.fail( |
| "An error occurred invoking the WSDL service", e); |
| return; |
| } |
| |
| } |
| |
| }); |
| |
| } |
| |
| private Integer getOutputPortDepth(String portName) { |
| for (ActivityOutputPort outputPort : getOutputPorts()) { |
| if (outputPort.getName().equals(portName)) { |
| return outputPort.getDepth(); |
| } |
| } |
| return null; |
| } |
| |
| private String getEndpointReferenceInputPortName() { |
| String endpointReferenceInputPortName = null; |
| if (parser.isWsrfService()) { |
| Set<String> inputPorts = new HashSet<>(); |
| try { |
| List<TypeDescriptor> inputDescriptors = parser.getOperationInputParameters(configurationBean |
| .get("operation").get("name").textValue()); |
| for (TypeDescriptor descriptor : inputDescriptors) { |
| inputPorts.add(descriptor.getName()); |
| } |
| } catch (UnknownOperationException | IOException e) { |
| } |
| // Make sure the port name is unique |
| endpointReferenceInputPortName = WSDLActivity.ENDPOINT_REFERENCE; |
| int counter = 0; |
| while (inputPorts.contains(endpointReferenceInputPortName)) { |
| endpointReferenceInputPortName = WSDLActivity.ENDPOINT_REFERENCE + counter++; |
| } |
| } |
| return endpointReferenceInputPortName; |
| } |
| |
| } |