org.wso2.carbon.bpel.core.ode.integration.utils.AxisServiceUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.bpel.core.ode.integration.utils.AxisServiceUtils.java

Source

/*
 * Copyright (c) 2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * Licensed 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.
 */

package org.wso2.carbon.bpel.core.ode.integration.utils;

import org.apache.axis2.AxisFault;
import org.apache.axis2.client.OperationClient;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.context.ServiceContext;
import org.apache.axis2.context.ServiceGroupContext;
import org.apache.axis2.deployment.util.Utils;
import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.AxisServiceGroup;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.description.WSDL11ToAxisServiceBuilder;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.commons.httpclient.Header;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.protocol.HTTP;
import org.apache.ode.bpel.iapi.ProcessConf;
import org.apache.ode.utils.Namespaces;
import org.apache.ode.utils.stl.CollectionsX;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.w3c.dom.Element;
import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.bpel.core.BPELConstants;
import org.wso2.carbon.bpel.core.internal.BPELServiceComponent;
import org.wso2.carbon.bpel.core.ode.integration.BPELMessageContext;
import org.wso2.carbon.bpel.core.ode.integration.BPELProcessProxy;
import org.wso2.carbon.bpel.core.ode.integration.axis2.Axis2UriResolver;
import org.wso2.carbon.bpel.core.ode.integration.axis2.Axis2WSDLLocator;
import org.wso2.carbon.bpel.core.ode.integration.axis2.receivers.BPELMessageReceiver;
import org.wso2.carbon.bpel.core.ode.integration.config.BPELServerConfiguration;
import org.wso2.carbon.utils.ServerConstants;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.wsdl.BindingOperation;
import javax.wsdl.Definition;
import javax.wsdl.Input;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.extensions.http.HTTPAddress;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.extensions.soap.SOAPOperation;
import javax.wsdl.extensions.soap12.SOAP12Address;
import javax.wsdl.extensions.soap12.SOAP12Operation;
import javax.xml.namespace.QName;

/**
 * This class contains utility functions used by ODE-Carbon Integration Layer to create,
 * and configure AxisServices.
 */
public final class AxisServiceUtils {
    private static final Log log = LogFactory.getLog(AxisServiceUtils.class);

    private AxisServiceUtils() {
    }

    /**
     * Build the underlying Axis Service from Service QName and Port Name of interest using given WSDL
     * for BPEL document.
     * In the current implementation we are extracting service name from the soap:address' location property.
     * But specified port may not contain soap:adress instead it may contains http:address. We need to handle that
     * situation.
     *
     * @param axisConfiguration AxisConfiguration to which we should publish the service
     * @param processProxy      BPELProcessProxy
     * @return Axis Service build using WSDL, Service and Port
     * @throws org.apache.axis2.AxisFault on error
     */
    public static AxisService createAxisService(AxisConfiguration axisConfiguration, BPELProcessProxy processProxy)
            throws AxisFault {
        QName serviceName = processProxy.getServiceName();
        String portName = processProxy.getPort();
        Definition wsdlDefinition = processProxy.getWsdlDefinition();
        ProcessConf processConfiguration = processProxy.getProcessConfiguration();

        if (log.isDebugEnabled()) {
            log.debug("Creating AxisService: Service=" + serviceName + " port=" + portName + " WSDL="
                    + wsdlDefinition.getDocumentBaseURI() + " BPEL=" + processConfiguration.getBpelDocument());
        }

        WSDL11ToAxisServiceBuilder serviceBuilder = createAxisServiceBuilder(processProxy);

        /** Need to figure out a way to handle service name extractoin. According to my perspective extracting
         * the service name from the EPR is not a good decision. But we need to handle JMS case properly.
         * I am keeping JMS handling untouched until we figureout best solution. */
        /* String axisServiceName = extractServiceName(processConf, wsdlServiceName, portName);*/
        AxisService axisService = populateAxisService(processProxy, axisConfiguration, serviceBuilder);

        Iterator operations = axisService.getOperations();
        BPELMessageReceiver messageRec = new BPELMessageReceiver();

        /** Set the corresponding BPELService to message receivers */
        messageRec.setProcessProxy(processProxy);

        while (operations.hasNext()) {
            AxisOperation operation = (AxisOperation) operations.next();
            // Setting WSDLAwareMessage Receiver even if operation has a message receiver specified.
            // This is to fix the issue when build service configuration using services.xml(Always RPCMessageReceiver
            // is set to operations).
            operation.setMessageReceiver(messageRec);
            axisConfiguration.getPhasesInfo().setOperationPhases(operation);
        }

        /**
         * TODO: JMS Destination handling.
         */
        return axisService;
    }

    private static AxisService populateAxisService(BPELProcessProxy processProxy,
            AxisConfiguration axisConfiguration, WSDL11ToAxisServiceBuilder serviceBuilder) throws AxisFault {
        ProcessConf pConf = processProxy.getProcessConfiguration();
        AxisService axisService = serviceBuilder.populateService();
        axisService.setParent(axisConfiguration);
        axisService.setWsdlFound(true);
        axisService.setCustomWsdl(true);
        axisService.setClassLoader(axisConfiguration.getServiceClassLoader());
        URL wsdlUrl = null;
        for (File file : pConf.getFiles()) {
            if (file.getAbsolutePath().indexOf(processProxy.getWsdlDefinition().getDocumentBaseURI()) > 0) {
                try {
                    wsdlUrl = file.toURI().toURL();
                } catch (MalformedURLException e) {
                    String errorMessage = "Cannot convert File URI to URL.";
                    handleException(pConf.getProcessId(), errorMessage, e);
                }
            }
        }
        if (wsdlUrl != null) {
            axisService.setFileName(wsdlUrl);
        }

        Utils.setEndpointsToAllUsedBindings(axisService);

        axisService.addParameter(new Parameter("useOriginalwsdl", "true"));
        axisService.addParameter(new Parameter("modifyUserWSDLPortAddress", "true"));
        axisService.addParameter(new Parameter("setEndpointsToAllUsedBindings", "true"));

        /* Setting service type to use in service management*/
        axisService.addParameter(ServerConstants.SERVICE_TYPE, "bpel");

        /* Process ID as a service parameter to use with process try-it*/
        axisService.addParameter(BPELConstants.PROCESS_ID, pConf.getProcessId());

        /* Fix for losing of security configuration  when updating BPEL package*/
        axisService.addParameter(new Parameter(CarbonConstants.PRESERVE_SERVICE_HISTORY_PARAM, "true"));
        //        axisService.addParameter(
        //                new Parameter(BPELConstants.MESSAGE_RECEIVER_INVOKE_ON_SEPARATE_THREAD, "true"));

        return axisService;
    }

    private static WSDL11ToAxisServiceBuilder createAxisServiceBuilder(BPELProcessProxy processProxy)
            throws AxisFault {
        Definition wsdlDef = processProxy.getWsdlDefinition();
        QName serviceName = processProxy.getServiceName();
        String portName = processProxy.getPort();
        ProcessConf pConf = processProxy.getProcessConfiguration();
        QName pid = pConf.getProcessId();
        InputStream wsdlInStream = null;

        URI wsdlBaseURI = pConf.getBaseURI().resolve(wsdlDef.getDocumentBaseURI());
        try {
            wsdlInStream = wsdlBaseURI.toURL().openStream();
        } catch (MalformedURLException e) {
            String errMsg = "Malformed WSDL base URI.";
            handleException(pid, errMsg, e);
        } catch (IOException e) {
            String errMsg = "Error opening stream.";
            handleException(pid, errMsg, e);
        }
        WSDL11ToAxisServiceBuilder serviceBuilder = new WSDL11ToAxisPatchedBuilder(wsdlInStream, serviceName,
                portName);
        serviceBuilder.setBaseUri(wsdlBaseURI.toString());
        serviceBuilder.setCustomResolver(new Axis2UriResolver());
        try {
            serviceBuilder.setCustomWSDLResolver(new Axis2WSDLLocator(wsdlBaseURI));
        } catch (URISyntaxException e) {
            String errorMessage = "URI syntax invalid.";
            handleException(pid, errorMessage, e);
        }
        serviceBuilder.setServerSide(true);

        return serviceBuilder;
    }

    private static void handleException(QName pid, String errorMessage, Exception e) throws AxisFault {
        String tErrorMessage = "Error creating axis service for process " + pid + ".Cause: " + errorMessage;
        log.error(tErrorMessage, e);
        throw new AxisFault(tErrorMessage, e);
    }

    //    public static void engageModules(AxisDescription description, String... modules)
    //            throws AxisFault {
    //        for (String m : modules) {
    //            if (description.getAxisConfiguration().getModule(m) != null) {
    //                if (!description.getAxisConfiguration().isEngaged(m) && !description.isEngaged(m)) {
    //                    description.engageModule(description.getAxisConfiguration().getModule(m));
    //                }
    //            } else {
    //                if (log.isDebugEnabled()) {
    //                    log.debug("Module " + m + " is not available.");
    //                }
    //            }
    //        }
    //    }

    /**
     * Axis2 monkey patching to force the usage of the read(element,baseUri) method
     * of XmlSchema as the normal read is broken.
     */
    public static class WSDL11ToAxisPatchedBuilder extends WSDL11ToAxisServiceBuilder {
        public WSDL11ToAxisPatchedBuilder(InputStream in, QName serviceName, String portName) {
            super(in, serviceName, portName);
        }

        //        public WSDL11ToAxisPatchedBuilder(Definition def, QName serviceName, String portName) {
        //            super(def, serviceName, portName);
        //        }
        //
        //        public WSDL11ToAxisPatchedBuilder(Definition def, QName serviceName, String portName,
        //                                          boolean isAllPorts) {
        //            super(def, serviceName, portName, isAllPorts);
        //        }
        //
        //        public WSDL11ToAxisPatchedBuilder(InputStream in, AxisService service) {
        //            super(in, service);
        //        }
        //
        //        public WSDL11ToAxisPatchedBuilder(InputStream in) {
        //            super(in);
        //        }

        protected XmlSchema getXMLSchema(Element element, String baseUri) {
            XmlSchemaCollection schemaCollection = new XmlSchemaCollection();
            if (baseUri != null) {
                schemaCollection.setBaseUri(baseUri);
            }
            return schemaCollection.read(element, baseUri);
        }
    }

