package com.voxeo.moho.presence.sip.impl; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadPoolExecutor; import org.apache.log4j.Logger; import com.voxeo.moho.presence.NotifyDispatcher; /** * * MemoryNotifyDispatcher from SIPoint * */ public class MemoryNotifyDispatcher implements NotifyDispatcher { private static final Logger LOG = Logger.getLogger(MemoryNotifyDispatcher.class); int _remainingThreshold = 0; int _cap = 10000; Executor _executor = null; boolean _stop = false; BlockingQueue<NotifyRequest> _queue = null; // The tolal length of the queue public int getCapability() { return _cap; } // currently used length public int remainingCapacity() { return _queue.remainingCapacity(); } public boolean isBusy() { return remainingCapacity() < _remainingThreshold; } public boolean isFull() { boolean result = false; result = (remainingCapacity() == 0); return result; } /** * Adds the specified element to the tail of this queue, waiting if necessary * for space to become available. * * @param nr * @return * @throws InterruptedException */ public void put(NotifyRequest nr) throws InterruptedException { _queue.put(nr); } /** * Retrieves and removes the head of this queue, waiting if no elements are * present on this queue * * * @return * @throws InterruptedException */ public NotifyRequest take() throws InterruptedException { return _queue.take(); } public MemoryNotifyDispatcher(Executor executor, int cap) { /** * by limiting the max thread number of this independent thread pool, we can * avoid too many blocked threads caused by the SipMessage.send(), this * happens when the remote TCP socket is down, and the OS has to wait for a * time period befroe it throws an IOException */ _executor = executor; _cap = cap; _remainingThreshold = (int) (_cap * 0.25); _queue = new LinkedBlockingQueue<NotifyRequest>(_cap); } public void shutdown() { _stop = true; // return immediately if full _queue.offer(ShutdownSignal.SHUTDOWN_SIGNAL); } public void run() { String name = Thread.currentThread().getName(); Thread.currentThread().setName(toString()); LOG.info("Started " + toString()); while (!_stop) { try { NotifyRequest nr = take(); if (nr == ShutdownSignal.SHUTDOWN_SIGNAL) { break; } sendIt(nr); } catch (InterruptedException e) { continue; } catch (Throwable t) { LOG.error("MemoryNotifyDispatcher Error", t); continue; } } LOG.info("Stopped " + toString()); Thread.currentThread().setName(name); } /** * Dispath a NOTIFY message to a new thread in the thread pool to send it. * <br> * If the thread pool is full, this method will block until it can get a spare * thread to send the notify. * * @param session * @param id * @return */ protected void sendIt(NotifyRequest nr) { try { // wait until the thread pool is able to send it while (true) { try { _executor.execute(nr); break; } catch (RejectedExecutionException e) { if (_executor instanceof ThreadPoolExecutor) { ThreadPoolExecutor exec = (ThreadPoolExecutor) _executor; LOG.warn("MemoryNotifySender thread pool is full. Thread num = " + exec.getPoolSize()); } else { LOG.warn("MemoryNotifySender thread pool is full"); } try { Thread.sleep(500); } catch (InterruptedException e1) { //ignore } } } } catch (Throwable t) { LOG.error("MemoryNotifyDispatcher Error", t); } finally { ; } } // indicate the shutdown of the queue worker thread static class ShutdownSignal extends NotifyRequest { static final ShutdownSignal SHUTDOWN_SIGNAL = new ShutdownSignal(); ShutdownSignal() { } } @Override public String toString() { return "MemNotifySender[cap=" + _cap + "]"; } }