Package org.activemq.transport.reliable

Source Code of org.activemq.transport.reliable.KeepAliveDaemon

/*
* KeepAliveThread.java
*/
package org.activemq.transport.reliable;

import java.util.Iterator;

import javax.jms.JMSException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.activemq.message.KeepAlive;

import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;


/**
* KeepAliveDaemon keeps channels alive by sending KeepAlive packets on a
* specified interval. If the packets aren't don't get a receipt within a
* specified time, the channel will be forcefully disconnected.
*/
public class KeepAliveDaemon implements Runnable {

  private static final Log log = LogFactory.getLog(KeepAliveDaemon.class);

  private static KeepAliveDaemon instance = null;

  private long checkInterval = 15000L;
  private long lastCheck = 0;
  private Object lock = new Object();

  private SynchronizedBoolean started = new SynchronizedBoolean(false);
  private SynchronizedBoolean stopped = new SynchronizedBoolean(false);
  private CopyOnWriteArraySet monitoredChannels = new CopyOnWriteArraySet();
  private CopyOnWriteArraySet zombieChannelSuspects = new CopyOnWriteArraySet();


  /**
   * Constructs a new KeepAliveDaemon which will send KeepAlive packets
   * throught the wrapped channel.
   */
  protected KeepAliveDaemon() {

  }

  /**
   * Gets the current instance. Singletons implemented this way aren't popular
   * these days, but it might be good here. :)
   *
   * @return the daemon
   */
  public static synchronized KeepAliveDaemon getInstance() {
    if (instance == null)
      instance = new KeepAliveDaemon();
    return instance;
  }

  public void addMonitoredChannel(ReliableTransportChannel channel) {
    if (channel.getKeepAliveTimeout() <= 0)
      return;
    log.debug("Adding channel " + channel);
    // Check that the timeout isn't lower than our check interval as
    // this would cause the channel to constantly be disconnected.
    // This check should perhaps be done whenever the channel changes it's
    // interval, but in practice, this will probably never happen.
    if (channel.getKeepAliveTimeout() / 2 < checkInterval) {
      setCheckInterval(channel.getKeepAliveTimeout() / 2);
      log.info("Adjusting check interval to " + checkInterval + " as channel " + channel.toString()
          + " has lower timeout time than the current check interval.");
    }
    monitoredChannels.add(channel);
  }

  public void removeMonitoredChannel(ReliableTransportChannel channel) {
    log.debug("Removing channel " + channel);
    monitoredChannels.remove(channel);
  }

  /**
   * Sets the number of milliseconds between keep-alive checks are done.
   *
   * @param interval
   *            the interval
   */
  public void setCheckInterval(long interval) {
    this.checkInterval = interval;
    if (started.and(!stopped.get())) {
      restart();
    }
  }

  public long getCheckInterval() {
    return checkInterval;
  }

  public long getLastCheckTime() {
    return lastCheck;
  }

  public void start() {
    if (started.commit(false, true)) {
      log.debug("Scheduling keep-alive every " + checkInterval + " millisecond.");
      Thread t = new Thread(this);
      t.setName("KeepAliveDaemon");
      t.setDaemon(true);
      t.start();
    }
  }

  public void stop() {
    if (stopped.commit(false, true)) {
      synchronized (lock) {
        lock.notifyAll();
      }
      log.debug("Stopping keep-alive.");
    }
  }

  public void restart() {
    log.debug("Restarting keep-alive.");
    stop();
    start();
  }

  public void run() {
    lastCheck = System.currentTimeMillis() - checkInterval;
    while (!stopped.get()) {
      for (Iterator i = zombieChannelSuspects.iterator(); i.hasNext();) {
        ReliableTransportChannel channel = (ReliableTransportChannel) i.next();
        examineZombieSuspect(channel);
      }

      for (Iterator i = monitoredChannels.iterator(); i.hasNext();) {
        ReliableTransportChannel channel = (ReliableTransportChannel) i.next();
        if (!zombieChannelSuspects.contains(channel))
          examineChannel(channel);
      }
      lastCheck = System.currentTimeMillis();
      synchronized (lock) {
        try {
          lock.wait(checkInterval);
        } catch (InterruptedException e) {

        }
      }
    }
  }

  private void examineZombieSuspect(ReliableTransportChannel channel) {
    if ((channel.getLastReceiptTimestamp() + channel.getKeepAliveTimeout() * 2) < System.currentTimeMillis()) {
      // Timed out
      log.info("Forcing channel "
          + channel
          + " to disconnect since it "
          + (channel.getLastReceiptTimestamp() == 0 ? "never has responded " : "hasn't responded since "
              + new java.util.Date(channel.getLastReceiptTimestamp())) + " and has a timeout of "
          + channel.getKeepAliveTimeout());
      channel.forceDisconnect();
      // Remove it and wait for a reconnect
      zombieChannelSuspects.remove(channel);
    } else if ((channel.getLastReceiptTimestamp() + channel.getKeepAliveTimeout()) < System.currentTimeMillis()) {
      // Still a zombie suspect, but has not timed out
      log.debug("Still waiting for response from channel " + channel);
    } else {
      // It's alive again
      log.debug("Channel " + channel + " responded in time.");
      zombieChannelSuspects.remove(channel);
    }
  }

  private void examineChannel(ReliableTransportChannel channel) {
        // Is the channel stopping?
        if (channel.isPendingStop()) {
            removeMonitoredChannel(channel);
        } else {
            // Then it should be active
            // Keep pinging the channel periodically
            if ((channel.getLastReceiptTimestamp() + channel.getKeepAliveTimeout()) < System.currentTimeMillis()) {
                log.debug("Sending keep-alive on channel " + channel.toString());
                KeepAlive packet = new KeepAlive();
                packet.setReceiptRequired(true);
                boolean wasConnected = channel.isTransportConnected();
                try {
                    channel.asyncSendWithReceipt(packet);
                    zombieChannelSuspects.add(channel);
                } catch (JMSException e) {
                    // only report an error if the channel was connetected.
                    if (wasConnected) {
                        log.error("Error sending keep-alive to channel " + channel.toString()
                                + ". Treating as temporary problem.", e);
                    }
                }
            }
        }
    }
}
TOP

Related Classes of org.activemq.transport.reliable.KeepAliveDaemon

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.