    public static void invokeService(BPELMessageContext partnerInvocationContext,
            ConfigurationContext configContext) throws AxisFault {
        MessageContext mctx = partnerInvocationContext.getInMessageContext();
        OperationClient opClient = getOperationClient(partnerInvocationContext, configContext);
        mctx.getOptions().setParent(opClient.getOptions());

        /*
        Else we assume that the epr is not changed by the process.
        In this case there's a limitation we cannot invoke the epr in the wsdl
        (by assingning that epr by partnerlink assign) if there is a endpoint
        configuration available for that particular service
        */

        addCustomHeadersToMessageContext(mctx);

        opClient.addMessageContext(mctx);
        Options operationOptions = opClient.getOptions();

        if (partnerInvocationContext.getUep().isAddressingEnabled()) {
            //Currently we set the action manually, but this should be handled by
            // addressing module it-self?
            String action = getAction(partnerInvocationContext);
            if (log.isDebugEnabled()) {
                log.debug("Soap action: " + action);
            }
            operationOptions.setAction(action);
            //TODO set replyto as well
            //operationOptions.setReplyTo(mctx.getReplyTo());
        }

        if (partnerInvocationContext.getUep().getAddress() == null) {
            partnerInvocationContext.getUep()
                    .setAddress(getEPRfromWSDL(partnerInvocationContext.getBpelServiceWSDLDefinition(),
                            partnerInvocationContext.getService(), partnerInvocationContext.getPort()));
        }

        operationOptions.setTo(partnerInvocationContext.getUep());

        opClient.execute(true);

        if (partnerInvocationContext.isTwoWay()) {
            partnerInvocationContext
                    .setOutMessageContext(opClient.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE));
            partnerInvocationContext
                    .setFaultMessageContext(opClient.getMessageContext(WSDLConstants.MESSAGE_LABEL_FAULT_VALUE));
        }
    }

    /**
     * Extracts the action to be used for the given operation.  It first checks to see
     * if a value is specified using WS-Addressing in the portType, it then falls back onto
     * getting it from the SOAP Binding.
     *
     * @param partnerMessageContext BPELMessageContext
     * @return The action value for the specified operation
     */
    public static String getAction(BPELMessageContext partnerMessageContext) {
        String action = getWSAInputAction(partnerMessageContext);
        if (action == null || "".equals(action)) {
            action = getSoapAction(partnerMessageContext);
        }
        return action;
    }

    /**
     * Attempts to extract the WS-Addressing "Action" attribute value from the operation definition.
     * When WS-Addressing is being used by a service provider, the "Action" is specified in the
     * portType->operation instead of the SOAP binding->operation.
     *
     * @param partnerMessageContext BPELMessageContext
     * @return the SOAPAction value if one is specified, otherwise empty string
     */
    public static String getWSAInputAction(BPELMessageContext partnerMessageContext) {
        BindingOperation bop = partnerMessageContext.getWsdlBindingForCurrentMessageFlow()
                .getBindingOperation(partnerMessageContext.getOperationName(), null, null);
        if (bop == null) {
            return "";
        }

        Input input = bop.getOperation().getInput();
        if (input != null) {
            Object action = input.getExtensionAttribute(new QName(Namespaces.WS_ADDRESSING_NS, "Action"));
            if (action instanceof String) {
                return ((String) action);
            }

            action = input.getExtensionAttribute(new QName(BPELConstants.WS_ADDRESSING_NS2, "Action"));
            if (action instanceof String) {
                return ((String) action);
            }

            action = input.getExtensionAttribute(new QName(BPELConstants.WS_ADDRESSING_NS3, "Action"));
            if (action instanceof String) {
                return ((String) action);
            }

            action = input.getExtensionAttribute(new QName(BPELConstants.WS_ADDRESSING_NS4, "Action"));
            if (action instanceof String) {
                return ((String) action);
            }
        }
        return "";
    }

    /**
     * Attempts to extract the SOAP Action is defined in the WSDL document.
     *
     * @param partnerMessageContext BPELMessageContext
     * @return the SOAPAction value if one is specified, otherwise empty string
     */
    public static String getSoapAction(BPELMessageContext partnerMessageContext) {
        BindingOperation bop = partnerMessageContext.getWsdlBindingForCurrentMessageFlow()
                .getBindingOperation(partnerMessageContext.getOperationName(), null, null);
        if (bop == null) {
            return "";
        }

        if (partnerMessageContext.isSoap12()) {
            for (SOAP12Operation soapOp : CollectionsX.filter(bop.getExtensibilityElements(),
                    SOAP12Operation.class)) {
                return soapOp.getSoapActionURI();
            }
        } else {
            for (SOAPOperation soapOp : CollectionsX.filter(bop.getExtensibilityElements(), SOAPOperation.class)) {
                return soapOp.getSoapActionURI();
            }
        }

        return "";
    }

    public static OperationClient getOperationClient(BPELMessageContext partnerMessageContext,
            ConfigurationContext clientConfigCtx) throws AxisFault {
        AxisService anonymousService = AnonymousServiceFactory.getAnonymousService(
                partnerMessageContext.getService(), partnerMessageContext.getPort(),
                clientConfigCtx.getAxisConfiguration(), partnerMessageContext.getCaller());
        anonymousService.engageModule(clientConfigCtx.getAxisConfiguration().getModule("UEPModule"));
        anonymousService.getParent().addParameter(BPELConstants.HIDDEN_SERVICE_PARAM, "true");
        ServiceGroupContext sgc = new ServiceGroupContext(clientConfigCtx,
                (AxisServiceGroup) anonymousService.getParent());
        ServiceContext serviceCtx = sgc.getServiceContext(anonymousService);

        // get a reference to the DYNAMIC operation of the Anonymous Axis2 service
        AxisOperation axisAnonymousOperation = anonymousService.getOperation(
                partnerMessageContext.isTwoWay() ? ServiceClient.ANON_OUT_IN_OP : ServiceClient.ANON_OUT_ONLY_OP);

        Options clientOptions = cloneOptions(partnerMessageContext.getInMessageContext().getOptions());
        clientOptions.setExceptionToBeThrownOnSOAPFault(false);
        /* This value doesn't overrideend point config. */
        clientOptions.setTimeOutInMilliSeconds(60000);

        return axisAnonymousOperation.createClient(serviceCtx, clientOptions);
    }

    /**
     * Clones the given {@link org.apache.axis2.client.Options} object. This is not a deep copy
     * because this will be called for each and every message going out from synapse. The parent
     * of the cloning options object is kept as a reference.
     *
     * @param options clonning object
     * @return clonned Options object
     */
    public static Options cloneOptions(Options options) {

        // create new options object and set the parent
        Options clonedOptions = new Options(options.getParent());

        // copy general options
        clonedOptions.setCallTransportCleanup(options.isCallTransportCleanup());
        clonedOptions.setExceptionToBeThrownOnSOAPFault(options.isExceptionToBeThrownOnSOAPFault());
        clonedOptions.setManageSession(options.isManageSession());
        clonedOptions.setSoapVersionURI(options.getSoapVersionURI());
        clonedOptions.setTimeOutInMilliSeconds(options.getTimeOutInMilliSeconds());
        clonedOptions.setUseSeparateListener(options.isUseSeparateListener());

        // copy transport related options
        clonedOptions.setListener(options.getListener());
        clonedOptions.setTransportIn(options.getTransportIn());
        clonedOptions.setTransportInProtocol(options.getTransportInProtocol());
        clonedOptions.setTransportOut(clonedOptions.getTransportOut());

        // copy username and password options
        clonedOptions.setUserName(options.getUserName());
        clonedOptions.setPassword(options.getPassword());

        // cloen the property set of the current options object
        for (Object o : options.getProperties().keySet()) {
            String key = (String) o;
            clonedOptions.setProperty(key, options.getProperty(key));
        }

        return clonedOptions;
    }

    /**
     * Get the EPR of this service from the WSDL.
     *
     * @param wsdlDef     WSDL Definition
     * @param serviceName service name
     * @param portName    port name
     * @return XML representation of the EPR
     */
    public static String getEPRfromWSDL(final Definition wsdlDef, final QName serviceName, final String portName) {
        Service serviceDef = wsdlDef.getService(serviceName);
        if (serviceDef != null) {
            Port portDef = serviceDef.getPort(portName);
            if (portDef != null) {
                for (Object extElmt : portDef.getExtensibilityElements()) {
                    if (extElmt instanceof SOAPAddress) {
                        return ((SOAPAddress) extElmt).getLocationURI();
                    } else if (extElmt instanceof HTTPAddress) {
                        return ((HTTPAddress) extElmt).getLocationURI();
                    } else if (extElmt instanceof SOAP12Address) {
                        return ((SOAP12Address) extElmt).getLocationURI();
                    }
                }
            }
        }
        return null;
    }

    public static void addCustomHeadersToMessageContext(MessageContext mctx) {

        List<Header> headers = null;

        BPELServerConfiguration bpelServerConfiguration = BPELServiceComponent.getBPELServer()
                .getBpelServerConfiguration();

        if (!bpelServerConfiguration.isKeepAlive()) {

            headers = new ArrayList();
            headers.add(new Header(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE));
        }

        //Add more custom header values in the future
        if ((headers != null) && (headers.size() > 0)) {
            mctx.setProperty(HTTPConstants.HTTP_HEADERS, headers);
        }
    }
}