Package org.codehaus.activemq.transport.multicast

Source Code of org.codehaus.activemq.transport.multicast.MulticastDiscoveryAgent

/**
*
* Copyright 2004 Protique Ltd
*
* 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.
*
**/

package org.codehaus.activemq.transport.multicast;
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.Map;
import javax.jms.JMSException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.activemq.io.impl.DefaultWireFormat;
import org.codehaus.activemq.message.ActiveMQMessage;
import org.codehaus.activemq.message.ActiveMQObjectMessage;
import org.codehaus.activemq.message.Packet;
import org.codehaus.activemq.message.PacketListener;
import org.codehaus.activemq.transport.DiscoveryAgentSupport;
import org.codehaus.activemq.transport.DiscoveryEvent;
import org.codehaus.activemq.util.IdGenerator;
import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedLong;

/**
* An agent used to discover other instances of a service
*
* @version $Revision: 1.5 $
*/
public class MulticastDiscoveryAgent extends DiscoveryAgentSupport implements PacketListener, Runnable {
    private static final Log log = LogFactory.getLog(MulticastDiscoveryAgent.class);
    /**
     * default URI used for discovery
     */
    public static final String DEFAULT_DISCOVERY_URI = "multicast://224.1.2.3:6066";
    private static final String KEEP_ALIVE_TYPE = "KEEP_ALIVE";
    private static final String SERVICE_TYPE = "SERVICE";
    private static final String STARTED_TYPE = "STARTED_TYPE";
    private static final String SERVICE_NAME = "SERVICE_NAME";
    private static final String CHANNEL_NAME = "CHANNEL_NAME";
    private static final long DEFAULT_KEEP_ALIVE_TIMEOUT = 5000;
    private static final int DEFAULT_TIMEOUT_COUNT = 2;
    private ConcurrentHashMap services;
    private ConcurrentHashMap keepAliveMap;
    private SynchronizedBoolean started;
    private MulticastTransportChannel channel;
    private Thread runner;
    private IdGenerator idGen;
    private String localId;
    private URI uri;
    private int timeoutCount;
    private long keepAliveTimeout;
    private long timeoutExpiration;
    private ActiveMQMessage keepAliveMessage;
    private ActiveMQObjectMessage serviceMessage;
    private String serviceName = "";
    private int timeToLive = 1;
    private String channelName;

    /**
     * Construct a discovery agent that uses multicast
     *
     * @param channelName
     * @throws JMSException
     */
    public MulticastDiscoveryAgent(String channelName) throws JMSException {
        this.channelName = channelName;
        this.started = new SynchronizedBoolean(false);
        this.services = new ConcurrentHashMap();
        this.keepAliveMap = new ConcurrentHashMap();
        this.idGen = new IdGenerator();
        this.localId = idGen.generateId();
        this.keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
        this.timeoutCount = DEFAULT_TIMEOUT_COUNT;
        this.timeoutExpiration = this.keepAliveTimeout * timeoutCount;
        try {
            setUri(new URI(DEFAULT_DISCOVERY_URI));
        }
        catch (URISyntaxException e) {
            JMSException jmsEx = new JMSException("URI Syntax exception: " + e.getMessage());
            jmsEx.setLinkedException(e);
            throw jmsEx;
        }
    }

    /**
     * @return Returns the keepAliveTimeout.
     */
    public long getKeepAliveTimeout() {
        return keepAliveTimeout;
    }

    /**
     * @param keepAliveTimeout The keepAliveTimeout to set.
     */
    public void setKeepAliveTimeout(long keepAliveTimeout) {
        this.keepAliveTimeout = keepAliveTimeout;
    }

    /**
     * @return Returns the timeoutCount.
     */
    public int getTimeoutCount() {
        return timeoutCount;
    }

    /**
     * @param timeoutCount The timeoutCount to set.
     */
    public void setTimeoutCount(int timeoutCount) {
        this.timeoutCount = timeoutCount;
    }

    /**
     * @return Returns the localId.
     */
    public String getLocalId() {
        return localId;
    }

    /**
     * @param localId The localId to set.
     */
    public void setLocalId(String localId) {
        this.localId = localId;
    }

    /**
     * @return Returns the uri.
     */
    public URI getUri() {
        return uri;
    }

    /**
     * @param uri The uri to set.
     */
    public void setUri(URI uri) {
        this.uri = uri;
    }

    /**
     * @return the timeToLive of multicast packets used for discovery
     */
    public int getTimeToLive() {
        return this.timeToLive;
    }

    /**
     * @param timeToLive The timeToLive for multicast packets used in discovery.
     * @throws IOException
     */
    public void setTimeToLive(int timeToLive) throws IOException {
        this.timeToLive = timeToLive;
        if (channel != null) {
            channel.setTimeToLive(timeToLive);
        }
    }

    /**
     * @return Returns the channelName.
     */
    public String getChannelName() {
        return channelName;
    }

    /**
     * @param channelName The channelName to set.
     */
    public void setChannelName(String channelName) {
        this.channelName = channelName;
    }

    /**
     * @return a pretty print of this instance
     */
    public String toString() {
        return "MulticastDiscoveryAgent:" + serviceName;
    }

    /**
     * @return the number of active services, including self
     */
    public int getServicesCount() {
        return (keepAliveMessage != null ? 1 : 0) + services.size();
    }

    /**
     * Register a service for other discover nodes
     *
     * @param name
     * @param details
     * @throws JMSException
     */
    public void registerService(String name, Map details) throws JMSException {
        if (this.keepAliveMessage != null) {
            this.keepAliveMessage.setBooleanProperty(STARTED_TYPE, true);
            sendKeepAlive();
        }
        this.serviceName = name;
        this.serviceMessage = new ActiveMQObjectMessage();
        this.serviceMessage.setJMSType(SERVICE_TYPE);
        this.serviceMessage.setStringProperty(SERVICE_NAME, name);
        this.serviceMessage.setStringProperty(CHANNEL_NAME, channelName);
        this.serviceMessage.setObject((Serializable) details);
        sendService();
        this.keepAliveMessage = new ActiveMQMessage();
        this.keepAliveMessage.setJMSType(KEEP_ALIVE_TYPE);
        this.keepAliveMessage.setStringProperty(SERVICE_NAME, name);
        this.keepAliveMessage.setStringProperty(CHANNEL_NAME, channelName);
        this.keepAliveMessage.setBooleanProperty(STARTED_TYPE, true);
        sendKeepAlive();
    }

    /**
     * start this discovery agent
     *
     * @throws JMSException
     */
    public void start() throws JMSException {
        if (started.commit(false, true)) {
            this.timeoutExpiration = this.keepAliveTimeout * timeoutCount;
            channel = new MulticastTransportChannel(new DefaultWireFormat(), uri);
            channel.setClientID(localId);
            channel.setPacketListener(this);
            try {
                channel.setTimeToLive(getTimeToLive());
            }
            catch (IOException e) {
                JMSException jmsEx = new JMSException("Set time to live failed");
                jmsEx.setLinkedException(e);
                throw jmsEx;
            }
            channel.start();
            runner = new Thread(this);
            runner.setName(toString());
            runner.setDaemon(true);
            runner.setPriority(Thread.MAX_PRIORITY);
            runner.start();
            sendService();
            sendKeepAlive();
            fireServiceStarted(serviceMessage);
        }
    }

    /**
     * stop this discovery agent
     *
     * @throws JMSException
     */
    public void stop() throws JMSException {
        boolean doStop = false;
        synchronized (started) {
            doStop = started.get();
            if (doStop) {
                if (keepAliveMessage != null) {
                    keepAliveMessage.setBooleanProperty(STARTED_TYPE, false);
                    sendKeepAlive();
                }
                channel.stop();
                started.set(false);
            }
        }
        if (doStop) {
            fireServiceStopped(serviceMessage);
        }
    }

    /**
     * send a keep alive message
     */
    public void run() {
        try {
            int count = 0;
            while (started.get()) {
                sendKeepAlive();
                log.debug(serviceName + " sent keep alive");
                if (++count >= timeoutCount) {
                    count = 0;
                    checkNodesAlive();
                }
                Thread.sleep(getKeepAliveTimeout());
            }
        }
        catch (Throwable e) {
            log.error(toString() + " run failed", e);
        }
    }

