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

org.mule.providers.AbstractMessageReceiver Maven / Gradle / Ivy

The newest version!
/*
 * $Id: AbstractMessageReceiver.java 11728 2008-05-13 07:31:11Z dirk.olmes $
 * --------------------------------------------------------------------------------------
 * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
 *
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */

/**
 * Copyright 2012 Bull S.A.S.
 *
 * 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.
 */
// Jasmine: No significant change

package org.mule.providers;

import org.mule.config.ExceptionHelper;
import org.mule.config.MuleProperties;
import org.mule.config.i18n.CoreMessages;
import org.mule.impl.MuleEvent;
import org.mule.impl.MuleMessage;
import org.mule.impl.MuleSession;
import org.mule.impl.NullSessionHandler;
import org.mule.impl.OptimizedRequestContext;
import org.mule.impl.RequestContext;
import org.mule.impl.ResponseOutputStream;
import org.mule.impl.internal.notifications.ConnectionNotification;
import org.mule.impl.internal.notifications.MessageNotification;
import org.mule.impl.internal.notifications.SecurityNotification;
import org.mule.transaction.TransactionCoordination;
import org.mule.umo.UMOComponent;
import org.mule.umo.UMOEvent;
import org.mule.umo.UMOException;
import org.mule.umo.UMOMessage;
import org.mule.umo.UMOSession;
import org.mule.umo.UMOTransaction;
import org.mule.umo.endpoint.UMOEndpoint;
import org.mule.umo.endpoint.UMOEndpointURI;
import org.mule.umo.lifecycle.InitialisationException;
import org.mule.umo.manager.UMOWorkManager;
import org.mule.umo.provider.UMOConnector;
import org.mule.umo.provider.UMOMessageReceiver;
import org.mule.umo.security.SecurityException;
import org.mule.umo.transformer.TransformerException;
import org.mule.umo.transformer.UMOTransformer;
import org.mule.util.ClassUtils;
import org.mule.util.StringMessageUtils;
import org.mule.util.concurrent.WaitableBoolean;

import java.io.OutputStream;

import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * AbstractMessageReceiver provides common methods for all Message
 * Receivers provided with Mule. A message receiver enables an endpoint to receive a
 * message from an external system.
 */
public abstract class AbstractMessageReceiver implements UMOMessageReceiver {
    /**
     * logger used by this class
     */
    protected final Log logger = LogFactory.getLog(getClass());

    /**
     * The Component with which this receiver is associated with
     */
    protected UMOComponent component = null;

    /**
     * The endpoint descriptor which is associated with this receiver
     */
    protected UMOEndpoint endpoint = null;

    private InternalMessageListener listener;

    /**
     * the connector associated with this receiver
     */
    protected AbstractConnector connector = null;

    protected final AtomicBoolean disposing = new AtomicBoolean(false);

    protected final WaitableBoolean connected = new WaitableBoolean(false);

    protected final WaitableBoolean stopped = new WaitableBoolean(true);

    protected final AtomicBoolean connecting = new AtomicBoolean(false);

    /**
     * Stores the key to this receiver, as used by the Connector to
     * store the receiver.
     */
    protected String receiverKey = null;

    /**
     * Stores the endpointUri that this receiver listens on. This enpoint can be
     * different to the endpointUri in the endpoint stored on the receiver as
     * endpoint endpointUri may get rewritten if this endpointUri is a wildcard
     * endpointUri such as jms.*
     */
    private UMOEndpointURI endpointUri;

    private UMOWorkManager workManager;

    protected ConnectionStrategy connectionStrategy;

    /**
     * Creates the Message Receiver
     *
     * @param connector the endpoint that created this listener
     * @param component the component to associate with the receiver. When data is
     *                  received the component dispatchEvent or
     *                  sendEvent is used to dispatch the data to the
     *                  relivant UMO.
     * @param endpoint  the provider contains the endpointUri on which the receiver
     *                  will listen on. The endpointUri can be anything and is specific to
     *                  the receiver implementation i.e. an email address, a directory, a
     *                  jms destination or port address.
     * @see UMOComponent
     * @see UMOEndpoint
     */
    public AbstractMessageReceiver(UMOConnector connector, UMOComponent component, UMOEndpoint endpoint)
            throws InitialisationException {
        setConnector(connector);
        setComponent(component);
        setEndpoint(endpoint);

        listener = new DefaultInternalMessageListener();
        endpointUri = endpoint.getEndpointURI();

        try {
            workManager = this.connector.getReceiverWorkManager("receiver");
        } catch (UMOException e) {
            throw new InitialisationException(e, this);
        }

        connectionStrategy = this.connector.getConnectionStrategy();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mule.umo.provider.UMOMessageReceiver#getEndpointName()
     */
    public UMOEndpoint getEndpoint() {
        return endpoint;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mule.umo.provider.UMOMessageReceiver#getExceptionListener()
     */
    public void handleException(Exception exception) {
        if (exception instanceof ConnectException) {
            logger.info("Exception caught is a ConnectException, disconnecting receiver and invoking ReconnectStrategy");
            try {
                disconnect();
            } catch (Exception e) {
                connector.getExceptionListener().exceptionThrown(e);
            }
        }
        connector.getExceptionListener().exceptionThrown(exception);
        if (exception instanceof ConnectException) {
            try {
                logger.warn("Reconnecting after exception: " + exception.getMessage(), exception);
                connectionStrategy.connect(this);
            } catch (UMOException e) {
                connector.getExceptionListener().exceptionThrown(e);
            }
        }
    }

    /**
     * This method is used to set any additional aand possibly transport specific
     * information on the return message where it has an exception payload.
     *
     * @param message
     * @param exception
     */
    protected void setExceptionDetails(UMOMessage message, Throwable exception) {
        String propName = ExceptionHelper.getErrorCodePropertyName(connector.getProtocol());
        // If we dont find a error code property we can assume there are not
        // error code mappings for this connector
        if (propName != null) {
            String code = ExceptionHelper.getErrorMapping(connector.getProtocol(), exception.getClass());
            if (logger.isDebugEnabled()) {
                logger.debug("Setting error code for: " + connector.getProtocol() + ", " + propName + "="
                        + code);
            }
            message.setProperty(propName, code);
        }
    }

    public UMOConnector getConnector() {
        return connector;
    }

    public void setConnector(UMOConnector connector) {
        if (connector != null) {
            if (connector instanceof AbstractConnector) {
                this.connector = (AbstractConnector) connector;
            } else {
                throw new IllegalArgumentException(CoreMessages.propertyIsNotSupportedType(
                        "connector", AbstractConnector.class, connector.getClass()).getMessage());
            }
        } else {
            throw new IllegalArgumentException(CoreMessages.objectIsNull("connector").getMessage());
        }
    }

    public UMOComponent getComponent() {
        return component;
    }

    public final UMOMessage routeMessage(UMOMessage message) throws UMOException {
        return routeMessage(message, (endpoint.isSynchronous() || TransactionCoordination.getInstance()
                .getTransaction() != null));
    }

    public final UMOMessage routeMessage(UMOMessage message, boolean synchronous) throws UMOException {
        UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
        return routeMessage(message, tx, tx != null || synchronous, null);
    }

    public final UMOMessage routeMessage(UMOMessage message, UMOTransaction trans, boolean synchronous)
            throws UMOException {
        return routeMessage(message, trans, synchronous, null);
    }

    public final UMOMessage routeMessage(UMOMessage message, OutputStream outputStream) throws UMOException {
        return routeMessage(message, endpoint.isSynchronous(), outputStream);
    }

    public final UMOMessage routeMessage(UMOMessage message, boolean synchronous, OutputStream outputStream)
            throws UMOException {
        UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
        return routeMessage(message, tx, tx != null || synchronous, outputStream);
    }

    public final UMOMessage routeMessage(UMOMessage message,
                                         UMOTransaction trans,
                                         boolean synchronous,
                                         OutputStream outputStream) throws UMOException {

        if (connector.isEnableMessageEvents()) {
            connector.fireNotification(new MessageNotification(message, endpoint, component.getDescriptor()
                    .getName(), MessageNotification.MESSAGE_RECEIVED));
        }

        if (endpoint.isRemoteSync()) {
            message.setBooleanProperty(MuleProperties.MULE_REMOTE_SYNC_PROPERTY, true);
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Message Received from: " + endpoint.getEndpointURI());
            try {
                logger.trace("Message Payload: \n"
                        + StringMessageUtils.truncate(StringMessageUtils.toString(message.getPayload()),
                        200, false));
            } catch (Exception e) {
                // ignore
            }
        }

        // Apply the endpoint filter if one is configured
        if (endpoint.getFilter() != null) {
            if (!endpoint.getFilter().accept(message)) {
                //TODO RM* This ain't pretty, we don't yet have an event context since the message hasn't gon to the 
                //message listener yet. So we need to create a new context so that EventAwareTransformers can be applied
                //to response messages where the filter denied the message
                //Maybe the filter should be checked in the MessageListener...
                message = handleUnacceptedFilter(message);
                RequestContext.setEvent(new MuleEvent(message, endpoint,
                        new MuleSession(message, new NullSessionHandler()), synchronous));
                return message;
            }
        }
        return listener.onMessage(message, trans, synchronous, outputStream);
    }

    protected UMOMessage handleUnacceptedFilter(UMOMessage message) {
        String messageId;
        messageId = message.getUniqueId();

        if (logger.isDebugEnabled()) {
            logger.debug("Message " + messageId + " failed to pass filter on endpoint: " + endpoint
                    + ". Message is being ignored");
        }

        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mule.umo.provider.UMOMessageReceiver#setEndpoint(org.mule.umo.endpoint.UMOEndpoint)
     */
    public void setEndpoint(UMOEndpoint endpoint) {
        if (endpoint == null) {
            throw new IllegalArgumentException("Endpoint cannot be null");
        }
        this.endpoint = endpoint;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mule.umo.provider.UMOMessageReceiver#setSession(org.mule.umo.UMOSession)
     */
    public void setComponent(UMOComponent component) {
        if (component == null) {
            throw new IllegalArgumentException("Component cannot be null");
        }
        this.component = component;
    }

    public final void dispose() {
        stop();
        disposing.set(true);
        doDispose();
    }

    public UMOEndpointURI getEndpointURI() {
        return endpointUri;
    }

    protected UMOWorkManager getWorkManager() {
        return workManager;
    }

    protected void setWorkManager(UMOWorkManager workManager) {
        this.workManager = workManager;
    }

    public void connect() throws Exception {
        if (connected.get()) {
            return;
        }

        if (connecting.compareAndSet(false, true)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Connecting: " + this);
            }

            connectionStrategy.connect(this);

            logger.info("Connected: " + this);
            return;
        }

        try {
            this.doConnect();
            connected.set(true);
            connecting.set(false);

            connector.fireNotification(new ConnectionNotification(this, getConnectEventId(),
                    ConnectionNotification.CONNECTION_CONNECTED));
        } catch (Exception e) {
            connected.set(false);
            connecting.set(false);

            connector.fireNotification(new ConnectionNotification(this, getConnectEventId(),
                    ConnectionNotification.CONNECTION_FAILED));

            if (e instanceof ConnectException) {
                throw (ConnectException) e;
            } else {
                throw new ConnectException(e, this);
            }
        }
    }

    public void disconnect() throws Exception {
        if (logger.isDebugEnabled()) {
            logger.debug("Disconnecting: " + this);
        }

        this.doDisconnect();
        connected.set(false);

        logger.info("Disconnected: " + this);

        connector.fireNotification(new ConnectionNotification(this, getConnectEventId(),
                ConnectionNotification.CONNECTION_DISCONNECTED));
    }

    public String getConnectionDescription() {
        return endpoint.getEndpointURI().toString();
    }

    public final void start() throws UMOException {
        if (stopped.compareAndSet(true, false)) {
            if (!connected.get()) {
                connectionStrategy.connect(this);
            }
            doStart();
        }
    }

    public final void stop() {
        try {
            if (connected.get()) {
                disconnect();
            }
        } catch (Exception e) {
            // TODO MULE-863: What should we really do?
            logger.error(e.getMessage(), e);
        }

        if (stopped.compareAndSet(false, true)) {
            try {
                doStop();
            } catch (UMOException e) {
                // TODO MULE-863: What should we really do?
                logger.error(e.getMessage(), e);
            }

        }
    }

    public final boolean isConnected() {
        return connected.get();
    }

    public InternalMessageListener getListener() {
        return listener;
    }

    public void setListener(InternalMessageListener listener) {
        this.listener = listener;
    }

    private class DefaultInternalMessageListener implements InternalMessageListener {

        public UMOMessage onMessage(UMOMessage message,
                                    UMOTransaction trans,
                                    boolean synchronous,
                                    OutputStream outputStream) throws UMOException {

            UMOMessage resultMessage = null;
            ResponseOutputStream ros = null;
            if (outputStream != null) {
                if (outputStream instanceof ResponseOutputStream) {
                    ros = (ResponseOutputStream) outputStream;
                } else {
                    ros = new ResponseOutputStream(outputStream);
                }
            }
            UMOSession session = new MuleSession(message, connector.getSessionHandler(), component);
            UMOEvent muleEvent = new MuleEvent(message, endpoint, session, synchronous, ros);
            muleEvent = OptimizedRequestContext.unsafeSetEvent(muleEvent);
            message = muleEvent.getMessage();

            // Apply Security filter if one is set
            boolean authorised = false;
            if (endpoint.getSecurityFilter() != null) {
                try {
                    endpoint.getSecurityFilter().authenticate(muleEvent);
                    authorised = true;
                } catch (SecurityException e) {
                    // TODO MULE-863: Do we need to warn?
                    logger.warn("Request was made but was not authenticated: " + e.getMessage(), e);
                    connector.fireNotification(new SecurityNotification(e,
                            SecurityNotification.SECURITY_AUTHENTICATION_FAILED));
                    handleException(e);
                    resultMessage = message;
                    // setExceptionDetails(resultMessage, e);
                }
            } else {
                authorised = true;
            }

            if (authorised) {
                // the security filter may update the payload so we need to get the
                // latest event again
                muleEvent = RequestContext.getEvent();

                // This is a replyTo event for a current request
                if (UMOEndpoint.ENDPOINT_TYPE_RESPONSE.equals(endpoint.getType())) {
                    // Transform response message before it is processed by response router(s)
                    UMOEvent responseEvent = new MuleEvent(new MuleMessage(muleEvent.getTransformedMessage(),
                            muleEvent.getMessage()), muleEvent);
                    OptimizedRequestContext.unsafeSetEvent(responseEvent);
                    component.getDescriptor().getResponseRouter().route(responseEvent);
                    return null;
                } else {
                    resultMessage = component.getDescriptor().getInboundRouter().route(muleEvent);
                }
            }
            if (resultMessage != null) {
                if (resultMessage.getExceptionPayload() != null) {
                    setExceptionDetails(resultMessage, resultMessage.getExceptionPayload().getException());
                }
                OptimizedRequestContext.unsafeRewriteEvent(resultMessage);
            }
            return applyResponseTransformer(resultMessage);
        }
    }

    protected String getConnectEventId() {
        return connector.getName() + ".receiver (" + endpoint.getEndpointURI() + ")";
    }

    protected UMOMessage applyResponseTransformer(UMOMessage returnMessage) throws TransformerException {
        UMOTransformer transformer = endpoint.getResponseTransformer();

        // no endpoint transformer, so check on component
        if (transformer == null) {
            transformer = component.getDescriptor().getResponseTransformer();
        }

        // still no transformer, so do nothing.
        if (transformer == null) {
            return returnMessage;
        }

        if (returnMessage == null) {
            if (transformer.isAcceptNull()) {
                returnMessage = new MuleMessage(NullPayload.getInstance(), RequestContext.getEventContext()
                        .getMessage());
            } else {
                return null;
            }
        }

        Object returnPayload = returnMessage.getPayload();
        if (transformer.isSourceTypeSupported(returnPayload.getClass())) {
            Object result = transformer.transform(returnPayload);
            if (result instanceof UMOMessage) {
                returnMessage = (UMOMessage) result;
            } else {
                // Try and wrap the response in the correct messageAdapter, if this
                // doesn't work for some reason
                // just use a standard adater
                // try {
                // UMOMessageAdapter adapter =
                // endpoint.getConnector().getMessageAdapter(result);
                // returnMessage = new MuleMessage(adapter, returnMessage);
                // } catch (MessagingException e) {
                // if(logger.isWarnEnabled()) {
                // logger.warn("Failed to wrap response in " +
                // endpoint.getConnector().getProtocol() + ". Error is: " +
                // e.getMessage());
                // }

                // need to add properties that may have been set in various transformers
//                returnMessage = new MuleMessage(result, returnMessage);
                returnMessage = new MuleMessage(result, RequestContext.getEvent().getMessage());
                // }
                //
            }
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Response transformer: " + transformer + " doesn't support the result payload: "
                        + returnPayload.getClass());
            }
        }
        return returnMessage;
    }

    public void setReceiverKey(String receiverKey) {
        this.receiverKey = receiverKey;
    }

    public String getReceiverKey() {
        return receiverKey;
    }

    public String toString() {
        final StringBuffer sb = new StringBuffer(80);
        sb.append(ClassUtils.getSimpleName(this.getClass()));
        sb.append("{this=").append(Integer.toHexString(System.identityHashCode(this)));
        sb.append(", receiverKey=").append(receiverKey);
        sb.append(", endpoint=").append(endpoint.getEndpointURI());
        sb.append('}');
        return sb.toString();
    }

    protected abstract void doStart() throws UMOException;

    protected abstract void doStop() throws UMOException;

    protected abstract void doConnect() throws Exception;

    protected abstract void doDisconnect() throws Exception;

    protected abstract void doDispose();

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy