package vnet.sms.gateway.nettytest.embedded; import static org.apache.commons.lang.Validate.isTrue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import org.jboss.netty.channel.ChannelEvent; import org.jboss.netty.channel.ExceptionEvent; import com.google.common.base.Predicate; class FilteringChannelEventFuture<T extends ChannelEvent> implements Future<T>, ChannelEventSink<T> { private final Predicate<T> filter; private final CountDownLatch done = new CountDownLatch(1); private final AtomicReference<Value<T>> value = new AtomicReference<Value<T>>(); FilteringChannelEventFuture(final Predicate<T> filter) { this.filter = filter; } @Override public boolean cancel(final boolean mayInterruptIfRunning) { // We cannot be canceled return false; } @Override public T get() throws InterruptedException, ExecutionException { this.done.await(); return this.value.get().resolve(); } @Override public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (!this.done.await(timeout, unit)) { throw new TimeoutException("Timed get timed out after timeout of [" + timeout + "] " + unit); } return this.value.get().resolve(); } @Override public boolean isCancelled() { // We cannot be canceled return false; } @Override public boolean isDone() { return this.done.getCount() == 0; } @Override public boolean acceptsChannelEvent(final T candidate) { if (this.filter.apply(candidate) && this.value.compareAndSet(null, Value.fromChannelEvent(candidate))) { this.done.countDown(); return true; } return false; } @Override public boolean acceptsExceptionEvent(final ExceptionEvent e) { if (this.value.compareAndSet(null, Value.<T> fromException(e.getCause()))) { this.done.countDown(); return true; } return false; } private static final class Value<T extends ChannelEvent> { static <T extends ChannelEvent> Value<T> fromChannelEvent( final T channelEvent) { return new Value<T>(channelEvent, null); } static <T extends ChannelEvent> Value<T> fromException( final Throwable exception) { return new Value<T>(null, exception); } private final T channelEvent; private final Throwable exception; private Value(final T channelEvent, final Throwable exception) { isTrue(((channelEvent != null) && (exception == null)) || ((channelEvent == null) && (exception != null)), "Exactly one of 'channelEvent' and 'exception' must be non-null"); this.channelEvent = channelEvent; this.exception = exception; } T resolve() throws ExecutionException { if (this.exception != null) { throw new ExecutionException(this.exception); } return this.channelEvent; } } }