All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ow2.petals.binding.soap.listener.outgoing.SOAPCaller Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2007-2012 EBM WebSourcing, 2012-2023 Linagora
 * 
 * This program/library 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/library 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/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.binding.soap.listener.outgoing;

import static javax.jbi.messaging.NormalizedMessageProperties.PROTOCOL_HEADERS;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.jbi.messaging.MessagingException;
import javax.jbi.messaging.NormalizedMessage;
import javax.xml.namespace.QName;

import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNode;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPFault;
import org.apache.axiom.soap.SOAPFaultDetail;
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.context.MessageContext;
import org.apache.axis2.util.XMLUtils;
import org.ow2.easywsdl.extensions.wsdl4complexwsdl.api.Description;
import org.ow2.easywsdl.wsdl.api.Binding;
import org.ow2.easywsdl.wsdl.api.BindingOperation;
import org.ow2.easywsdl.wsdl.api.Endpoint;
import org.ow2.petals.binding.soap.ServiceContext;
import org.ow2.petals.binding.soap.SoapComponentContext;
import org.ow2.petals.binding.soap.SoapProvideExtFlowStepBeginLogData;
import org.ow2.petals.binding.soap.addressing.Addressing;
import org.ow2.petals.binding.soap.addressing.WSAHelper;
import org.ow2.petals.binding.soap.exception.ServiceClientPoolExhaustedException;
import org.ow2.petals.binding.soap.util.Marshaller;
import org.ow2.petals.binding.soap.util.SUPropertiesHelper;
import org.ow2.petals.commons.log.FlowAttributes;
import org.ow2.petals.commons.log.Level;
import org.ow2.petals.commons.log.PetalsExecutionContext;
import org.ow2.petals.component.framework.api.configuration.SuConfigurationParameters;
import org.ow2.petals.component.framework.api.message.Exchange;
import org.ow2.petals.component.framework.jbidescriptor.generated.Provides;
import org.ow2.petals.component.framework.logger.ProvideExtFlowStepEndLogData;
import org.ow2.petals.component.framework.logger.ProvideExtFlowStepFailureLogData;
import org.ow2.petals.component.framework.logger.StepLogHelper;
import org.ow2.petals.probes.api.enums.ExecutionStatus;
import org.ow2.petals.probes.api.exceptions.ProbeException;
import org.ow2.petals.probes.api.exceptions.ProbeKeyMissingException;
import org.ow2.petals.probes.api.exceptions.ProbeNotStartedException;
import org.ow2.petals.probes.api.exceptions.ResponseTimeCollectionStoppedException;
import org.ow2.petals.probes.api.exceptions.StartDateItemUnknownException;
import org.ow2.petals.probes.api.probes.KeyedStartDateItem;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * An external web service dispatcher.
 * 

* This dispatcher send the JBI message to an external web service. The service URL is specified in the Petals * extensions. *

