package org.handwerkszeug.riak.transport.internal; import static org.handwerkszeug.riak.util.Validation.notNull; import java.util.Iterator; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; import org.handwerkszeug.riak.Markers; import org.handwerkszeug.riak.RiakException; import org.handwerkszeug.riak._; import org.handwerkszeug.riak.model.RiakContentsResponse; import org.handwerkszeug.riak.model.RiakFuture; import org.handwerkszeug.riak.nls.Messages; import org.handwerkszeug.riak.op.RiakResponseHandler; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author taichi */ public class CompletionSupport { static Logger LOG = LoggerFactory.getLogger(CompletionSupport.class); final Channel channel; final Set<String> inProgress = new ConcurrentSkipListSet<String>(); final Queue<OperationTask> waitQueue = new ConcurrentLinkedQueue<OperationTask>(); final AtomicBoolean operationComplete = new AtomicBoolean(false); final ReentrantLock lock = new ReentrantLock(); public CompletionSupport(Channel channel) { notNull(channel, "channel"); this.channel = channel; } public void responseComplete() { complete(); } public void operationComplete() { this.operationComplete.compareAndSet(false, true); complete(); } protected void complete() { if (LOG.isDebugEnabled()) { LOG.debug( Markers.DETAIL, "queue:{} inprocess:{} complete:{}", new Object[] { this.waitQueue.size(), this.inProgress.size(), this.operationComplete.get() }); } if (this.operationComplete.get() && this.waitQueue.size() < 1 && this.inProgress.size() < 1) { close(this.channel); } } protected void close(Channel channel) { if (this.lock.tryLock()) { try { if (channel.isOpen()) { LOG.debug(Markers.BOUNDARY, Messages.CloseChannel); channel.close(); } } finally { this.lock.unlock(); } } else { LOG.debug(Markers.DETAIL, "race channel condition."); } } public void decrementProgress(String name) { this.channel.getPipeline().remove(name); this.inProgress.remove(name); } public CountDownRiakFuture newRiakFuture(String name) { return new CountDownRiakFuture(name, this); } public <T> RiakFuture handle(String name, Object send, RiakResponseHandler<T> users, MessageHandler handler) { CountDownRiakFuture future = newRiakFuture(name); ChannelHandler ch = new DefaultCompletionChannelHandler<T>(this, name, users, handler, future); return handle(name, send, users, ch, future); } public <T> RiakFuture handle(String name, Object send, RiakResponseHandler<T> users, ChannelHandler handler, RiakFuture future) { OperationTask tsk = new OperationTask(this.channel, send, name, handler); try { if (entry(tsk)) { tsk.execute(); } else { if (LOG.isDebugEnabled()) { LOG.debug(Markers.DETAIL, Messages.Queue, name); } this.waitQueue.add(tsk); } return future; } catch (Exception e) { completeOnError(name); throw new RiakException(e); } catch (Error e) { completeOnError(name); throw e; } } protected boolean entry(OperationTask tsk) { return this.inProgress.size() < 1 && this.inProgress.add(tsk.name); } protected void completeOnError(String name) { decrementProgress(name); operationComplete(); } protected void invokeNext() { for (Iterator<OperationTask> i = this.waitQueue.iterator(); i.hasNext();) { OperationTask tsk = i.next(); if (entry(tsk)) { i.remove(); tsk.execute(); break; } } } public RiakContentsResponse<_> newResponse() { return newResponse(_._); } public <T> RiakContentsResponse<T> newResponse(final T contents) { return new RiakContentsResponse<T>(contents); } }