package org.smartly.commons.network.socket.messages.multipart; import org.smartly.commons.Delegates; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Repository for Multipart message containers. * This pool checks for Timeouts in Multipart and raise Event when multipart is Full and ready. */ public class MultipartPool { // -------------------------------------------------------------------- // f i e l d s // -------------------------------------------------------------------- private static final int DEFAULT_TIMEOUT = 60 * 30 * 1000; // 30 minute timeout private static final Class EVENT_ON_TIMEOUT = Multipart.OnTimeOutListener.class; private static final Class EVENT_ON_FULL = Multipart.OnFullListener.class; private static final Class EVENT_ON_PART = Multipart.OnPartListener.class; //private final MultipartPoolEvents _events; private final Delegates.Handlers _eventHandlers; private final Map<String, Multipart> _data; private PoolGarbageCollector _gc; private int _timeOut; // -------------------------------------------------------------------- // c o n s t r u c t o r // -------------------------------------------------------------------- public MultipartPool() { this(DEFAULT_TIMEOUT); } public MultipartPool(int timeOut) { _timeOut = timeOut; _data = Collections.synchronizedMap(new HashMap<String, Multipart>()); //_events = new MultipartPoolEvents(); _eventHandlers = new Delegates.Handlers(); //-- gc for current pool --// _gc = new PoolGarbageCollector(this); _gc.start(); } @Override protected void finalize() throws Throwable { try { _gc.interrupt(); _gc = null; this.clear(); } catch (Throwable ignored) { } super.finalize(); } // -------------------------------------------------------------------- // p u b l i c // -------------------------------------------------------------------- public int size() { synchronized (_data) { return _data.size(); } } public void setTimeout(final int timeOut) { _timeOut = timeOut; } public int getTimeout() { return _timeOut; } public void clear() { //_events.clear(); _eventHandlers.clear(); synchronized (_data) { _data.clear(); } } /** * Add part into pool and returns pool size. * * @param part Part to add * @return Pool Size. */ public void add(final MultipartMessagePart part) { this.add(part, null); } /** * Add part into pool and returns pool size. * * @param part Part to Add * @param userData Custom data to pass to Multipart container */ public void add(final MultipartMessagePart part, final Object userData) { if (null != part) { this.addPart(part, userData); } this.size(); } // -------------------------------------------------------------------- // e v e n t // -------------------------------------------------------------------- public void onPart(final Multipart.OnPartListener listener) { _eventHandlers.add(listener); } public void onFull(final Multipart.OnFullListener listener) { _eventHandlers.add(listener); } public void onTimeOut(final Multipart.OnTimeOutListener listener) { _eventHandlers.add(listener); } // -------------------------------------------------------------------- // p r i v a t e // -------------------------------------------------------------------- private void doOnTimeOut(final Multipart multipart) { _eventHandlers.triggerAsync(EVENT_ON_TIMEOUT, multipart); } private void doOnFull(final Multipart multipart) { // remove from data list synchronized (_data) { _data.remove(multipart.getUid()); } // event _eventHandlers.triggerAsync(EVENT_ON_FULL, multipart); } private void doOnPart(final Multipart multipart, final MultipartMessagePart part) { // event _eventHandlers.triggerAsync(EVENT_ON_PART, multipart, part); } private Multipart addPart(final MultipartMessagePart part, final Object userData) { synchronized (_data) { final String key = part.getUid(); if (!_data.containsKey(key)) { // new multipart container final Multipart multipart = new Multipart(key, part.getPartCount()); _data.put(key, multipart); multipart.onFull(new Multipart.OnFullListener() { @Override public void handle(final Multipart sender) { doOnFull(sender); } }); multipart.onPart(new Multipart.OnPartListener() { @Override public void handle(final Multipart sender, final MultipartMessagePart part) { doOnPart(sender, part); } }); } final Multipart multipart = _data.get(key); if (null != multipart) { multipart.add(part); multipart.setUserData(userData); } return multipart; } } // -------------------------------------------------------------------- // S T A T I C - E M B E D D E D // -------------------------------------------------------------------- private static class PoolGarbageCollector extends Thread { private final MultipartPool _multipartPool; private final Map<String, Multipart> _pool; public PoolGarbageCollector(final MultipartPool multipartPool) { _multipartPool = multipartPool; _pool = multipartPool._data; super.setPriority(Thread.NORM_PRIORITY); super.setDaemon(true); } @Override public void run() { try { while (!super.isInterrupted()) { Thread.sleep(this.getTimeout()); try { this.garbage(); } catch (Throwable ignored) { } } } catch (InterruptedException ignored) { } } // ------------------------------------------------------------------------ // p r i v a t e // ------------------------------------------------------------------------ private int getTimeout() { return _multipartPool._timeOut; } private void doTimeout(final Multipart multipart) { _multipartPool.doOnTimeOut(multipart); } private void garbage() { synchronized (_pool) { final Collection<Multipart> items = _pool.values(); for (final Multipart multipart : items) { if (multipart.isExpired(this.getTimeout())) { _pool.remove(multipart.getUid()); this.doTimeout(multipart); } } } } } }