net.sf.taverna.t2.activities.wsdl.T2WSDLSOAPInvoker.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.taverna.t2.activities.wsdl.T2WSDLSOAPInvoker.java

Source

/*******************************************************************************
 * 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.io.StringReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import net.sf.taverna.t2.activities.wsdl.security.SecurityProfiles;
import net.sf.taverna.t2.security.credentialmanager.CMException;
import net.sf.taverna.t2.security.credentialmanager.CredentialManager;
import net.sf.taverna.t2.security.credentialmanager.UsernamePassword;
import net.sf.taverna.wsdl.parser.WSDLParser;
import net.sf.taverna.wsdl.soap.WSDLSOAPInvoker;

import org.apache.axis.AxisProperties;
import org.apache.axis.EngineConfiguration;
import org.apache.axis.MessageContext;
import org.apache.axis.client.Call;
import org.apache.axis.configuration.XMLStringProvider;
import org.apache.axis.message.SOAPHeaderElement;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.DOMOutputter;

import com.fasterxml.jackson.databind.JsonNode;

/**
 * Invokes SOAP based Web Services from T2.
 *
 * Subclasses WSDLSOAPInvoker used for invoking Web Services from Taverna 1.x
 * and extends it to provide support for invoking secure Web services.
 *
 * @author Stuart Owen
 * @author Alex Nenadic
 * @author Stian Soiland-Reyes
 *
 */
public class T2WSDLSOAPInvoker extends WSDLSOAPInvoker {

    private static final String REFERENCE_PROPERTIES = "ReferenceProperties";
    private static final String ENDPOINT_REFERENCE = "EndpointReference";
    private static Logger logger = Logger.getLogger(T2WSDLSOAPInvoker.class);
    private static final Namespace wsaNS = Namespace.getNamespace("wsa",
            "http://schemas.xmlsoap.org/ws/2004/03/addressing");

    private String wsrfEndpointReference = null;

    private CredentialManager credentialManager;

    public T2WSDLSOAPInvoker(WSDLParser parser, String operationName, List<String> outputNames,
            CredentialManager credentialManager) {
        super(parser, operationName, outputNames);
        this.credentialManager = credentialManager;
    }

    public T2WSDLSOAPInvoker(WSDLParser parser, String operationName, List<String> outputNames,
            String wsrfEndpointReference, CredentialManager credentialManager) {
        this(parser, operationName, outputNames, credentialManager);
        this.wsrfEndpointReference = wsrfEndpointReference;
    }

    @SuppressWarnings("unchecked")
    protected void addEndpointReferenceHeaders(List<SOAPHeaderElement> soapHeaders) {
        // Extract elements
        // Add WSA-stuff
        // Add elements

        Document wsrfDoc;
        try {
            wsrfDoc = parseWsrfEndpointReference(wsrfEndpointReference);
        } catch (JDOMException e) {
            logger.warn("Could not parse endpoint reference, ignoring:\n" + wsrfEndpointReference, e);
            return;
        } catch (IOException e) {
            logger.error("Could not read endpoint reference, ignoring:\n" + wsrfEndpointReference, e);
            return;
        }

        Element endpointRefElem = null;
        Element wsrfRoot = wsrfDoc.getRootElement();
        if (wsrfRoot.getNamespace().equals(wsaNS) && wsrfRoot.getName().equals(ENDPOINT_REFERENCE)) {
            endpointRefElem = wsrfRoot;
        } else {
            // Only look for child if the parent is not an EPR
            Element childEndpoint = wsrfRoot.getChild(ENDPOINT_REFERENCE, wsaNS);
            if (childEndpoint != null) {
                // Support wrapped endpoint reference for backward compatibility
                // and convenience (T2-677)
                endpointRefElem = childEndpoint;
            } else {
                logger.warn("Unexpected element name for endpoint reference, but inserting anyway: "
                        + wsrfRoot.getQualifiedName());
                endpointRefElem = wsrfRoot;
            }
        }

        Element refPropsElem = endpointRefElem.getChild(REFERENCE_PROPERTIES, wsaNS);
        if (refPropsElem == null) {
            logger.warn("Could not find " + REFERENCE_PROPERTIES);
            return;
        }

        List<Element> refProps = refPropsElem.getChildren();
        // Make a copy of the list as it would be modified by
        // prop.detach();
        for (Element prop : new ArrayList<Element>(refProps)) {
            DOMOutputter domOutputter = new DOMOutputter();
            SOAPHeaderElement soapElem;
            prop.detach();
            try {
                org.w3c.dom.Document domDoc = domOutputter.output(new Document(prop));
                soapElem = new SOAPHeaderElement(domDoc.getDocumentElement());
            } catch (JDOMException e) {
                logger.warn("Could not translate wsrf element to DOM:\n" + prop, e);
                continue;
            }
            soapElem.setMustUnderstand(false);
            soapElem.setActor(null);
            soapHeaders.add(soapElem);
        }

        // soapHeaders.add(new SOAPHeaderElement((Element) wsrfDoc
        // .getDocumentElement()));
    }

    protected void configureSecurity(Call call, JsonNode bean) throws Exception {

        // If security settings require WS-Security - configure the axis call
        // with appropriate properties
        URI securityProfile = new URI(bean.get("securityProfile").textValue());
        if (securityProfile.equals(SecurityProfiles.WSSECURITY_USERNAMETOKEN_PLAINTEXTPASSWORD)
                || securityProfile.equals(SecurityProfiles.WSSECURITY_USERNAMETOKEN_DIGESTPASSWORD)
                || securityProfile.equals(SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_PLAINTEXTPASSWORD)
                || securityProfile.equals(SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_DIGESTPASSWORD)) {

            UsernamePassword usernamePassword = getUsernameAndPasswordForService(bean, false);
            call.setProperty(Call.USERNAME_PROPERTY, usernamePassword.getUsername());
            call.setProperty(Call.PASSWORD_PROPERTY, usernamePassword.getPasswordAsString());
            usernamePassword.resetPassword();
        } else if (securityProfile.equals(SecurityProfiles.HTTP_BASIC_AUTHN)) {
            // Basic HTTP AuthN - set HTTP headers
            // pathrecursion allowed
            UsernamePassword usernamePassword = getUsernameAndPasswordForService(bean, true);
            MessageContext context = call.getMessageContext();
            context.setUsername(usernamePassword.getUsername());
            context.setPassword(usernamePassword.getPasswordAsString());
            usernamePassword.resetPassword();
        } else {
            logger.error("Unknown security profile " + securityProfile);
        }
    }

    /**
     * Get username and password from Credential Manager or ask user to supply
     * one. Username is the first element of the returned array, and the
     * password is the second.
     */
    protected UsernamePassword getUsernameAndPasswordForService(JsonNode bean, boolean usePathRecursion)
            throws CMException {

        // Try to get username and password for this service from Credential
        // Manager (which should pop up UI if needed)
        URI serviceUri = URI.create(bean.get("operation").get("wsdl").textValue());
        UsernamePassword username_password = credentialManager.getUsernameAndPasswordForService(serviceUri,
                usePathRecursion, null);
        if (username_password == null) {
            throw new CMException("No username/password provided for service " + serviceUri);
        }
        return username_password;
    }

    @Override
    protected List<SOAPHeaderElement> makeSoapHeaders() {
        List<SOAPHeaderElement> soapHeaders = new ArrayList<SOAPHeaderElement>(super.makeSoapHeaders());
        if (wsrfEndpointReference != null && getParser().isWsrfService()) {
            addEndpointReferenceHeaders(soapHeaders);
        }
        return soapHeaders;
    }

    protected org.jdom.Document parseWsrfEndpointReference(String wsrfEndpointReference)
            throws JDOMException, IOException {
        SAXBuilder builder = new SAXBuilder();
        return builder.build(new StringReader(wsrfEndpointReference));
    }

    public Map<String, Object> invoke(Map<String, Object> inputMap, JsonNode bean) throws Exception {

        EngineConfiguration wssEngineConfiguration = null;
        if (bean.has("securityProfile")) {
            URI securityProfile = new URI(bean.get("securityProfile").textValue());

            // If security settings require WS-Security and not just e.g. Basic HTTP
            // AuthN - configure the axis engine from the appropriate config strings
            if (securityProfile.equals(SecurityProfiles.WSSECURITY_USERNAMETOKEN_PLAINTEXTPASSWORD)) {
                wssEngineConfiguration = new XMLStringProvider(
                        SecurityProfiles.WSSECURITY_USERNAMETOKEN_PLAINTEXTPASSWORD_CONFIG);
            } else if (securityProfile.equals(SecurityProfiles.WSSECURITY_USERNAMETOKEN_DIGESTPASSWORD)) {
                wssEngineConfiguration = new XMLStringProvider(
                        SecurityProfiles.WSSECURITY_USERNAMETOKEN_DIGESTPASSWORD_CONFIG);
            } else if (securityProfile
                    .equals(SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_PLAINTEXTPASSWORD)) {
                wssEngineConfiguration = new XMLStringProvider(
                        SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_PLAINTETPASSWORD_CONFIG);
            } else if (securityProfile.equals(SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_DIGESTPASSWORD)) {
                wssEngineConfiguration = new XMLStringProvider(
                        SecurityProfiles.WSSECURITY_TIMESTAMP_USERNAMETOKEN_DIGESTPASSWORD_CONFIG);
            }
        }

        // This does not work
        //      ClassUtils.setClassLoader("net.sf.taverna.t2.activities.wsdl.security.TavernaAxisCustomSSLSocketFactory",TavernaAxisCustomSSLSocketFactory.class.getClassLoader());

        // Setting Axis property only works when we also set the Thread's classloader as below
        // (we do it from the net.sf.taverna.t2.workflowmodel.processor.dispatch.layers.Invoke.requestRun())
        //      Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
        if (AxisProperties.getProperty("axis.socketSecureFactory") == null
                || !AxisProperties.getProperty("axis.socketSecureFactory")
                        .equals("net.sf.taverna.t2.activities.wsdl.security.TavernaAxisCustomSSLSocketFactory")) {
            AxisProperties.setProperty("axis.socketSecureFactory",
                    "net.sf.taverna.t2.activities.wsdl.security.TavernaAxisCustomSSLSocketFactory");
            logger.info("Setting axis.socketSecureFactory property to "
                    + AxisProperties.getProperty("axis.socketSecureFactory"));
        }

        // This also does not work
        //AxisProperties.setClassDefault(SecureSocketFactory.class, "net.sf.taverna.t2.activities.wsdl.security.TavernaAxisCustomSSLSocketFactory");

        Call call = super.getCall(wssEngineConfiguration);

        // Now that we have an axis Call object, configure any additional
        // security properties on it (or its message context or its Transport
        // handler),
        // such as WS-Security UsernameToken or HTTP Basic AuthN
        if (bean.has("securityProfile")) {
            configureSecurity(call, bean);
        }

        return invoke(inputMap, call);
    }

}