org.mule.transport.amqp.internal.client.ChannelHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.transport.amqp.internal.client.ChannelHandler.java

Source

/*
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  https://github.com/mulesoft/mule-transport-amqp
 * 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.
 */
package org.mule.transport.amqp.internal.client;

import com.rabbitmq.client.AlreadyClosedException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ShutdownListener;
import com.rabbitmq.client.ShutdownSignalException;
import org.mule.api.MuleMessage;
import org.mule.api.endpoint.ImmutableEndpoint;
import org.mule.api.transaction.Transaction;
import org.mule.api.transaction.TransactionConfig;
import org.mule.api.transaction.TransactionException;
import org.mule.config.i18n.MessageFactory;
import org.mule.processor.DelegateTransaction;
import org.mule.transaction.IllegalTransactionStateException;
import org.mule.transaction.TransactionCoordination;
import org.mule.transport.ConnectException;
import org.mule.transport.amqp.internal.connector.AmqpConnector;
import org.mule.transport.amqp.internal.transaction.AmqpTransaction;
import org.mule.transport.amqp.internal.transaction.CloseableChannelWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

public class ChannelHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(ChannelHandler.class);

    public Channel getOrCreateChannel(ImmutableEndpoint endpoint) throws Exception {
        Channel ret;

        ret = getTransactedResourceChannel(endpoint);
        if (ret != null) {
            return ret;
        }

        // if no transaction is configured there is no way to find the channel. Creating a new one.
        LOGGER.debug("Channel not found creating a new one");
        return createChannel(endpoint);
    }

    public Channel getOrDefaultChannel(ImmutableEndpoint endpoint, Channel defaultChannel) throws Exception {
        return getOrDefaultChannel(endpoint, null, defaultChannel);
    }

    public Channel getOrDefaultChannel(ImmutableEndpoint endpoint, MuleMessage message, Channel defaultChannel)
            throws Exception {
        Channel ret;

        ret = getChannel(endpoint, message);
        if (ret != null) {
            return ret;
        }

        LOGGER.debug("Channel not found using default");
        // if no transaction is configured there is no way to find the channel. Returning the default value
        return defaultChannel;
    }

    public Channel getChannel(ImmutableEndpoint endpoint, MuleMessage message)
            throws IOException, TransactionException {
        Channel ret;

        if (endpoint.getTransactionConfig().isConfigured()) {
            ret = getTransactedResourceChannel(endpoint);
            if (ret != null) {
                LOGGER.debug("Found Channel as transaction resource");
                return ret;
            }
        } else if (message != null) {
            ret = getFlowVariableChannel(message);
            if (ret != null) {
                LOGGER.debug("Found Channel as flow variable resource");
                return ret;
            }
        }

        return null;
    }

    public Channel getFlowVariableChannel(final MuleMessage message) {
        return message.getInvocationProperty(AmqpConnector.MESSAGE_PROPERTY_CHANNEL);
    }

    public Channel getTransactedResourceChannel(ImmutableEndpoint endpoint)
            throws IOException, TransactionException {
        final byte action = endpoint.getTransactionConfig().getAction();

        final boolean mayUseChannelFromTransaction = action == TransactionConfig.ACTION_BEGIN_OR_JOIN
                || action == TransactionConfig.ACTION_JOIN_IF_POSSIBLE
                || action == TransactionConfig.ACTION_INDIFFERENT;

        final boolean mustUseChannelFromTransaction = action == TransactionConfig.ACTION_ALWAYS_JOIN;

        final Transaction transaction = TransactionCoordination.getInstance().getTransaction();
        if (transaction instanceof AmqpTransaction) {
            if (mustUseChannelFromTransaction || mayUseChannelFromTransaction) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Using transacted channel from current transaction: " + transaction);
                }

                return ((AmqpTransaction) transaction).getTransactedChannel();
            }
        } else if (transaction instanceof DelegateTransaction) {
            // we can't use the current endpoint channel because it may get closed (if the
            // endpoint instance is destroyed) while the transaction block is not done with...
            final Channel channel = createChannel(endpoint);
            channel.txSelect();
            // we wrap the channel so the transaction will know it can safely close it an
            // commit/rollback
            transaction.bindResource(channel.getConnection(), new CloseableChannelWrapper(channel));

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Created transacted channel for delegate transaction: " + transaction);
            }

            return channel;
        } else {
            if (mustUseChannelFromTransaction) {
                throw new IllegalTransactionStateException(MessageFactory
                        .createStaticMessage("No active AMQP transaction found for endpoint: " + endpoint));
            }
        }

        return null;
    }

    public Channel createChannel(ImmutableEndpoint endpoint) throws IOException {
        final AmqpConnector connector = (AmqpConnector) endpoint.getConnector();

        try {
            final Channel channel = connector.getConnection().createChannel();
            channel.addReturnListener(connector.getDefaultReturnListener());
            channel.basicQos(connector.getPrefetchSize(), connector.getPrefetchCount(), false);

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Created and configured new channel: " + channel);
            }

            channel.addShutdownListener(new ShutdownListener() {
                public void shutdownCompleted(final ShutdownSignalException sse) {
                    if (sse.isInitiatedByApplication()) {
                        return;
                    }

                    // do not inform the connector of the issue as it can't
                    // decide what to do reset the channel so it would later
                    // be lazily reconnected
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Terminated dead channel: " + channel, sse);
                    }
                }
            });

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Shutdown listener configured on channel: " + channel);
            }

            return channel;
        } catch (final Exception e) {
            if ((!connector.isStopping()) && (connector.isStarted())) {
                connector.getMuleContext().getExceptionListener()
                        .handleException(new ConnectException(MessageFactory.createStaticMessage(
                                "Impossible to create new channels on connection: " + connector.getConnection()), e,
                                connector));
            }
            return null;
        }

    }

    public void closeChannelIfNotTransacted(final Channel channel) throws ConnectException {
        if (TransactionCoordination.getInstance().getTransaction() != null) {
            return;
        }

        closeChannel(channel);
    }

    public void closeChannel(final Channel channel) throws ConnectException {
        if (channel == null) {
            return;
        }

        if (!channel.isOpen()) {
            return;
        }

        try {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Closing channel: " + channel);
            }

            channel.close();

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Closed channel: " + channel);
            }
        } catch (AlreadyClosedException alreadyClosedException) {
            LOGGER.warn("Attempted to close an already closed channel: " + channel, alreadyClosedException);
        } catch (final Exception e) {
            LOGGER.warn("Failed to close channel: " + channel, e);
        }
    }
}