    /**
     * Consume multicast packets
     *
     * @param packet
     */
    public void consume(Packet packet) {
        try {
            if (packet != null && packet.isJMSMessage()) {
                ActiveMQMessage msg = (ActiveMQMessage) packet;
                String receivedChannelName = msg.getStringProperty(CHANNEL_NAME);
                if (receivedChannelName != null && receivedChannelName.equals(channelName)) {
                    String type = msg.getJMSType();
                    if (type != null) {
                        if (type.equals(KEEP_ALIVE_TYPE)) {
                            processKeepAlive(msg);
                        }
                        else if (type.equals(SERVICE_TYPE)) {
                            processService(msg);
                        }
                        else {
                            log.warn(toString() + " received Message of unknown type: " + type);
                        }
                    }else {
                        log.error(toString() + " message type is null");
                    }
                }
            }
            else {
                log.warn(toString() + " received unexpected packet: " + packet);
            }
        }
        catch (Throwable e) {
            log.error(toString() + " couldn't process packet: " + packet, e);
        }
    }

    private void sendKeepAlive() throws JMSException {
        if (started.get() && channel != null && !channel.isPendingStop() && keepAliveMessage != null) {
            channel.asyncSend(keepAliveMessage);
        }
    }

    private void sendService() throws JMSException {
        if (started.get() && channel != null && !channel.isPendingStop() && serviceMessage != null) {
            channel.asyncSend(serviceMessage);
        }
    }

    private void processKeepAlive(ActiveMQMessage message) throws JMSException {
        String name = message.getStringProperty(SERVICE_NAME);
        if (message.getBooleanProperty(STARTED_TYPE)) {
            addService(name);
        }
        else {
            removeService(name);
        }
    }

    private void processService(ActiveMQMessage message) throws JMSException {
        if (message != null) {
            ActiveMQObjectMessage objMsg = (ActiveMQObjectMessage) message;
            String name = objMsg.getStringProperty(SERVICE_NAME);
            addService(name);
            ActiveMQObjectMessage oldMsg = (ActiveMQObjectMessage) services.get(name);
            services.put(name, objMsg);
            if (oldMsg == null) {
                fireServiceStarted(objMsg);
                //send out that we are here!
                sendService();
            }
        }
    }

    private void fireServiceStarted(ActiveMQObjectMessage message) throws JMSException {
        if (message != null) {
            String name = message.getStringProperty(SERVICE_NAME);
            Map map = (Map) message.getObject();
            DiscoveryEvent event = new DiscoveryEvent(this, name, map);
            fireAddService(event);
        }
    }

    private void fireServiceStopped(ActiveMQObjectMessage message) throws JMSException {
        if (message != null) {
            String name = message.getStringProperty(SERVICE_NAME);
            Map map = (Map) message.getObject();
            DiscoveryEvent event = new DiscoveryEvent(this, name, map);
            fireRemoveService(event);
        }
    }

    private void addService(String name) {
        long timestamp = System.currentTimeMillis();
        SynchronizedLong activeTime = (SynchronizedLong) keepAliveMap.get(name);
        if (activeTime == null) {
            activeTime = new SynchronizedLong(0);
            keepAliveMap.put(name, activeTime);
        }
        activeTime.set(timestamp);
    }

    private void removeService(String name) throws JMSException {
        keepAliveMap.remove(name);
        ActiveMQObjectMessage message = (ActiveMQObjectMessage) services.remove(name);
        if (message != null) {
            fireServiceStopped(message);
        }
    }

    private void checkNodesAlive() throws JMSException {
        long timestamp = System.currentTimeMillis();
        long timeout = timestamp - timeoutExpiration;
        for (Iterator i = keepAliveMap.entrySet().iterator();i.hasNext();) {
            Map.Entry entry = (Map.Entry) i.next();
            SynchronizedLong activeTime = (SynchronizedLong) entry.getValue();
            if (activeTime.get() < timeout) {
                String name = entry.getKey().toString();
                removeService(name);
                log.warn(serviceName + " Expiring node: " + name);
            }
        }
    }
}
TOP

Related Classes of org.codehaus.activemq.transport.multicast.MulticastDiscoveryAgent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.