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

org.ow2.petals.binding.soap.listener.incoming.PetalsReceiver Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2010-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.incoming;

import static org.ow2.petals.binding.soap.SoapConstants.Axis2.SOAP_EXTERNAL_LISTENER_SERVICE_PARAM;
import static org.ow2.petals.binding.soap.SoapConstants.SOAP.FAULT_SERVER;

import java.util.Optional;

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

import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.engine.AxisEngine;
import org.apache.axis2.receivers.AbstractInOutMessageReceiver;
import org.apache.axis2.receivers.AbstractMessageReceiver;
import org.apache.axis2.util.MessageContextBuilder;
import org.ow2.easywsdl.wsdl.api.abstractItf.AbsItfOperation.MEPPatternConstants;
import org.ow2.petals.binding.soap.ServiceContext;
import org.ow2.petals.binding.soap.SoapComponentContext;
import org.ow2.petals.binding.soap.SoapConstants;
import org.ow2.petals.binding.soap.listener.incoming.jetty.IncomingServiceKey;
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.Message.MEPConstants;
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.Consumes;
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.ProbeNotStartedException;
import org.ow2.petals.probes.api.exceptions.ResponseTimeCollectionStoppedException;
import org.ow2.petals.probes.api.exceptions.StartDateItemLostException;
import org.ow2.petals.probes.api.probes.KeyedStartDateItem;

