org.objectweb.joram.mom.dest.amqp.LiveServerConnection.java Source code

Java tutorial

Introduction

Here is the source code for org.objectweb.joram.mom.dest.amqp.LiveServerConnection.java

Source

/*
 * JORAM: Java(TM) Open Reliable Asynchronous Messaging
 * Copyright (C) 2011 ScalAgent Distributed Technologies
 *
 * This 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 any later version.
 *
 * This 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA.
 *
 * Initial developer(s): ScalAgent Distributed Technologies
 * Contributor(s): 
 */
package org.objectweb.joram.mom.dest.amqp;

import java.io.IOException;
import java.io.Serializable;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ShutdownListener;
import com.rabbitmq.client.ShutdownSignalException;

import fr.dyade.aaa.agent.AgentServer;
import fr.dyade.aaa.common.Daemon;
import fr.dyade.aaa.common.Debug;
import fr.dyade.aaa.util.management.MXWrapper;

/**
 * A {@link LiveServerConnection} keeps alive a connection to an AMQP server.
 * When the connection fails, a reconnection routine starts.
 */
public class LiveServerConnection implements LiveServerConnectionMBean, ShutdownListener, Serializable {

    private static final long serialVersionUID = 1L;

    private static final Logger logger = Debug.getLogger(LiveServerConnection.class.getName());

    private transient ConnectionFactory cnxFactory;

    private transient ReconnectionDaemon cnxDaemon;

    private transient volatile Connection conn = null;

    private String name;

    private String host;

    private int port;

    private String user;

    private String password;

    /**
     * Starts a connection with a default AMQP server.
     */
    public LiveServerConnection() {
    }

    /**
     * Starts a connection with a server accessible via the factory provided.
     * 
     * @param factory the factory used to access the server.
     */
    public LiveServerConnection(String name, String host, int port, String user, String password) {
        this.name = name;
        this.host = host;
        this.port = port;
        this.user = user;
        this.password = password;
    }

    public void startLiveConnection() {
        this.cnxFactory = new ConnectionFactory();
        cnxFactory.setHost(host);
        cnxFactory.setPort(port);
        if (user != null) {
            cnxFactory.setUsername(user);
        }
        if (password != null) {
            cnxFactory.setPassword(password);
        }
        cnxDaemon = new ReconnectionDaemon();

        try {
            conn = cnxFactory.newConnection();
            conn.addShutdownListener(this);
        } catch (Exception exc) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "connection failed, start daemon.", exc);
            }
            cnxDaemon.start();
        }

        try {
            MXWrapper.registerMBean(this, getMBeanName());
        } catch (Exception e) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "registerMBean", e);
            }
        }
    }

    public boolean isConnectionOpen() {
        return conn != null && conn.isOpen();
    }

    public Connection getConnection() {
        return conn;
    }

    public ConnectionFactory getConnectionFactory() {
        return cnxFactory;
    }

    private String getMBeanName() {
        StringBuilder strbuf = new StringBuilder();

        strbuf.append("AMQP#").append(AgentServer.getServerId());
        strbuf.append(':');
        strbuf.append("type=Connections,name=").append(name);
        strbuf.append('[').append(cnxFactory.getHost()).append(']');

        return strbuf.toString();
    }

    /**
     * Stops maintaining the connection alive with the server.
     */
    public void stopLiveConnection() {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "Close connection.");
        }
        if (conn != null) {
            conn.removeShutdownListener(this);
            try {
                conn.close();
            } catch (IOException exc) {
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Connection closing error.", exc);
                }
            }
        }
        if (cnxDaemon.isRunning()) {
            cnxDaemon.stop();
        }
        try {
            MXWrapper.unregisterMBean(getMBeanName());
        } catch (Exception exc) {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "unregisterMBean", exc);
            }
        }
    }

    public void shutdownCompleted(ShutdownSignalException cause) {
        if (logger.isLoggable(BasicLevel.WARN)) {
            logger.log(BasicLevel.WARN, "Connection with AMQP server lost, start reconnecting.", cause);
        }
        cnxDaemon.start();
    }

    /**
     * The <code>ReconnectionDaemon</code> thread is responsible for reconnecting
     * the module with the foreign AMQP server in case of disconnection.
     */
    private class ReconnectionDaemon extends Daemon {

        /** Number of reconnection trials of the first step. */
        private int attempts1 = 30;

        /** Retry interval (in milliseconds) of the first step. */
        private long interval1 = 1000L;

        /** Number of reconnection trials of the second step. */
        private int attempts2 = 55;

        /** Retry interval (in milliseconds) of the second step. */
        private long interval2 = 5000L;

        /** Retry interval (in milliseconds) of the third step. */
        private long interval3 = 60000L;

        /** Constructs a <code>ReconnectionDaemon</code> thread. */
        protected ReconnectionDaemon() {
            super("ReconnectionDaemon#" + name, logger);
            setDaemon(false);
            if (logmon.isLoggable(BasicLevel.DEBUG)) {
                logmon.log(BasicLevel.DEBUG, "ReconnectionDaemon<init>");
            }
        }

        /** The daemon's loop. */
        public void run() {
            if (logmon.isLoggable(BasicLevel.DEBUG)) {
                logmon.log(BasicLevel.DEBUG, "run()");
            }

            int attempts = 0;
            long interval;

            try {
                while (running) {

                    attempts++;

                    if (attempts == 1) {
                        interval = 0;
                    } else if (attempts <= attempts1) {
                        interval = interval1;
                    } else if (attempts <= attempts2) {
                        interval = interval2;
                    } else {
                        interval = interval3;
                    }

                    canStop = true;
                    try {
                        if (logmon.isLoggable(BasicLevel.DEBUG)) {
                            logmon.log(BasicLevel.DEBUG, "attempt " + attempts + ", wait=" + interval);
                        }
                        Thread.sleep(interval);

                        if (logmon.isLoggable(BasicLevel.DEBUG)) {
                            logmon.log(BasicLevel.DEBUG, "connect...");
                        }
                        canStop = false;

                        conn = cnxFactory.newConnection();
                        conn.addShutdownListener(LiveServerConnection.this);

                    } catch (Exception exc) {
                        if (logmon.isLoggable(BasicLevel.DEBUG)) {
                            logmon.log(BasicLevel.DEBUG, "connection failed, continue... " + exc.getMessage());
                        }
                        continue;
                    }

                    if (logmon.isLoggable(BasicLevel.DEBUG)) {
                        logmon.log(BasicLevel.DEBUG,
                                "Connected on " + cnxFactory.getHost() + ':' + cnxFactory.getPort());
                    }
                    break;
                }
            } finally {
                finish();
            }
        }

        /** Shuts the daemon down. */
        public void shutdown() {
            interrupt();
        }

        /** Releases the daemon's resources. */
        public void close() {
        }
    }

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }

    public String getName() {
        return name;
    }

    public String getUserName() {
        return user;
    }

    public String getState() {
        if (isConnectionOpen()) {
            return "OK";
        }
        return "FAILING";
    }

}