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

org.seedstack.jms.internal.ManagedConnection Maven / Gradle / Ivy

/**
 * Copyright (c) 2013-2016, The SeedStack authors 
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package org.seedstack.jms.internal;

import com.google.common.collect.Sets;
import org.seedstack.jms.spi.ConnectionDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jms.Connection;
import javax.jms.ConnectionConsumer;
import javax.jms.ConnectionMetaData;
import javax.jms.Destination;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.ServerSessionPool;
import javax.jms.Session;
import javax.jms.Topic;
import java.util.Calendar;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * This connection is a facade to the actual jms connection. It provides the reconnection mechanism.
 */
class ManagedConnection implements Connection, ExceptionListener {
    private static final Logger LOGGER = LoggerFactory.getLogger(ManagedConnection.class);
    private final Set sessions = Sets.newConcurrentHashSet();
    private final AtomicBoolean needToStart = new AtomicBoolean(false);
    private final ConnectionDefinition connectionDefinition;
    private final JmsFactoryImpl jmsFactoryImpl;
    private final AtomicBoolean scheduleInProgress;
    private final ReentrantReadWriteLock connectionLock = new ReentrantReadWriteLock();
    private Connection connection;
    private ExceptionListener exceptionListener;

    ManagedConnection(ConnectionDefinition connectionDefinition, JmsFactoryImpl jmsFactoryImpl) throws JMSException {
        checkNotNull(connectionDefinition);

        this.jmsFactoryImpl = jmsFactoryImpl;
        this.connectionDefinition = connectionDefinition;
        this.scheduleInProgress = new AtomicBoolean(false);
        this.connection = createConnection();
    }

    private Connection createConnection() throws JMSException {
        LOGGER.debug("Initializing managed JMS connection {}", connectionDefinition.getName());

        Connection newConnection = jmsFactoryImpl.createRawConnection(connectionDefinition);

        // Set the exception listener to ourselves so we can monitor the underlying connection
        if (!connectionDefinition.isJeeMode()) {
            newConnection.setExceptionListener(this);
        }

        return newConnection;
    }

    private void scheduleReconnection() {
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                connectionLock.writeLock().lock();

                try {
                    // Recreate the connection
                    LOGGER.info("Recreating managed JMS connection {}", connectionDefinition.getName());
                    connection = createConnection();

                    // Refresh sessions
                    for (ManagedSession session : sessions) {
                        session.refresh(connection);
                    }

                    // Start the new connection if needed
                    if (needToStart.get()) {
                        LOGGER.info("Restarting managed JMS connection {}", connectionDefinition.getName());
                        connection.start();
                        scheduleInProgress.set(false);
                    }
                } catch (Exception e) {
                    LOGGER.error("Failed to restart managed JMS connection {}, next attempt in {} ms", connectionDefinition.getName(), connectionDefinition.getReconnectionDelay());
                    scheduleReconnection();
                } finally {
                    connectionLock.writeLock().unlock();
                }

            }
        };

        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MILLISECOND, connectionDefinition.getReconnectionDelay());
        new Timer().schedule(timerTask, calendar.getTime());
    }

    private Connection getConnection() throws JMSException {
        connectionLock.readLock().lock();
        try {
            if (connection == null) {
                throw new JMSException("Managed JMS connection " + connectionDefinition.getName() + " is not ready");
            }

            return connection;
        } finally {
            connectionLock.readLock().unlock();
        }
    }

    @Override
    public void onException(JMSException exception) {
        LOGGER.error("An exception occurred on managed JMS connection {}", connectionDefinition.getName());
        if (exception != null && LOGGER.isDebugEnabled()) {
            LOGGER.debug("Original exception below", exception);
        }

        if (exceptionListener != null) {
            exceptionListener.onException(exception);
        }

        reset();
    }

    private void reset() {
        if (scheduleInProgress.getAndSet(true)) {
            LOGGER.debug("Managed JMS connection {} already scheduled for restart", connectionDefinition.getName());
        } else {
            // reset the connection
            LOGGER.warn("Resetting managed JMS connection {} and scheduling restart in {} ms", connectionDefinition.getName(), connectionDefinition.getReconnectionDelay());

            connectionLock.writeLock().lock();
            try {
                // Reset the sessions to prevent their use during refresh
                for (ManagedSession session : sessions) {
                    session.reset();
                }

                // Effectively close th connection and prevent its use during refresh
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (JMSException e) {
                        LOGGER.warn("Unable to cleanly close the managed JMS connection {}", connectionDefinition.getName());
                    }
                }
                connection = null;

                // Schedule
                scheduleReconnection();
            } finally {
                connectionLock.writeLock().unlock();
            }
        }
    }

    // Delegated methods

    @Override
    public Session createSession(boolean transacted, int acknowledgeMode) throws JMSException {
        connectionLock.readLock().lock();
        try {
            ManagedSession managedSession = new ManagedSession(
                    getConnection().createSession(transacted, acknowledgeMode),
                    transacted,
                    acknowledgeMode,
                    connectionDefinition.isJeeMode(),
                    this);
            sessions.add(managedSession);
            return managedSession;
        } finally {
            connectionLock.readLock().unlock();
        }
    }

    @Override
    public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException {
        return getConnection().createConnectionConsumer(destination, messageSelector, sessionPool, maxMessages);
    }

    @Override
    public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException {
        return getConnection().createDurableConnectionConsumer(topic, subscriptionName, messageSelector, sessionPool, maxMessages);
    }

    @Override
    public void close() throws JMSException {
        LOGGER.info("Closing managed JMS connection {}", connectionDefinition.getName());
        getConnection().close();
    }

    @Override
    public void start() throws JMSException {
        LOGGER.info("Starting managed JMS connection {}", connectionDefinition.getName());
        getConnection().start();
        needToStart.set(true);
    }

    @Override
    public void stop() throws JMSException {
        LOGGER.info("Stopping managed JMS connection {}", connectionDefinition.getName());
        getConnection().stop();
        needToStart.set(false);
    }

    @Override
    public ConnectionMetaData getMetaData() throws JMSException {
        return getConnection().getMetaData();
    }

    @Override
    public void setExceptionListener(ExceptionListener exceptionListener) throws JMSException {
        this.exceptionListener = exceptionListener;
    }

    @Override
    public ExceptionListener getExceptionListener() throws JMSException {
        return exceptionListener;
    }

    @Override
    public void setClientID(String clientID) throws JMSException {
        throw new IllegalStateException("Client ID cannot be changed on managed connections");
    }

    @Override
    public String getClientID() throws JMSException {
        throw new IllegalStateException("Client ID cannot be retrieved on managed connections");
    }

    void removeSession(ManagedSession managedSession) {
        sessions.remove(managedSession);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy