package org.openamq.pool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoFilterAdapter; import org.apache.mina.common.IoSession; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class PoolingFilter extends IoFilterAdapter implements Job.JobCompletionHandler { private static final Logger _logger = LoggerFactory.getLogger(PoolingFilter.class); public static final Set<EventType> READ_EVENTS = new HashSet<EventType>(Arrays.asList(EventType.RECEIVED)); public static final Set<EventType> WRITE_EVENTS = new HashSet<EventType>(Arrays.asList(EventType.WRITE)); private final ConcurrentMap<IoSession, Job> _jobs = new ConcurrentHashMap<IoSession, Job>(); private final ReferenceCountingExecutorService _poolReference; private final Set<EventType> _asyncTypes; private final String _name; private final int _maxEvents = Integer.getInteger("amqj.server.read_write_pool.max_events", 10); public PoolingFilter(ReferenceCountingExecutorService refCountingPool, Set<EventType> asyncTypes, String name) { _poolReference = refCountingPool; _asyncTypes = asyncTypes; _name = name; } private void fireEvent(IoSession session, Event event) { if (_asyncTypes.contains(event.getType())) { Job job = getJobForSession(session); job.acquire(); //prevents this job being removed from _jobs job.add(event); if (job.activate()) { _poolReference.getPool().execute(job); } } else { event.process(session); } } private Job getJobForSession(IoSession session) { Job job = _jobs.get(session); return job == null ? createJobForSession(session) : job; } private Job createJobForSession(IoSession session) { return addJobForSession(session, new Job(session, this, _maxEvents)); } private Job addJobForSession(IoSession session, Job job) { //atomic so ensures all threads agree on the same job Job existing = _jobs.putIfAbsent(session, job); return existing == null ? job : existing; } //Job.JobCompletionHandler public void completed(IoSession session, Job job) { if (job.isComplete()) { job.release(); if (!job.isReferenced()) { _jobs.remove(session); } } else { if (job.activate()) { _poolReference.getPool().execute(job); } } } //IoFilter methods that are processed by threads on the pool public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception { fireEvent(session, new Event(nextFilter, EventType.OPENED, null)); } public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception { fireEvent(session, new Event(nextFilter, EventType.CLOSED, null)); } public void sessionIdle(NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception { fireEvent(session, new Event(nextFilter, EventType.IDLE, status)); } public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception { fireEvent(session, new Event(nextFilter, EventType.EXCEPTION, cause)); } public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception { //ByteBufferUtil.acquireIfPossible( message ); fireEvent(session, new Event(nextFilter, EventType.RECEIVED, message)); } public void messageSent(NextFilter nextFilter, IoSession session, Object message) throws Exception { //ByteBufferUtil.acquireIfPossible( message ); fireEvent(session, new Event(nextFilter, EventType.SENT, message)); } public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception { fireEvent(session, new Event(nextFilter, EventType.WRITE, writeRequest)); } //IoFilter methods that are processed on current thread (NOT on pooled thread) public void filterClose(NextFilter nextFilter, IoSession session) throws Exception { nextFilter.filterClose(session); } public void sessionCreated(NextFilter nextFilter, IoSession session) { nextFilter.sessionCreated(session); } public String toString() { return _name; } // LifeCycle methods public void init() { _logger.info("Init called on PoolingFilter " + toString()); // called when the filter is initialised in the chain. If the reference count is // zero this acquire will initialise the pool _poolReference.acquireExecutorService(); } public void destroy() { _logger.info("Destroy called on PoolingFilter " + toString()); // when the reference count gets to zero we release the executor service _poolReference.releaseExecutorService(); } }