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