/**
 * The message receiver used by the binding component.
 * 

* The SOAP message is forwarded to the JBI endpoint. *

* * We cannot simply extend {@link AbstractInOutMessageReceiver} because we need to catch send errors from Axis and * sometimes we don't even need to send back an answer (for InOnly) * * @author Nicolas Oddoux - EBM WebSourcing */ public class PetalsReceiver extends AbstractMessageReceiver { // this is not really used as an exception for knowing where it happened // we can thus reuse it and avoid the overhead of creating the exception public static final AxisFault TIMEOUT_EXCEPTION = new AxisFault( "A timeout happened while the BC SOAP sent an exchange to a JBI service", FAULT_SERVER); static { TIMEOUT_EXCEPTION.setStackTrace(new StackTraceElement[0]); } private final SoapComponentContext soapContext; /** * @param soapContext */ public PetalsReceiver(final SoapComponentContext soapContext) { super(); this.soapContext = soapContext; } @Override public final void invokeBusinessLogic(final MessageContext msgContext) throws AxisFault { @SuppressWarnings("unchecked") final ServiceContext context = (ServiceContext) msgContext.getAxisService() .getParameter(SoapConstants.Axis2.CONSUMES_SERVICE_CONTEXT_PARAM).getValue(); try { // send message to JBI endpoint process(msgContext, context); } catch (final Exception af) { // Update service invocation probe this.updateIncomingProbes(msgContext, context, ExecutionStatus.ERROR); final Optional flowTracingActivationState = (Optional) msgContext .getProperty(SoapConstants.MESSAGE_CONTEXT_FLOW_TRACING_ACTIVATION_STATE); assert flowTracingActivationState != null; this.soapContext.getComponent().logMonitTrace(flowTracingActivationState, context .getConfig(), StepLogHelper.getMonitExtFailureTrace(PetalsExecutionContext.getFlowAttributes(), af, true)); throw af; } } /** * Process the request. Sends the SOAP Envelop of the request message directly to a JBI service and operation on the * JBI container. The response message from the JBI service invocation is transmitted as an outgoing message. * * @param context2 * * @param the * in message context * @return the out message context * @throws AxisFault * the axis fault */ private void process(final MessageContext messageContext, final ServiceContext context) throws AxisFault { if (context.getLogger().isLoggable(Level.FINE)) { context.getLogger().log(Level.FINE, "Processing the incoming SOAP message"); } // Type of service has been checked before final AxisService axisService = messageContext.getAxisService(); final AxisOperation axisOperation = messageContext.getAxisOperation(); if (context.getLogger().isLoggable(Level.FINEST)) { context.getLogger().log(Level.FINEST, "soapAction found in the incoming message : " + axisOperation.getSoapAction()); } // JBI operation = Axis operation name final QName jbiOperation = axisOperation.getName(); try { final String wsPath = (String) messageContext .getProperty(SoapConstants.MESSAGE_CONTEXT_INCOMINGSERVICEKEY_WS_PATH); final String clientIpAddress = (String) messageContext .getProperty(SoapConstants.MESSAGE_CONTEXT_INCOMINGSERVICEKEY_CLIENT_IP_ADDRESS); final IncomingServiceKey serviceInvocationKey = new IncomingServiceKey(wsPath, axisOperation.getName().toString(), clientIpAddress); messageContext .setProperty( org.ow2.petals.binding.soap.SoapConstants.MESSAGE_CONTEXT_INCOMINGSERVICEKEY_NAME, serviceInvocationKey); soapContext.getIncomingProbes().probeHttpRequestsInvocationsCount.incPending(serviceInvocationKey); try { final KeyedStartDateItem responseTime = soapContext .getIncomingProbes().probeHttpRequestsInvocationsResponseTime .newExecution(serviceInvocationKey); messageContext.setProperty(org.ow2.petals.binding.soap.SoapConstants.MESSAGE_CONTEXT_RESPONSETIME_NAME, responseTime); } catch (final ResponseTimeCollectionStoppedException e) { // This exception should not occur because the HTTP server // should be stopped in the same time than the probes context.getLogger().warning("A response times collection is stopped."); } catch (final StartDateItemLostException e) { context.getLogger() .warning("A response time is lost because it can't be added to the internal list."); } } catch (final ProbeNotStartedException e) { context.getLogger() .warning("HTTP probes are not started. Values of probes could be incorrect."); } // JBI MEP compute from the Axis MEP defined at AxisServicesHelper.getWSDL2Mep(...) final int axisMep = axisOperation.getAxisSpecificMEPConstant(); final MEPPatternConstants mep; if (axisMep == org.apache.axis2.wsdl.WSDLConstants.MEP_CONSTANT_IN_OUT) { mep = MEPConstants.IN_OUT_PATTERN; } else if (axisMep == org.apache.axis2.wsdl.WSDLConstants.MEP_CONSTANT_ROBUST_IN_ONLY) { mep = MEPConstants.ROBUST_IN_ONLY_PATTERN; } else if (axisMep == org.apache.axis2.wsdl.WSDLConstants.MEP_CONSTANT_IN_ONLY) { mep = MEPConstants.IN_ONLY_PATTERN; } else { context.getLogger().warning(String.format("Axis MEP not managed: [%d]. We use the default MEP.", axisMep)); mep = null; } final SoapExternalListener soapExternalListener = (SoapExternalListener) axisService .getParameter(SOAP_EXTERNAL_LISTENER_SERVICE_PARAM).getValue(); final Consumes consumes = context.getConfig(); final SuConfigurationParameters consumesExtensions = context.getExtensions(); final Exchange exchange; try { // create the message exchange exchange = this.createMessageExchange(messageContext, jbiOperation, mep, soapExternalListener, consumes, consumesExtensions); } catch (final MessagingException me) { final String errorMsg = "Error while transforming SOAP request to JBI MessageExchange"; context.getLogger().log(Level.SEVERE, errorMsg, me); throw new AxisFault(errorMsg, FAULT_SERVER, me); } // send the message exchange through JBI // As the SOAP/HTTP request may wait for a response, final SOAPFactory soapFactory = this.getSOAPFactory(messageContext); sendJBIMessage(messageContext, soapFactory, exchange, soapExternalListener, context); } /** * Create the JBI message exchange * * @param inContext * the SOAP message * @param operation * the operation * @param mep * the MEP * @param soapExternalListener * the SOAP external listener * @param consumes * the consumes * @param consumesExtensions * the consume extensions * * @return the JBI message exchange * * @throws MessagingException */ private final Exchange createMessageExchange(final MessageContext inContext, final QName operation, final MEPPatternConstants mep, final SoapExternalListener soapExternalListener, final Consumes consumes, final SuConfigurationParameters consumesExtensions) throws MessagingException { // create the message exchange from the extensions. final Optional flowTracingActivationState = (Optional) inContext .getProperty(SoapConstants.MESSAGE_CONTEXT_FLOW_TRACING_ACTIVATION_STATE); assert flowTracingActivationState != null; final Exchange msgExchange = soapExternalListener.createExchange(consumes, mep, flowTracingActivationState); msgExchange.setOperation(operation); Marshaller.fillJBIMessage(inContext, msgExchange.getInMessage(), SUPropertiesHelper.isAxis1CompatibilityEnabled(consumesExtensions), soapExternalListener.getLogger()); return msgExchange; } /** * Put the {@link NormalizedMessage} attachments in the output Axis2 {@link MessageContext}. * * @param soapFactory * the SOAP factory * @param nm * the normalized message * @param outMessage * the SOAP message * @param context * @throws AxisFault */ private final void handleResponseAttachments(final SOAPFactory soapFactory, final NormalizedMessage nm, final MessageContext outMessage, final ServiceContext context) throws AxisFault { // process the output message with its attachments Marshaller.fillSOAPBodyWithAttachments(nm, soapFactory, outMessage); final SOAPEnvelope env = outMessage.getEnvelope(); if (env != null && context.getLogger().isLoggable(Level.FINE)) { context.getLogger().log(Level.FINE, "SOAPENVELOPE AFTER Attachment handling"); context.getLogger().log(Level.FINE, outMessage.getEnvelope().toString()); } } /** * Process the JBI response.
* According to the response status : *
    *
  • set outContext to null for DONE
  • *
  • throws an AxisFault for ERROR
  • *
  • set outContext body to a FAULT
  • *
  • set outContext body to a response
  • *
* * @param exchange * JBI response * @param inMessage * the SOAP message * @param factory * soap factory used to create body response * @param soapExternalListener * the SOAP external listener * @param context * @return a FAULT or OUT JBI response or null if no response * @throws AxisFault * with ERROR JBI status, or problem while creating response */ private void processJBIResponse(final Exchange exchange, final MessageContext inMessage, final SOAPFactory factory, SoapExternalListener soapExternalListener, final ServiceContext context) throws AxisFault { if (exchange.isDoneStatus()) { // exchange DONE, nothing to do, no soap response if (context.getLogger().isLoggable(Level.FINE)) { context.getLogger().log(Level.FINE, "Receive a Done status response"); } final FlowAttributes flowAttributes = PetalsExecutionContext.getFlowAttributes(); this.soapContext.getComponent().logMonitTrace(context.getConfig(), StepLogHelper.getMonitExtEndOrFailureTrace(exchange.getMessageExchange(), flowAttributes, true)); // Update service invocation probe this.updateIncomingProbes(inMessage, context, ExecutionStatus.SUCCEEDED); } else if (exchange.isErrorStatus()) { // exchange ERROR: throws an axis fault if (context.getLogger().isLoggable(Level.FINE)) { context.getLogger().log(Level.FINE, "Receive an Error status response"); } // we can't just use the exception message because it could contain the whole stacktrace (because of how // MessagingException are transformed to something classpath-safe). // the probe will be updated in the catch clause of invokeBusinessLogic throw new AxisFault("Error in the JBI exchange", FAULT_SERVER, exchange.getError()); } else if (exchange.isActiveStatus()) { if (context.getLogger().isLoggable(Level.FINE)) { context.getLogger().log(Level.FINE, "Receive an Active status response"); } final MessageContext outMessage = MessageContextBuilder.createOutMessageContext(inMessage); outMessage.getOperationContext().addMessageContext(outMessage); if (exchange.getFault() != null) { final SOAPEnvelope envelope = Marshaller.createSOAPEnvelope(factory, exchange.getFault(), context.getLogger()); outMessage.setEnvelope(envelope); } else { // Get the output message if the message is an instance of out final NormalizedMessage nm = exchange.getOutMessage(); if (nm == null) { final String errorMsg = "The MEP '" + exchange.getPattern() + "' does not accept a null response"; if (context.getLogger().isLoggable(Level.WARNING)) { context.getLogger().log(Level.WARNING, errorMsg); } // the probe will be updated in the catch clause of invokeBusinessLogic throw new AxisFault(errorMsg, FAULT_SERVER); } else { final SOAPEnvelope envelope = Marshaller.createSOAPEnvelope(factory, nm, context.getLogger()); outMessage.setEnvelope(envelope); handleResponseAttachments(factory, nm, outMessage, context); } } try { // this is what AbstractInOutMessageReceiver does! replicateState(outMessage); // send back the response to the WS consumer AxisEngine.send(outMessage); } catch (final AxisFault e) { // no need to throw it, the answer won't be returned to the client anyway if this failed exchange.setError(e); } final ExecutionStatus status; if (exchange.isErrorStatus()) { status = ExecutionStatus.ERROR; } else if (exchange.getFault() != null) { status = ExecutionStatus.FAULT; } else { status = ExecutionStatus.SUCCEEDED; } this.updateIncomingProbes(inMessage, context, status); // let's log the end or failure trace (after we successfully, or not, sent back the answer) final FlowAttributes flowAttributes = PetalsExecutionContext.getFlowAttributes(); final Optional flowTracingActivationState = (Optional) inMessage .getProperty(SoapConstants.MESSAGE_CONTEXT_FLOW_TRACING_ACTIVATION_STATE); assert flowTracingActivationState != null; this.soapContext.getComponent().logMonitTrace(flowTracingActivationState, context .getConfig(), StepLogHelper.getMonitExtEndOrFailureTrace(exchange.getMessageExchange(), flowAttributes, true)); // the rest of the method should not throw AxisFault // (because it will result in incoming probes be updated again in invokeBusinessLogic) try { // let's acknowledge the JBI message // test in case it was set to error by the axis send before! if (exchange.isActiveStatus()) { exchange.setStatus(ExchangeStatus.DONE); } soapExternalListener.send(exchange); } catch (final MessagingException e) { context.getLogger().log(Level.WARNING, "Error while acknowledging the JBI MessageExchange.", e); } } else { throw new AxisFault( SoapConstants.SOAP.ERROR_WRONG_MESSAGE_STATUS + " " + exchange.getStatus().toString(), FAULT_SERVER); } } private void updateIncomingProbes(final MessageContext inMessage, final ServiceContext context, final ExecutionStatus executionStatus) { try { final IncomingServiceKey currentServiceKey = (IncomingServiceKey) inMessage .getProperty(SoapConstants.MESSAGE_CONTEXT_INCOMINGSERVICEKEY_NAME); soapContext.getIncomingProbes().probeHttpRequestsInvocationsCount.move(currentServiceKey, executionStatus); @SuppressWarnings("unchecked") final KeyedStartDateItem responseTime = (KeyedStartDateItem) inMessage .getProperty(SoapConstants.MESSAGE_CONTEXT_RESPONSETIME_NAME); soapContext.getIncomingProbes().probeHttpRequestsInvocationsResponseTime.endsExecution(responseTime, executionStatus); } catch (final ProbeException e) { // The request processing should not be interrupted if an error occurs during the probe update. context.getLogger().log(Level.WARNING, "Error updating probes", e); } } /** * Send synchronously the JBI message * * @param inContext * the message context * @param factory * the SOAP factory * @param exchange * the message exchange * @param soapExternalListener * the SOAP external listener * @param context * @return the message context with the response * * @throws AxisFault */ private void sendJBIMessage(final MessageContext inContext, final SOAPFactory factory, Exchange exchange, final SoapExternalListener soapExternalListener, final ServiceContext context) throws AxisFault { boolean noTimeout = true; try { // TODO move to async sending... noTimeout = soapExternalListener.sendSync(exchange); } catch (final MessagingException e) { final String errorMsg = "Error while sending JBI exchange with id '" + exchange.getExchangeId() + "'"; if (context.getLogger().isLoggable(Level.WARNING)) { context.getLogger().log(Level.WARNING, errorMsg, e); } throw new AxisFault(errorMsg, FAULT_SERVER, e); } if (!noTimeout) { final String errorMsg = "A timeout occurs on JBI exchange with id '" + exchange.getExchangeId() + "'"; if (context.getLogger().isLoggable(Level.WARNING)) { context.getLogger().log(Level.WARNING, errorMsg); } throw TIMEOUT_EXCEPTION; } // process the response received from JBI NMR. The out context is // returned processJBIResponse(exchange, inContext, factory, soapExternalListener, context); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy