You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by ad...@apache.org on 2011/07/29 08:26:14 UTC
svn commit: r1152118 - in /mina/branches/3.0: ./ core/
core/src/main/java/org/apache/mina/api/
core/src/main/java/org/apache/mina/util/
core/src/test/java/org/apache/mina/util/
Author: adc
Date: Fri Jul 29 06:26:13 2011
New Revision: 1152118
URL: http://svn.apache.org/viewvc?rev=1152118&view=rev
Log:
Fixed IoFuture and added tests
Added:
mina/branches/3.0/core/src/main/java/org/apache/mina/util/AbstractIoFuture.java
- copied, changed from r1151890, mina/branches/3.0/core/src/main/java/org/apache/mina/util/DefaultIoFuture.java
mina/branches/3.0/core/src/test/java/org/apache/mina/util/
mina/branches/3.0/core/src/test/java/org/apache/mina/util/AbstractIoFutureTest.java
Removed:
mina/branches/3.0/core/src/main/java/org/apache/mina/util/DefaultIoFuture.java
Modified:
mina/branches/3.0/core/pom.xml
mina/branches/3.0/core/src/main/java/org/apache/mina/api/IoFutureListener.java
mina/branches/3.0/pom.xml
Modified: mina/branches/3.0/core/pom.xml
URL: http://svn.apache.org/viewvc/mina/branches/3.0/core/pom.xml?rev=1152118&r1=1152117&r2=1152118&view=diff
==============================================================================
--- mina/branches/3.0/core/pom.xml (original)
+++ mina/branches/3.0/core/pom.xml Fri Jul 29 06:26:13 2011
@@ -35,5 +35,13 @@
<symbolicName>${project.groupId}.core</symbolicName>
<exportedPackage>${project.groupId}</exportedPackage>
</properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
</project>
Modified: mina/branches/3.0/core/src/main/java/org/apache/mina/api/IoFutureListener.java
URL: http://svn.apache.org/viewvc/mina/branches/3.0/core/src/main/java/org/apache/mina/api/IoFutureListener.java?rev=1152118&r1=1152117&r2=1152118&view=diff
==============================================================================
--- mina/branches/3.0/core/src/main/java/org/apache/mina/api/IoFutureListener.java (original)
+++ mina/branches/3.0/core/src/main/java/org/apache/mina/api/IoFutureListener.java Fri Jul 29 06:26:13 2011
@@ -26,8 +26,12 @@ package org.apache.mina.api;
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
public interface IoFutureListener<V> {
+
/**
* Called if there was an exception by the task as it was executing.
+ * Expect {@link java.util.concurrent.CancellationException} of the
+ * future was canceled or {@link java.util.concurrent.ExecutionException}
+ * if there was an error executing the task the future was waiting on.
*
* @param t an instance of {@link Throwable}
*/
Copied: mina/branches/3.0/core/src/main/java/org/apache/mina/util/AbstractIoFuture.java (from r1151890, mina/branches/3.0/core/src/main/java/org/apache/mina/util/DefaultIoFuture.java)
URL: http://svn.apache.org/viewvc/mina/branches/3.0/core/src/main/java/org/apache/mina/util/AbstractIoFuture.java?p2=mina/branches/3.0/core/src/main/java/org/apache/mina/util/AbstractIoFuture.java&p1=mina/branches/3.0/core/src/main/java/org/apache/mina/util/DefaultIoFuture.java&r1=1151890&r2=1152118&rev=1152118&view=diff
==============================================================================
--- mina/branches/3.0/core/src/main/java/org/apache/mina/util/DefaultIoFuture.java (original)
+++ mina/branches/3.0/core/src/main/java/org/apache/mina/util/AbstractIoFuture.java Fri Jul 29 06:26:13 2011
@@ -20,6 +20,7 @@ package org.apache.mina.util;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -32,91 +33,40 @@ import org.slf4j.LoggerFactory;
import org.apache.mina.api.IoFuture;
import org.apache.mina.api.IoFutureListener;
+
/**
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
-public class DefaultIoFuture<V> implements IoFuture<V> {
- static final Logger LOG = LoggerFactory.getLogger(DefaultIoFuture.class);
+public abstract class AbstractIoFuture<V> implements IoFuture<V> {
+
+ static final Logger LOG = LoggerFactory.getLogger(AbstractIoFuture.class);
private final CountDownLatch latch = new CountDownLatch(1);
private final List<IoFutureListener<V>> listeners = new ArrayList<IoFutureListener<V>>();
private final AtomicReference<Object> result = new AtomicReference<Object>();
- private final FutureResultOwner owner;
- private volatile boolean canceled;
-
- /**
- * There may be many futures but there will be a single result owner. The
- * interface {@link FutureResultOwner} allows instances of {@link DefaultIoFuture}
- * to call cancel on the actual owner of the future result.
- *
- * @param owner the owner of the future result
- */
- DefaultIoFuture(FutureResultOwner owner) {
- this.owner = owner;
- }
-
- /**
- * Set the future result as a {@link Throwable}, indicating that a
- * throwable was thrown while executing the task. This value is usually
- * set by the future result owner.
- * <p/>
- * Any {@link IoFutureListener}s are notified of the exception.
- *
- * @param t the throwable that was thrown while executing the task.
- */
- public void exception(Throwable t) {
- assert !isDone();
-
- synchronized (latch) {
- result.set(t);
- latch.countDown();
-
- for (IoFutureListener<V> listener : listeners) {
- listener.exception(t);
- }
- }
-
- listeners.clear();
- }
-
- /**
- * Set the future result of the executing task. Any {@link IoFutureListener}s
- * are notified of the
- *
- * @param value the value returned by the executing task.
- */
- public void set(V value) {
- assert !isDone();
-
- synchronized (latch) {
- result.set(value);
- latch.countDown();
- }
-
- for (IoFutureListener<V> listener : listeners) {
- listener.completed(value);
- }
-
- listeners.clear();
- }
/**
* {@inheritDoc}
*/
@SuppressWarnings({"unchecked"})
public IoFuture<V> register(IoFutureListener<V> listener) {
+
+ LOG.debug("registering listener {}", listener);
+
synchronized (latch) {
if (!isDone()) {
+ LOG.debug("future is not done, adding listener to listener set");
listeners.add(listener);
listener = null;
}
}
if (listener != null) {
+ LOG.debug("future is done calling listener");
Object object = result.get();
if (object instanceof Throwable) {
- listener.exception(new ExecutionException((Throwable) object));
+ scheduleException(listener, (Throwable) object);
} else {
- listener.completed((V) object);
+ scheduleResult(listener, (V) object);
}
}
@@ -127,20 +77,39 @@ public class DefaultIoFuture<V> implemen
* {@inheritDoc}
*/
public boolean cancel(boolean mayInterruptIfRunning) {
+
+ LOG.debug("Attempting to cancel");
+
+ CancellationException ce = null;
synchronized (latch) {
- boolean c = !canceled && !isDone() && owner.cancel(mayInterruptIfRunning);
+ if (!isCancelled() && !isDone() && cancelOwner(mayInterruptIfRunning)) {
- if (c) canceled = true;
+ LOG.debug("Successfully cancelled");
- return canceled;
+ ce = new CancellationException();
+ result.set(ce);
+ } else {
+ LOG.debug("Unable to cancel");
+ }
+ latch.countDown();
}
+
+ if (ce != null) {
+ LOG.debug("Calling listeners");
+
+ for (IoFutureListener<V> listener : listeners) {
+ scheduleException(listener, ce);
+ }
+ }
+
+ return ce != null;
}
/**
* {@inheritDoc}
*/
public boolean isCancelled() {
- return canceled;
+ return result.get() instanceof CancellationException;
}
/**
@@ -155,11 +124,16 @@ public class DefaultIoFuture<V> implemen
*/
@SuppressWarnings({"unchecked"})
public V get() throws InterruptedException, ExecutionException {
+
+ LOG.trace("Entering wait");
latch.await();
+ LOG.trace("Wait completed");
+
+ if (isCancelled()) throw new CancellationException();
Object object = result.get();
- if (object instanceof Throwable) {
- throw new ExecutionException((Throwable) object);
+ if (object instanceof ExecutionException) {
+ throw (ExecutionException) object;
} else {
return (V) object;
}
@@ -170,17 +144,125 @@ public class DefaultIoFuture<V> implemen
*/
@SuppressWarnings({"unchecked"})
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+
+ LOG.trace("Entering wait");
if (!latch.await(timeout, unit)) throw new TimeoutException();
+ LOG.trace("Wait completed");
+
+ if (isCancelled()) throw new CancellationException();
Object object = result.get();
- if (object instanceof Throwable) {
- throw new ExecutionException((Throwable) object);
+ if (object instanceof ExecutionException) {
+ throw (ExecutionException) object;
} else {
return (V) object;
}
}
- public static interface FutureResultOwner {
- boolean cancel(boolean mayInterruptIfRunning);
+ /**
+ * Notify the owner of this future that a client is attempting to cancel.
+ * This attempt will fail if the task has already completed, has already
+ * been cancelled, or could not be cancelled for some other reason. If
+ * successful, and this task has not started when <tt>cancel</tt> is called,
+ * this task should never run. If the task has already started,
+ * then the <tt>mayInterruptIfRunning</tt> parameter determines
+ * whether the thread executing this task should be interrupted in
+ * an attempt to stop the task.
+ * <p/>
+ * <p>After this method returns, subsequent calls to {@link #isDone} will
+ * always return <tt>true</tt>. Subsequent calls to {@link #isCancelled}
+ * will always return <tt>true</tt> if this method returned <tt>true</tt>.
+ * <p/>
+ * <b>Note:</b> implementations must never throw an exception.
+ *
+ * @param mayInterruptIfRunning <tt>true</tt> if the owner executing this
+ * task should be interrupted; otherwise,
+ * in-progress tasks are allowed to complete
+ * @return <tt>false</tt> if the task could not be cancelled,
+ * typically because it has already completed normally;
+ * <tt>true</tt> otherwise
+ */
+ abstract protected boolean cancelOwner(boolean mayInterruptIfRunning);
+
+ /**
+ * Default implementation to call a listener's {@link IoFutureListener#completed(Object)}
+ * method. Owners may override this method so that the listener is called
+ * from a thread pool.
+ *
+ * @param listener the listener to call
+ * @param result the result to pass to the listener
+ */
+ protected void scheduleResult(IoFutureListener<V> listener, V result) {
+ LOG.debug("Calling the default result scheduler");
+ try {
+ listener.completed(result);
+ } catch (Throwable t) {
+ LOG.warn("Listener threw an exception", t);
+ }
+ }
+
+ /**
+ * Default implementation to call a listener's {@link IoFutureListener#exception(Throwable)}
+ * method. Owners may override this method so that the listener is called
+ * from a thread pool.
+ *
+ * @param listener the listener to call
+ * @param throwable the exception to pass to the listener
+ */
+ protected void scheduleException(IoFutureListener<V> listener, Throwable throwable) {
+ LOG.debug("Calling the default exception scheduler");
+ try {
+ listener.exception(throwable);
+ } catch (Throwable t) {
+ LOG.warn("Listener threw an exception", t);
+ }
+ }
+
+
+ /**
+ * Set the future result of the executing task. Any {@link IoFutureListener}s
+ * are notified of the
+ *
+ * @param value the value returned by the executing task.
+ */
+ protected final void setResult(V value) {
+ assert !isDone();
+
+ synchronized (latch) {
+ result.set(value);
+ latch.countDown();
+ }
+
+ for (IoFutureListener<V> listener : listeners) {
+ scheduleResult(listener, value);
+ }
+
+ listeners.clear();
+ }
+
+ /**
+ * Set the future result as a {@link Throwable}, indicating that a
+ * throwable was thrown while executing the task. This value is usually
+ * set by the future result owner.
+ * <p/>
+ * Any {@link IoFutureListener}s are notified of the exception.
+ *
+ * @param t the throwable that was thrown while executing the task.
+ */
+ protected final void setException(Throwable t) {
+ assert !isDone();
+
+ ExecutionException ee = new ExecutionException(t);
+
+ synchronized (latch) {
+ result.set(ee);
+ latch.countDown();
+ }
+
+ for (IoFutureListener<V> listener : listeners) {
+ scheduleException(listener, ee);
+ }
+
+ listeners.clear();
}
}
Added: mina/branches/3.0/core/src/test/java/org/apache/mina/util/AbstractIoFutureTest.java
URL: http://svn.apache.org/viewvc/mina/branches/3.0/core/src/test/java/org/apache/mina/util/AbstractIoFutureTest.java?rev=1152118&view=auto
==============================================================================
--- mina/branches/3.0/core/src/test/java/org/apache/mina/util/AbstractIoFutureTest.java (added)
+++ mina/branches/3.0/core/src/test/java/org/apache/mina/util/AbstractIoFutureTest.java Fri Jul 29 06:26:13 2011
@@ -0,0 +1,384 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.mina.util;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+import org.mockito.Matchers;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import org.apache.mina.api.IoFutureListener;
+
+
+/**
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public class AbstractIoFutureTest {
+
+ @Test
+ public void testSet() {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ future.setResult(true);
+
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ @SuppressWarnings({"unchecked", "ThrowableResultOfMethodCallIgnored"})
+ public void testSetListeners() {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+ IoFutureListener<Boolean> listener = mock(IoFutureListener.class);
+
+ future.register(listener);
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ future.setResult(true);
+
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+
+ verify(listener).completed(true);
+ verify(listener, never()).exception(Matchers.<Throwable>any());
+ }
+
+ @Test
+ @SuppressWarnings({"unchecked", "ThrowableResultOfMethodCallIgnored"})
+ public void testSetListenersAlreadySet() {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+ IoFutureListener<Boolean> listener = mock(IoFutureListener.class);
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ future.setResult(true);
+ future.register(listener);
+
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+
+ verify(listener).completed(true);
+ verify(listener, never()).exception(Matchers.<Throwable>any());
+ }
+
+ @Test
+ public void testTimedGet() {
+ final MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(1000);
+ future.setResult(true);
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }).start();
+
+ try {
+ assertTrue(future.get(1, TimeUnit.DAYS));
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+ } catch (InterruptedException e) {
+ fail("This future was not interrupted");
+ } catch (ExecutionException ee) {
+ fail("This future did not have an execution exception");
+ } catch (TimeoutException e) {
+ fail("This future was not interrupted");
+ }
+ }
+
+ @Test
+ public void testException() {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ future.setException(new NullPointerException());
+
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+
+ try {
+ future.get();
+ fail("This future had an execution exception");
+ } catch (InterruptedException e) {
+ fail("This future was not interrupted");
+ } catch (ExecutionException ee) {
+ assertTrue(ee.getCause() instanceof NullPointerException);
+ }
+
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ @SuppressWarnings({"ThrowableResultOfMethodCallIgnored", "unchecked"})
+ public void testExceptionListeners() {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+ IoFutureListener<Boolean> listener = mock(IoFutureListener.class);
+
+ future.register(listener);
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ future.setException(new NullPointerException());
+
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+
+ verify(listener).exception(argThat(matchesExecutionException()));
+ verify(listener, never()).completed(Matchers.<Boolean>any());
+ }
+
+ @Test
+ @SuppressWarnings({"ThrowableResultOfMethodCallIgnored", "unchecked"})
+ public void testExceptionListenersExceptionAlreadySet() {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+ IoFutureListener<Boolean> listener = mock(IoFutureListener.class);
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ future.setException(new NullPointerException());
+ future.register(listener);
+
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+
+ verify(listener).exception(argThat(matchesExecutionException()));
+ verify(listener, never()).completed(Matchers.<Boolean>any());
+ }
+
+ @Test
+ public void testImmediateExceptionForTimedGet() {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ future.setException(new NullPointerException());
+
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+
+ try {
+ future.get(1, TimeUnit.DAYS);
+ fail("This future had an execution exception");
+ } catch (InterruptedException e) {
+ fail("This future was not interrupted");
+ } catch (ExecutionException ee) {
+ assertTrue(ee.getCause() instanceof NullPointerException);
+ } catch (TimeoutException e) {
+ fail("This future was not interrupted");
+ }
+
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ public void testTimedExceptionForTimedGet() {
+ final MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(1000);
+ future.setException(new NullPointerException());
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }).start();
+
+ try {
+ assertTrue(future.get(1, TimeUnit.DAYS));
+ } catch (InterruptedException e) {
+ fail("This future was not interrupted");
+ } catch (ExecutionException ee) {
+ } catch (TimeoutException e) {
+ fail("This future was not interrupted");
+ }
+
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ public void testCancel() throws Exception {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+ doReturn(true).when(future).cancelOwner(anyBoolean());
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+ assertTrue(future.cancel(false));
+ assertTrue(future.isCancelled());
+ assertTrue(future.isDone());
+ assertFalse(future.cancel(false));
+ assertTrue(future.isCancelled());
+ assertTrue(future.isDone());
+
+ try {
+ future.get();
+ fail("This future was canceled");
+ } catch (CancellationException ignore) {
+ }
+ }
+
+ @Test
+ public void testCancelUncancelableOwner() throws Exception {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+ doReturn(false).when(future).cancelOwner(anyBoolean());
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+ assertFalse(future.cancel(false));
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+ }
+
+ @Test
+ public void testCancelFinishedFuture() throws Exception {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+ doReturn(true).when(future).cancelOwner(anyBoolean());
+
+ future.setResult(true);
+
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+ assertFalse(future.cancel(false));
+ assertFalse(future.isCancelled());
+ assertTrue(future.isDone());
+
+ assertTrue(future.get());
+ }
+
+ @Test
+ @SuppressWarnings({"unchecked", "ThrowableResultOfMethodCallIgnored"})
+ public void testCanceledListeners() {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+ doReturn(true).when(future).cancelOwner(anyBoolean());
+ IoFutureListener<Boolean> listener = mock(IoFutureListener.class);
+
+ future.register(listener);
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ future.cancel(true);
+
+ assertTrue(future.isCancelled());
+ assertTrue(future.isDone());
+
+ verify(listener).exception(Matchers.<CancellationException>any());
+ verify(listener, never()).completed(Matchers.<Boolean>any());
+ }
+
+ @Test
+ @SuppressWarnings({"unchecked", "ThrowableResultOfMethodCallIgnored"})
+ public void testCanceledListenersAlreadySet() {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+ doReturn(true).when(future).cancelOwner(anyBoolean());
+ IoFutureListener<Boolean> listener = mock(IoFutureListener.class);
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ future.cancel(true);
+ future.register(listener);
+
+ assertTrue(future.isCancelled());
+ assertTrue(future.isDone());
+
+ verify(listener).exception(Matchers.<CancellationException>any());
+ verify(listener, never()).completed(Matchers.<Boolean>any());
+ }
+
+ @Test
+ public void testTimeout() {
+ MockAbstractIoFuture<Boolean> future = spy(new MockAbstractIoFuture<Boolean>());
+
+ assertFalse(future.isCancelled());
+ assertFalse(future.isDone());
+
+ try {
+ future.get(10, TimeUnit.MILLISECONDS);
+ fail("This future has timed out");
+ } catch (InterruptedException e) {
+ fail("This future was not interrupted");
+ } catch (ExecutionException ee) {
+ fail("This future did not have an execution exception");
+ } catch (TimeoutException e) {
+ }
+ }
+
+ private static Matcher<Throwable> matchesExecutionException() {
+ return new BaseMatcher<Throwable>() {
+ @Override
+ public boolean matches(Object item) {
+ return item instanceof ExecutionException && ((ExecutionException) item).getCause() instanceof NullPointerException;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("ExecutionException(NullPointerException)");
+ }
+ };
+ }
+
+ public static class MockAbstractIoFuture<V> extends AbstractIoFuture<V> {
+
+ @Override
+ protected boolean cancelOwner(boolean mayInterruptIfRunning) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
Modified: mina/branches/3.0/pom.xml
URL: http://svn.apache.org/viewvc/mina/branches/3.0/pom.xml?rev=1152118&r1=1152117&r2=1152118&view=diff
==============================================================================
--- mina/branches/3.0/pom.xml (original)
+++ mina/branches/3.0/pom.xml Fri Jul 29 06:26:13 2011
@@ -125,6 +125,13 @@
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.8.5</version>
+ <scope>test</scope>
+ </dependency>
+
</dependencies>
</dependencyManagement>