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.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 TimedFilteringChannelEventFuture<T extends ChannelEvent> implements
TimedFuture<T>, ChannelEventSink<T> {
private final Predicate<T> filter;
private final CountDownLatch done = new CountDownLatch(
1);
private final AtomicReference<TimedValue<T>> timedValue = new AtomicReference<TimedValue<T>>();
private final long start = System.currentTimeMillis();
TimedFilteringChannelEventFuture(final Predicate<T> filter) {
this.filter = filter;
}
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
// We cannot be canceled
return false;
}
@Override
public TimedFuture.Value<T> get() throws InterruptedException,
ExecutionException {
this.done.await();
return this.timedValue.get();
}
@Override
public TimedFuture.Value<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.timedValue.get();
}
@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.timedValue.compareAndSet(null,
TimedValue.<T> fromChannelEvent(candidate, this.start))) {
this.done.countDown();
return true;
}
return false;
}
@Override
public boolean acceptsExceptionEvent(final ExceptionEvent e) {
if (this.timedValue.compareAndSet(null,
TimedValue.<T> fromException(e.getCause(), this.start))) {
this.done.countDown();
return true;
}
return false;
}
private static final class TimedValue<T extends ChannelEvent> implements
TimedFuture.Value<T> {
static <T extends ChannelEvent> TimedValue<T> fromChannelEvent(
final T channelEvent, final long start) {
return new TimedValue<T>(channelEvent, null, start);
}
static <T extends ChannelEvent> TimedValue<T> fromException(
final Throwable exception, final long start) {
return new TimedValue<T>(null, exception, start);
}
private final T channelEvent;
private final Throwable exception;
private final long start;
private final long end;
private TimedValue(final T channelEvent, final Throwable exception,
final long start) {
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;
this.start = start;
this.end = System.currentTimeMillis();
}
@Override
public T get() throws ExecutionException {
if (this.exception != null) {
throw new ExecutionException(this.exception);
}
return this.channelEvent;
}
@Override
public long elapsedDurationMillis() {
return this.end - this.start;
}
@Override
public String toString() {
return "TimedValue@" + this.hashCode() + "[channelEvent: "
+ this.channelEvent + "|exception: " + this.exception
+ "|start: " + this.start + "|end: " + this.end + "]";
}
}
}