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>