/** * Copyright (c) 2015 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * <p> * 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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.bpmn.extensions.soap; import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.delegate.Expression; import org.activiti.engine.delegate.JavaDelegate; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.util.AXIOMUtil; import org.apache.axiom.soap.SOAP11Constants; import org.apache.axiom.soap.SOAP12Constants; import org.apache.axis2.AxisFault; import org.apache.axis2.addressing.EndpointReference; import org.apache.axis2.client.Options; import org.apache.axis2.client.ServiceClient; import org.apache.axis2.transport.http.HTTPConstants; import org.apache.commons.httpclient.Header; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.bpmn.extensions.internal.BPMNExtensionsComponent; import org.wso2.carbon.registry.api.Registry; import org.wso2.carbon.registry.api.RegistryException; import org.wso2.carbon.registry.api.Resource; import org.wso2.carbon.unifiedendpoint.core.UnifiedEndpoint; import org.wso2.carbon.unifiedendpoint.core.UnifiedEndpointFactory; import javax.xml.stream.XMLStreamException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; /** * Provides SOAP service invocation support within BPMN processes. * Following fields is required when using the SOAP task : (Both string values and expressions can be used) * serviceURL : Endpoint url of the partner service * payload : Request payload which is attached to the SOAP body when creating the SOAP request * outputVariable : Name of the variable to save response * soapVersion : Soap version to be used when creating the SOAP request i.e. soap11 or soap12 * headers : SOAP header block which is attached to the SOAP header when creating the SOAP request * transportHeaders : additional transport header values in the format "headerName1:headerValue1,headerName2:headerValue2" * httpConnection : Control options for the current connection. Connection: keep-alive (set as the default value) * soapAction : Indicate the intent of the SOAP HTTP request * httpTransferEncoding : The form of encoding used to safely transfer the entity to the user. * Transfer-Encoding: chunked (set as the default value) * Example : * <serviceTask id="servicetask3" name="SOAP Task" * activiti:class="org.wso2.carbon.bpmn.extensions.soap.SOAPTask" * activiti:extensionId="org.wso2.bps.tooling.bpmn.extensions.soapTask.SOAPTask"> * <extensionElements> * <activiti:field name="serviceURL"> * <activiti:expression>${serviceURL eg: http://localhost:9763/services/HelloService }</activiti:expression> * </activiti:field> * <activiti:field name="payload"> * <activiti:expression>${input-payload}</activiti:expression> * </activiti:field> * <activiti:field name="soapVersion"> * <activiti:string>soap11</activiti:string> * </activiti:field> * <activiti:field name="httpTransferEncoding"> * <activiti:expression>${httpTransferEncoding}</activiti:expression> * </activiti:field> * <activiti:field name="outputVariable"> * <activiti:string>output</activiti:string> * </activiti:field> * <activiti:field name="headers"> * <activiti:string><ns1:hello xmlns:ns1='http://ode/bpel/unit-test.wsdl'> <TestPart>HEADER11</TestPart></ns1:hello></activiti:string> * </activiti:field> * <activiti:field name="httpConnection"> * <activiti:string>keep-alive</activiti:string> * </activiti:field> * <activiti:field name="transportHeaders"> * <activiti:string>Pragma: no-cache,Cache-Control: no-cache</activiti:string> * </activiti:field> * <activiti:field name="soapAction"> * <activiti:string>urn:sayHello</activiti:string> * </activiti:field> * </extensionElements> * </serviceTask> */ public class SOAPTask implements JavaDelegate { private static final Log log = LogFactory.getLog(SOAPTask.class); /** * Soap version numbers */ private static final String GOVERNANCE_REGISTRY_PREFIX = "gov:/"; private static final String CONFIGURATION_REGISTRY_PREFIX = "conf:/"; private static final String REST_INVOKE_ERROR = "REST_CLIENT_INVOKE_ERROR"; private static final String SOAP12_VERSION = "soap12"; private static final String SOAP11_VERSION = "soap11"; private static final String SOAP_INVOKE_ERROR_CODE = "SOAP_CLIENT_INVOKE_ERROR"; private Expression serviceURL; private Expression serviceRef; private Expression payload; private Expression headers; private Expression soapVersion; private Expression httpConnection; private Expression httpTransferEncoding; private Expression outputVariable; private Expression transportHeaders; private Expression soapAction; @Override public void execute(DelegateExecution execution) { String endpointURL; String payloadRequest; String version; String connection; String transferEncoding; String transportHeaderList[]; String action = ""; String soapVersionURI = SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI; List<Header> headerList = new ArrayList<Header>(); try { if (serviceURL != null) { endpointURL = serviceURL.getValue(execution).toString(); } else if (serviceRef != null) { String resourcePath = serviceRef.getValue(execution).toString(); String registryPath; String tenantId = execution.getTenantId(); Registry registry; if (resourcePath.startsWith(GOVERNANCE_REGISTRY_PREFIX)) { registryPath = resourcePath.substring(GOVERNANCE_REGISTRY_PREFIX.length()); registry = BPMNExtensionsComponent.getRegistryService().getGovernanceSystemRegistry( Integer.parseInt(tenantId)); } else if (resourcePath.startsWith(CONFIGURATION_REGISTRY_PREFIX)) { registryPath = resourcePath.substring(CONFIGURATION_REGISTRY_PREFIX.length()); registry = BPMNExtensionsComponent.getRegistryService().getConfigSystemRegistry( Integer.parseInt(tenantId)); } else { String msg = "Registry type is not specified for service reference in " + " serviceRef should begin with gov:/ or conf:/ to indicate the registry type."; throw new SOAPException(SOAP_INVOKE_ERROR_CODE , msg); } if (log.isDebugEnabled()) { log.debug("Reading endpoint from registry location: " + registryPath + " for task " + execution.getCurrentActivityName()); } Resource urlResource = registry.get(registryPath); if (urlResource != null) { String uepContent = new String((byte[]) urlResource.getContent(), Charset.defaultCharset()); UnifiedEndpointFactory uepFactory = new UnifiedEndpointFactory(); OMElement uepElement = AXIOMUtil.stringToOM(uepContent); UnifiedEndpoint uep = uepFactory.createEndpoint(uepElement); endpointURL = uep.getAddress(); } else { String errorMsg = "Endpoint resource " + registryPath + " is not found. Failed to execute REST invocation in task " + execution.getCurrentActivityName(); throw new SOAPException(SOAP_INVOKE_ERROR_CODE, errorMsg); } } else { String urlNotFoundErrorMsg = "Service URL is not provided. serviceURL must be provided."; throw new SOAPException(SOAP_INVOKE_ERROR_CODE, urlNotFoundErrorMsg); } if (payload != null) { payloadRequest = payload.getValue(execution).toString(); } else { String payloadNotFoundErrorMsg = "Payload request is not provided. Payload must be provided."; throw new SOAPException(SOAP_INVOKE_ERROR_CODE, payloadNotFoundErrorMsg); } if (soapVersion != null) { version = soapVersion.getValue(execution).toString(); if (version.equalsIgnoreCase(SOAP12_VERSION)) { soapVersionURI = SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI; } else if (version.equalsIgnoreCase(SOAP11_VERSION)) { soapVersionURI = SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI; } else { String invalidVersionErrorMsg = "Invalid soap version string specified"; throw new SOAPException(SOAP_INVOKE_ERROR_CODE, invalidVersionErrorMsg); } } //Adding the connection Header connectionHeader = new Header(); if (httpConnection != null) { connection = httpConnection.getValue(execution).toString(); if (connection != null && !connection.trim().equals("Keep-Alive")) { log.debug("Setting Keep-Alive header "); connectionHeader.setName("Connection"); connectionHeader.setValue(connection); headerList.add(connectionHeader); } } //Adding the additional transport headers if (transportHeaders != null) { String headerContent = transportHeaders.getValue(execution).toString(); if (headerContent != null) { transportHeaderList = headerContent.split(","); for (String transportHeader : transportHeaderList) { String pair[] = transportHeader.split(":"); Header additionalHeader = new Header(); if (pair.length == 1) { additionalHeader.setName(pair[0]); additionalHeader.setValue(""); if (log.isDebugEnabled()) { log.debug("Adding transport headers " + pair[0]); } } else { additionalHeader.setName(pair[0]); additionalHeader.setValue(pair[1]); if (log.isDebugEnabled()) { log.debug("Adding transport headers " + pair[0] + " " + pair[1] ); } } headerList.add(additionalHeader); } } } //Adding the soap action if (soapAction != null) { action = soapAction.getValue(execution).toString(); if (log.isDebugEnabled()) { log.debug("Setting soap action " + soapAction); } } //Converting the payload to an OMElement OMElement payLoad = AXIOMUtil.stringToOM(payloadRequest); //Creating the Service client ServiceClient sender = new ServiceClient(); OMElement response; //Creating options to set the headers Options options = new Options(); options.setTo(new EndpointReference(endpointURL)); options.setAction(action); options.setSoapVersionURI(soapVersionURI); options.setProperty(org.apache.axis2.transport.http.HTTPConstants.HTTP_HEADERS, headerList); //Adding the soap header block to the SOAP Header block when creating the SOAP Envelope if (headers != null) { String headerContent = headers.getValue(execution).toString(); OMElement headerElement = AXIOMUtil.stringToOM(headerContent); sender.addHeader(headerElement); if (log.isDebugEnabled()) { log.debug("Adding soap header " + headerContent); } } //Adding the transfer encoding if (httpTransferEncoding != null) { transferEncoding = httpTransferEncoding.getValue(execution).toString(); if (transferEncoding.equalsIgnoreCase("chunked")) { options.setProperty(HTTPConstants.CHUNKED, Boolean.TRUE); if (log.isDebugEnabled()) { log.debug("Enabling transfer encoding chunked "); } } else { options.setProperty(HTTPConstants.CHUNKED, Boolean.FALSE); if (log.isDebugEnabled()) { log.debug("Disabling transfer encoding chunked "); } } } sender.setOptions(options); //Invoking the endpoint response = sender.sendReceive(payLoad); //Getting the response as a string String responseStr = response.toStringWithConsume(); if (outputVariable != null) { String outVarName = outputVariable.getValue(execution).toString(); execution.setVariableLocal(outVarName, responseStr); } else { String outputNotFoundErrorMsg = "Output variable is not provided. " + "outputVariable must be provided to save " + "the response."; throw new SOAPException(SOAP_INVOKE_ERROR_CODE, outputNotFoundErrorMsg); } } catch (AxisFault axisFault) { log.error("Axis2 Fault", axisFault); throw new SOAPException(SOAP_INVOKE_ERROR_CODE, "Exception while getting response :" + axisFault.getMessage()); } catch (XMLStreamException | RegistryException e) { log.error("Exception in processing", e); throw new SOAPException(SOAP_INVOKE_ERROR_CODE, "Exception in processing :" + e.getMessage()); } } public void setServiceURL(Expression serviceURL) { this.serviceURL = serviceURL; } public void setServiceRef(Expression serviceRef) { this.serviceRef = serviceRef; } public void setPayload(Expression payload) { this.payload = payload; } public void setHeaders(Expression headers) { this.headers = headers; } public void setSoapVersion(Expression soapVersion) { this.soapVersion = soapVersion; } public void setHttpConnection(Expression httpConnection) { this.httpConnection = httpConnection; } public void setOutputVariable(Expression outputVariable) { this.outputVariable = outputVariable; } public void setHttpTransferEncoding(Expression httpTransferEncoding) { this.httpTransferEncoding = httpTransferEncoding; } public void setSoapAction(Expression soapAction) { this.soapAction = soapAction; } public void setTransportHeaders(Expression transportHeaders) { this.transportHeaders = transportHeaders; } }