package vnet.routing.netty.server.support.window; import static org.apache.commons.lang.Validate.notEmpty; import static org.apache.commons.lang.Validate.notNull; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; import vnet.routing.netty.server.support.window.spi.UnreleasedMessagesHandler; /** * @author obergner * */ public class WindowStore implements WindowStoreMBean { private final String ownerUid; private final int maximumCapacity; private final Map<Long, Serializable> messageIdToMessage; private final UnreleasedMessagesHandler unreleasedMessagesHandler; private final ReentrantLock capacityEnsuringLock = new ReentrantLock(); private volatile boolean shutDown = false; public WindowStore(final String ownerUid, final int maximumCapacity, final UnreleasedMessagesHandler unreleasedMessagesHandler) { notEmpty(ownerUid, "Argument 'ownerUid' must be neither null nor empty. Got: " + ownerUid); this.ownerUid = ownerUid; this.maximumCapacity = maximumCapacity; this.messageIdToMessage = new HashMap<Long, Serializable>( maximumCapacity); this.unreleasedMessagesHandler = unreleasedMessagesHandler; } /** * @see vnet.routing.netty.server.support.window.WindowStoreMBean#getOwnerUid() */ @Override public final String getOwnerUid() { return this.ownerUid; } public final String getObjectName() { return "service=WindowStore,owner=" + this.ownerUid; } /** * @see vnet.routing.netty.server.support.window.WindowStoreMBean#getMaximumCapacity() */ @Override public final int getMaximumCapacity() { return this.maximumCapacity; } /** * @see vnet.routing.netty.server.support.window.WindowStoreMBean#getCurrentMessageCount() */ @Override public final int getCurrentMessageCount() { return this.messageIdToMessage.size(); } /** * @see vnet.routing.netty.server.support.window.WindowStoreMBean#storeMessage(long, * java.io.Serializable) */ @Override public boolean storeMessage(final long messageId, final Serializable message) throws IllegalArgumentException { notNull(message, "Cannot store a null message"); ensureNotShutDown(); try { this.capacityEnsuringLock.lock(); return storeMessageHoldingLock(messageId, message); } finally { this.capacityEnsuringLock.unlock(); } } private void ensureNotShutDown() throws IllegalArgumentException { if (this.shutDown) { throw new IllegalArgumentException( "This WindowStore has already been shut down"); } } private boolean storeMessageHoldingLock(final long messageId, final Serializable message) throws IllegalArgumentException { if (this.messageIdToMessage.size() >= this.maximumCapacity) { return false; } final Serializable storedMessageHavingSameId = this.messageIdToMessage .get(messageId); if (storedMessageHavingSameId != null) { throw new IllegalArgumentException("Another message [" + storedMessageHavingSameId + "] having the same ID [" + messageId + "] has already been stored"); } this.messageIdToMessage.put(messageId, message); return true; } /** * @see vnet.routing.netty.server.support.window.WindowStoreMBean#releaseMessage(long) */ @Override public Serializable releaseMessage(final long messageId) throws IllegalArgumentException { ensureNotShutDown(); try { this.capacityEnsuringLock.lock(); return releaseMessageHoldingLock(messageId); } finally { this.capacityEnsuringLock.unlock(); } } private Serializable releaseMessageHoldingLock(final long messageId) throws IllegalArgumentException { final Serializable releasedMessage = this.messageIdToMessage .remove(messageId); if (releasedMessage == null) { throw new IllegalArgumentException( "Illegal attempt to release a non-existing message: no message having the supplied ID [" + messageId + "] is stored"); } return releasedMessage; } /** * @see vnet.routing.netty.server.support.window.WindowStoreMBean#shutDown() */ @Override public void shutDown() { if (this.shutDown) { return; } try { this.capacityEnsuringLock.lock(); this.unreleasedMessagesHandler.onShutDown(Collections .unmodifiableMap(this.messageIdToMessage)); this.shutDown = true; // Volatile write } finally { this.capacityEnsuringLock.unlock(); } } @Override public String toString() { return "WindowStore@" + hashCode() + "[ownerUid = " + this.ownerUid + "|maximumCapacity = " + this.maximumCapacity + "|messageIdToMessage = " + this.messageIdToMessage + "|unreleasedMessagesHandler = " + this.unreleasedMessagesHandler + "|capacityEnsuringLock = " + this.capacityEnsuringLock + "|shutDown = " + this.shutDown + "]"; } }