* * @author Christophe Hamerling - EBM WebSourcing */ public class SOAPCaller { private final SoapComponentContext soapContext; /** * Creates a new instance of {@link SOAPCaller} * * @param soapContext */ public SOAPCaller(final SoapComponentContext soapContext) { this.soapContext = soapContext; } public void call(final Exchange exchange, final Provides provides) { final ServiceContext context = soapContext.getProvidersManager().getServiceContext(provides); // Get the incoming Normalized message final NormalizedMessage in = exchange.getInMessage(); if (in == null) { exchange.setError(new MessagingException("Message exchange must have an In normalized message")); } else { final SuConfigurationParameters cdkExtensions = context.getExtensions(); // get address to send to final Addressing addressing = WSAHelper.getAddressing(cdkExtensions); final String address = addressing.getTo(); if (address == null) { final String message = "Cannot resolve the Web service address to send the message to"; if (context.getLogger().isLoggable(Level.WARNING)) { context.getLogger().log(Level.WARNING, message); } exchange.setError(new MessagingException("BC-SOAP Exception => " + message)); return; } if (context.getLogger().isLoggable(Level.FINE)) { context.getLogger().log(Level.FINE, "Calling external Web Service : " + address); } // Get operation final QName jbiOperation = exchange.getOperation(); // Trying to determine the value of the soapAction parameter to set on the outgoing message // first : the soapAction has been set in the jbi descriptor String suSoapAction = SUPropertiesHelper.retrieveDefaultSOAPAction(cdkExtensions); // or instead, try to retrieve it from the WSDL, based on the first element of the message final String soapAction = suSoapAction == null ? retrieveSoapActionFromWsdl(exchange, context) : suSoapAction; if (context.getLogger().isLoggable(Level.FINE)) { context.getLogger().fine("jbiOperation of the received exchange: " + jbiOperation); context.getLogger().fine("soapAction of the received exchange: " + soapAction); } // create service client used to invoke WS try { // The service client options are set during its creation final ServiceClient serviceClient = soapContext.borrowServiceClient(address, jbiOperation, soapAction, exchange.getPattern(), context); // add addressing information in the options updateAddressingOptions(serviceClient.getOptions(), addressing); try { // create the in body final OMElement inBodyElement = Marshaller.createSOAPBodyContent(in, serviceClient); addHeadersToServiceClient(in, cdkExtensions, serviceClient); if (context.getLogger().isLoggable(Level.FINE)) { context.getLogger().log(Level.FINE, "OUTGOING Payload : " + inBodyElement); } final FlowAttributes prev = PetalsExecutionContext.getFlowAttributes(); final FlowAttributes fa = PetalsExecutionContext.nextFlowStepId(); this.soapContext.getComponent().logMonitTrace(exchange, new SoapProvideExtFlowStepBeginLogData( fa.getFlowInstanceId(), prev.getFlowStepId(), fa.getFlowStepId(), address)); final ServiceClientKey probeKey = new ServiceClientKey(address, jbiOperation == null ? soapAction : jbiOperation.toString(), exchange.getPattern()); KeyedStartDateItem startDateItem; try { this.soapContext.getOutgoingProbes().probeWsRequestsInvocationsCount.incPending(probeKey); startDateItem = this.soapContext.getOutgoingProbes().probeWsClientInvocationsResponseTime .newExecution(probeKey); } catch (final ProbeException e) { context.getLogger().log(Level.WARNING, "The WS probes seems to have a problem.", e); startDateItem = null; } final boolean succeeded; try { if (exchange.isInOnlyPattern()) { // send as InOnly message: we use sendRobust because if an error happen // on the server side, we still want to know about it (i.e. a not Done exchange status) // sendRobust is NOT JBI RobustInOnly. JBI RobustInOnly is more like a SOAP InOut. // also, we use sendRobust because we want to block until the end of the send before // returning the serviceClient! serviceClient.sendRobust(jbiOperation, inBodyElement); succeeded = true; } else { final MessageContext response = ((PetalsServiceClient) serviceClient) .sendReceive2(jbiOperation, inBodyElement); // in RobustInOnly, if we didn't receive a fault, then we don't care if (exchange.isRobustInOnlyPattern() && (response == null || !response.isFault())) { succeeded = true; } else { succeeded = processResponse(exchange, cdkExtensions, context, probeKey, startDateItem, response, fa); } } // if not, handled in processResponse if (succeeded) { this.soapContext.getComponent().logMonitTrace(exchange, new ProvideExtFlowStepEndLogData(fa.getFlowInstanceId(), fa.getFlowStepId())); try { this.soapContext.getOutgoingProbes().probeWsRequestsInvocationsCount.move(probeKey, ExecutionStatus.SUCCEEDED); if (startDateItem != null) { this.soapContext.getOutgoingProbes().probeWsClientInvocationsResponseTime .endsExecution(startDateItem, ExecutionStatus.SUCCEEDED); } } catch (final ProbeException e) { context.getLogger().log(Level.WARNING, "The WS probes seems to have a problem.", e); } } } catch (final AxisFault e) { this.soapContext.getComponent() .logMonitTrace(exchange, new ProvideExtFlowStepFailureLogData(fa.getFlowInstanceId(), fa.getFlowStepId(), String.format(StepLogHelper.TECHNICAL_ERROR_MESSAGE_PATTERN, e.getMessage()))); try { this.soapContext.getOutgoingProbes().probeWsRequestsInvocationsCount.move(probeKey, ExecutionStatus.ERROR); if (startDateItem != null) { this.soapContext.getOutgoingProbes().probeWsClientInvocationsResponseTime .endsExecution(startDateItem, ExecutionStatus.ERROR); } } catch (final ProbeException e1) { context.getLogger().log(Level.WARNING, "The WS probes seems to have a problem.", e1); } throw e; } } finally { try { soapContext.returnServiceClient(serviceClient); } catch (final MessagingException e) { // let's not throw an exception in a finally, that's dangerous if (context.getLogger().isLoggable(Level.WARNING)) { context.getLogger().log(Level.WARNING, "Can't return the service client to the pool", e); } } } } catch (final ServiceClientPoolExhaustedException e) { if (context.getLogger().isLoggable(Level.WARNING)) { context.getLogger().log(Level.WARNING, e.getMessage()); } exchange.setError(new MessagingException(e.getMessage())); } catch (final Exception e) { if (context.getLogger().isLoggable(Level.WARNING)) { context.getLogger().log(Level.WARNING, "Exception on the WS invocation", e); } exchange.setError(e); } } } /** * Update the client properties * * @param options * @param addressing */ protected void updateAddressingOptions(final Options options, final Addressing addressing) { // update the WS-Addressing properties. No need to update the wsa:To // since this value has been used to create the client from the pool // factory. if (addressing.getFaultTo() != null) { options.setFaultTo(new EndpointReference(addressing.getFaultTo())); } if (addressing.getFrom() != null) { options.setFrom(new EndpointReference(addressing.getFrom())); } if (addressing.getReplyTo() != null) { options.setReplyTo(new EndpointReference(addressing.getReplyTo())); } } private boolean processResponse(final Exchange exchange, final SuConfigurationParameters cdkExtensions, final ServiceContext context, final ServiceClientKey probeKey, final KeyedStartDateItem startDateItem, final MessageContext response, final FlowAttributes fa) throws MessagingException, ProbeKeyMissingException, ProbeNotStartedException, ResponseTimeCollectionStoppedException, StartDateItemUnknownException { final boolean succeeded; final SOAPBody body = response.getEnvelope().getBody(); // if msg exchange required a response, set it if (body != null) { if (response.isFault()) { assert body.getFault() != null; final SOAPFault soapFault = body.getFault(); final SOAPFaultDetail soapFaultDetails = soapFault.getDetail(); if (soapFaultDetails != null) { final OMElement firstDetailFaultElt = soapFault.getDetail().getFirstElement(); if (firstDetailFaultElt == null) { // Technical error context.getLogger().log(Level.FINE, "RESPONSE is a SOAP Fault (Technical error)."); Marshaller.fillJBITechnicalError(soapFault, exchange); this.soapContext.getComponent().logMonitTrace(exchange, new ProvideExtFlowStepFailureLogData(fa.getFlowInstanceId(), fa.getFlowStepId(), String.format(StepLogHelper.TECHNICAL_ERROR_MESSAGE_PATTERN, exchange.getError().getMessage()))); try { this.soapContext.getOutgoingProbes().probeWsRequestsInvocationsCount.move(probeKey, ExecutionStatus.ERROR); if (startDateItem != null) { this.soapContext.getOutgoingProbes().probeWsClientInvocationsResponseTime .endsExecution(startDateItem, ExecutionStatus.ERROR); } } catch (final ProbeException e) { context.getLogger().log(Level.WARNING, "The WS probes seems to have a problem.", e); } } else if (firstDetailFaultElt.getType() == OMNode.ELEMENT_NODE) { // Business fault context.getLogger().log(Level.FINE, "RESPONSE is a SOAP Fault (Business error)."); this.soapContext.getComponent().logMonitTrace(exchange, new ProvideExtFlowStepFailureLogData( fa.getFlowInstanceId(), fa.getFlowStepId(), StepLogHelper.BUSINESS_ERROR_MESSAGE)); try { this.soapContext.getOutgoingProbes().probeWsRequestsInvocationsCount.move(probeKey, ExecutionStatus.FAULT); if (startDateItem != null) { this.soapContext.getOutgoingProbes().probeWsClientInvocationsResponseTime .endsExecution(startDateItem, ExecutionStatus.FAULT); } } catch (final ProbeException e) { context.getLogger().log(Level.WARNING, "The WS probes seems to have a problem.", e); } Marshaller.fillJBIBusinessFault(firstDetailFaultElt, exchange); } else { throw new MessagingException("Unsupported fault type: " + soapFault.getDetail().getType()); } } else { // Technical error context.getLogger().log(Level.FINE, "RESPONSE is a SOAP Fault (Technical error without details)."); Marshaller.fillJBITechnicalError(soapFault, exchange); this.soapContext.getComponent().logMonitTrace(exchange, new ProvideExtFlowStepFailureLogData(fa.getFlowInstanceId(), fa.getFlowStepId(), String.format(StepLogHelper.TECHNICAL_ERROR_MESSAGE_PATTERN, exchange.getError().getMessage()))); try { this.soapContext.getOutgoingProbes().probeWsRequestsInvocationsCount.move(probeKey, ExecutionStatus.ERROR); if (startDateItem != null) { this.soapContext.getOutgoingProbes().probeWsClientInvocationsResponseTime .endsExecution(startDateItem, ExecutionStatus.ERROR); } } catch (final ProbeException e) { context.getLogger().log(Level.WARNING, "The WS probes seems to have a problem.", e); } } succeeded = false; } else { Marshaller.fillJBIMessage(response, exchange.getOutMessage(), SUPropertiesHelper.isAxis1CompatibilityEnabled(cdkExtensions), context.getLogger()); succeeded = true; } } else { // TODO check that it is an optional in out? context.getLogger().log(Level.FINE, "RESPONSE Payload : No response."); succeeded = true; } return succeeded; } private void silentAddHeader(final DocumentFragment header, final ServiceClient serviceClient) { final Node node = header.getFirstChild(); if (node instanceof Element) { try { serviceClient.addHeader(XMLUtils.toOM((Element) node)); } catch (final Exception t) { soapContext.getLogger().log(Level.WARNING, "Can't parse the custom additional header and add it to be sent over SOAP", t); } } } /** * Retrieve data from JBI properties wich will be set into the SOAP header. * * @param nm * @return */ @SuppressWarnings("unchecked") private void addHeadersToServiceClient(final NormalizedMessage nm, final SuConfigurationParameters cdkExtensions, final ServiceClient serviceClient) { final List filters = SUPropertiesHelper.retrieveHeaderList(cdkExtensions); // 1. get the properties defined in the SU from the filter value if (filters != null && !filters.isEmpty()) { final Set properties = new HashSet(); final Set names = nm.getPropertyNames(); for (final Object object : names) { if (object instanceof String) { final String propertyName = (String) object; if (isFilteredValue(propertyName, filters)) { properties.add(propertyName); } } } for (final String propertyName : properties) { final Object property = nm.getProperty(propertyName); // TODO : Inject not only document fragments... if (property instanceof DocumentFragment) { silentAddHeader((DocumentFragment) property, serviceClient); } } } // 2. additional headers if (SUPropertiesHelper.retrieveInjectHeader(cdkExtensions)) { // add the protocol headers (the ones injected in consumer mode) final Object protocolHeadersPropertyObject = nm.getProperty(PROTOCOL_HEADERS); if (protocolHeadersPropertyObject != null && protocolHeadersPropertyObject instanceof Map) { for (final DocumentFragment df : ((Map) protocolHeadersPropertyObject) .values()) { silentAddHeader(df, serviceClient); } } for (final DocumentFragment h : SUPropertiesHelper.retrieveHeaderToInject(cdkExtensions, soapContext.getLogger())) { silentAddHeader(h, serviceClient); } } } /** * @param propertyName * @param filters * @return */ protected boolean isFilteredValue(final String propertyName, final List filters) { final boolean result = false; for (final String filter : filters) { if (propertyName.equalsIgnoreCase(filter)) { return true; } if (filter.endsWith("*")) { final String tmp = filter.substring(0, filter.lastIndexOf("*")); if (propertyName.equals(tmp) || propertyName.startsWith(tmp)) { return true; } } } return result; } private String retrieveSoapActionFromWsdl(final Exchange exchange, final ServiceContext context) { String soapAction = null; final String endpointName = exchange.getEndpointName(); final QName service = exchange.getEndpoint().getServiceName(); final Description d = context.getServiceDescription(); final Endpoint e = d.getService(service).getEndpoint(endpointName); if (e != null) { final Binding b = e.getBinding(); if (b != null) { final BindingOperation bo = b.getBindingOperation(exchange.getOperationName()); if (bo != null) { soapAction = bo.getSoapAction(); // See PETALSBCSOAP-151 if (soapAction == null || soapAction.isEmpty()) { // it must contain the characters for an empty string, if not // org.apache.axis2.transport.http.CommonsHTTPTransportSender.findSOAPAction(MessageContext) // won't be happy with it! soapAction = "\"\""; } } } } return soapAction; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy