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

org.ow2.cmi.ha.JGMessageManager Maven / Gradle / Ivy

There is a newer version: 2.2.6
Show newest version
/**
 * High Availability Service (HA) for JOnAS
 *
 * Copyright (C) 2007,2008 Bull S.A.S.
 * Contact: [email protected]
 *
 * Copyright (C) 2006 Distributed Systems Lab.
 * Universidad Politecnica de Madrid (Spain)
 * Contact: http://lsd.ls.fi.upm.es/lsd
 *
 * 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
 *
 * --------------------------------------------------------------------------
 * $Id: JGMessageManager.java 2438 2010-02-24 16:20:30Z benoitf $
 * --------------------------------------------------------------------------
 */

package org.ow2.cmi.ha;

import java.io.File;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.LinkedList;
import java.util.NoSuchElementException;

import org.jgroups.Address;
import org.jgroups.Channel;
import org.jgroups.ChannelException;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.blocks.GroupRequest;
import org.jgroups.blocks.RequestHandler;
import org.jgroups.jmx.JmxConfigurator;
import org.jgroups.util.RspList;
import org.ow2.cmi.admin.MBeanUtils;
import org.ow2.cmi.controller.common.IConfig;
import org.ow2.cmi.controller.factory.ClusterViewManagerFactory;
import org.ow2.util.cluster.jgroups.ConnectionManager;
import org.ow2.util.cluster.jgroups.IMessageDispatcher;
import org.ow2.util.cluster.jgroups.MessageDispatcherWrapper;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * This class processes the HA messages received through JGroups.
 * @author Francisco Perez-Sorrosal (fpsorrosal@[email protected])
 * @author Alberto Paz-Jimenez (apaz@[email protected])
 */
public class JGMessageManager implements RequestHandler, MessageManager {

    /**
     * Logger.
     */
    private static Log logger = LogFactory.getLog(JGMessageManager.class);

    /**
     * Default stack to configure JGroups.
     */
    private static final String JGROUPS_STACK = "UDP(mcast_addr=224.0.0.40" + ";mcast_port=30001" + ";ip_ttl=32;"
    + "mcast_send_buf_size=150000;mcast_recv_buf_size=80000):" + "PING(timeout=2000;num_initial_members=3):"
    + "MERGE2(min_interval=5000;max_interval=10000):" + "FD(timeout=2000;max_tries=3;shun=true):"
    + "VERIFY_SUSPECT(timeout=1500):" + "pbcast.NAKACK(gc_lag=50;retransmit_timeout=300,600,1200,2400,4800):"
    + "UNICAST(timeout=1200,2400,3600):" + "pbcast.STABLE(stability_delay=1000;desired_avg_gossip=2000):"
    + "FRAG(frag_size=4096;down_thread=false;up_thread=false):"
    + "pbcast.GMS(join_timeout=3000;join_retry_timeout=2000;" + "shun=false;print_local_addr=true)";


    /**
     * Default group name.
     */
    private static final String DEFAULT_GROUP_NAME = "jonas-rep";

    private final ReplicationManager replicationManager;

    /**
     * JGroups related classes.
     */
    private JChannel channel;

    private IMessageDispatcher dispatcher;

    private JGViewManager viewMgr;

    private Address address;

    private LinkedList messages;

    private ProcessThread processThread;

    private long messagesNumber;

    private long messagesSize;

    private final String jgroupsHAConfFilename;

    private final String jgroupsHAGroupName;


    /**
     * Constructor.
     */
    public JGMessageManager(final String jgroupsConf,
            final String jgroupsGroupname,
            final ReplicationManager replicationManager,
            final long timeout) {
        this.replicationManager = replicationManager;

        if(jgroupsConf == null) {
            jgroupsHAGroupName = DEFAULT_GROUP_NAME;
        } else {
            jgroupsHAGroupName = jgroupsGroupname;
        }
        logger.info("jgroups HA group name is: " + jgroupsHAGroupName);

        jgroupsHAConfFilename = jgroupsConf;
        logger.info("jgroups HA configuration file is: " + jgroupsHAConfFilename);

        IConfig config = ClusterViewManagerFactory.getFactory().getConfig();

        String confDir = config.getConfDir();

        URL file = null;
        if(confDir != null) {
            URI uri = new File(confDir, jgroupsHAConfFilename).toURI();
            try {
                file = uri.toURL();
            } catch (MalformedURLException e) {
                logger.warn("Cannot get the url for the uri: {0}", uri, e);
            }
        }
        if(file == null) {
            file = ClassLoader.getSystemClassLoader().getResource(jgroupsHAConfFilename);
            if(file == null) {
                file = Thread.currentThread().getContextClassLoader().getResource(jgroupsHAConfFilename);
                if(file == null) {
                    file = getClass().getResource(jgroupsHAConfFilename);
                }
            }
        }

        try {
            if (file != null) {
                channel = new JChannel(file);
            } else {
                logger.info("error accesing jgroups HA configuration file, using defaults");
                channel = new JChannel(JGROUPS_STACK);
            }

            channel.setOpt(Channel.VIEW, true);
            // Avoid our own messages
            channel.setOpt(Channel.LOCAL, false);

            // Set the auto-reconnect option for enabling a node to leave and re-join the cluster
            channel.setOpt(Channel.AUTO_RECONNECT, true);

            viewMgr = new JGViewManager();

            IMessageDispatcher messageDispatcher =
                new MessageDispatcherWrapper(channel, null, viewMgr, this);
            ConnectionManager connectionManager =
                new ConnectionManager(timeout, messageDispatcher, IMessageDispatcher.class);
            channel.addChannelListener(connectionManager);
            dispatcher = (IMessageDispatcher) Proxy.newProxyInstance(
                    IMessageDispatcher.class.getClassLoader(), new Class[]{IMessageDispatcher.class}, connectionManager);

            channel.connect(jgroupsHAGroupName);

            if(config.isAdminEnabled()) {
                // Register the JMX MBeans
                try {
                    JmxConfigurator.registerChannel(
                            channel, MBeanUtils.getMBeanServer(),
                            MBeanUtils.getMBeanDomainName(), channel.getClusterName() + ",name=HA", true);
                } catch (Exception e) {
                    logger.warn("Unable to register the channel to the MBean Server", e);
                }
            }

            messages = new LinkedList();

            processThread = new ProcessThread(messages);
            processThread.setDaemon(true);
            processThread.start();

            address = channel.getLocalAddress();
            logger.debug(address + " joined group " + jgroupsHAGroupName);

            // Initialize statistics
            messagesNumber = 0;
            messagesSize = 0;
        } catch (ChannelException e) {
            logger.error("Error creating JGroups channel", e);
        }
    }

    /* (non-Javadoc)
     * @see org.ow2.cmi.ha.MessageManager#sendMessage(org.ow2.cmi.ha.HaMessageData)
     */
    public void sendMessage(final HaMessageData messageData) {
        Message message = new Message(null, null, messageData);

        // Compute statistics
        messagesNumber++;
        messagesSize += message.size();

        RspList rspList = dispatcher.castMessage(null, message, GroupRequest.GET_FIRST, 0);
        if (logger.isDebugEnabled()) {
            Address localAddress = dispatcher.getChannel().getLocalAddress();
            logger.debug("Message sended. Length: " + message.getBuffer().length + " From: " + localAddress);
            logger.debug("Response: " + rspList.toString());
        }
    }

