Java tutorial
/******************************************************************************* * 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); } }