    /**
     * Processes the replication messages received. Depending on the message's type
     * a concrete process will be performed
     */
    public Object handle(final Message message) {
        if (logger.isDebugEnabled()) {
            logger.debug("-----------------------------------------------");
            logger.debug("Processing message: " + message + " in: " + dispatcher.getChannel().getLocalAddress());

        }

        Object o = message.getObject();
        if (o instanceof HaMessageData) {
            synchronized (messages) {
                messages.add((HaMessageData) o);
                // COMPLETE: Check if this should be > 0
                if (messages.size() == 1) {
                    processThread.resumeExecution();
                }
            }
        } else {
            logger.debug("\tNo action performed, unknown message format!!!");
        }
        logger.debug("-----------------------------------------------");
        return null;
    }

    /* (non-Javadoc)
     * @see org.ow2.cmi.ha.MessageManager#clear()
     */
    public void clear() {
        dispatcher.stop();
        if(ClusterViewManagerFactory.getFactory().getConfig().isAdminEnabled()) {
            String domain = MBeanUtils.getMBeanDomainName();
            String clusterName = channel.getClusterName();
            String name = domain + ":type=channel,cluster=" + clusterName
            + ",name=HA";
            String protos = domain + ":type=protocol,cluster=" + clusterName
            + ",name=HA" + ",*";
            try {
                JmxConfigurator.unregisterChannel(MBeanUtils.getMBeanServer(), name);
                JmxConfigurator.unregister(MBeanUtils.getMBeanServer(), protos);
            } catch (Exception e) {
                logger.warn(
                        "Error when unregistering the channel with name {0} from the MBean server",
                        clusterName, e);
            }
        }
        channel.disconnect();
        channel.close();
        processThread.stopExecution();
    }

    /**
     * Get the number of replicated messages sent.
     * @return the number of replicated messages
     */
    public long getNumberofReplicatedMessages() {
        return messagesNumber;
    }

    /**
     * Get the average size of the replicated messages sent.
     * @return the average size of the replicated messages sent.
     */
    public double getAvgSizeofReplicatedMessages() {
        if (messagesNumber > 0) {
            return messagesSize / messagesNumber;
        } else {
            return 0;
        }
    }

    /**
     * Get the total size of the replicated messages sent.
     * @return the total size of the replicated messages
     */
    public double getTotSizeofReplicatedMessages()  {
        return messagesSize;
    }

    /**
     * Get the JGroups configuration file name.
     * @return the JGroups configuration file name
     */
    public String jgroupsConfFileName() {
        return jgroupsHAConfFilename;
    }

    /**
     * This Thread process messages in messages list.
     * @author Francisco Perez-Sorrosal (fpsorrosal@[email protected])
     * @author Alberto Paz-Jimenez (apaz@[email protected])
     */
    private final class ProcessThread extends Thread {
        private volatile Thread test;
        private final LinkedList messages;
        private volatile boolean suspended;

        ProcessThread(final LinkedList messages) {
            this.messages = messages;
        }

        @Override
        public void run() {
            logger.debug("ProcessMessage thread started");

            suspended = false;
            test = Thread.currentThread();
            while (test == Thread.currentThread()) {
                try {
                    HaMessageData o = null;
                    synchronized (messages) {
                        if (messages.size() > 0) {
                            o = messages.removeFirst();
                        } else {
                            suspendExecution();
                        }
                    }

                    if (o != null) {
                        replicationManager.processMessage(o);
                    }
                } catch (NoSuchElementException e) {
                    // If there are no messages suspend the thread
                    suspendExecution();
                }

                if (suspended) {
                    synchronized(this) {
                        try {
                            while (suspended && test == Thread.currentThread()) {
                                wait();
                            }
                        } catch (InterruptedException e1) {
                            ;
                        }
                    }
                }
            }
        }

        void stopExecution() {
            logger.debug("ProcessMessage thread stoped");

            synchronized (this) {
                test = null;
                notify();
            }
        }

        void suspendExecution() {
            logger.debug("ProcessMessage thread suspended");

            synchronized (this) {
                suspended = true;
            }
        }

        void resumeExecution() {
            logger.debug("ProcessMessage thread resumed");

            synchronized (this) {
                suspended = false;
                notify();
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy