View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.mina.util;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.concurrent.CancellationException;
24  import java.util.concurrent.CountDownLatch;
25  import java.util.concurrent.ExecutionException;
26  import java.util.concurrent.TimeUnit;
27  import java.util.concurrent.TimeoutException;
28  import java.util.concurrent.atomic.AtomicReference;
29  
30  import org.apache.mina.api.IoFuture;
31  import org.apache.mina.api.IoFutureListener;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  /**
36   * An abstract implementation of {@link IoFuture}. Owners of this future must implement {@link #cancelOwner(boolean)} to
37   * receive notifications of when the future should be canceled.
38   * <p>
39   * Concrete implementations of this abstract class should consider overriding the two methods
40   * {@link #scheduleResult(org.apache.mina.api.IoFutureListener, Object)} and
41   * {@link #scheduleException(org.apache.mina.api.IoFutureListener, Throwable)} so that listeners are called in a
42   * separate thread. The default implementations may end up calling the listener in the same thread that is registering
43   * the listener, before the registration has completed.
44   * 
45   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
46   */
47  public abstract class AbstractIoFuture<V> implements IoFuture<V> {
48  
49      static final Logger LOG = LoggerFactory.getLogger(AbstractIoFuture.class);
50  
51      private final CountDownLatch latch = new CountDownLatch(1);
52  
53      private final List<IoFutureListener<V>> listeners = new ArrayList<IoFutureListener<V>>();
54  
55      private final AtomicReference<Object> result = new AtomicReference<Object>();
56  
57      /**
58       * {@inheritDoc}
59       */
60      @Override
61      @SuppressWarnings({ "unchecked" })
62      public IoFuture<V> register(IoFutureListener<V> listener) {
63  
64          LOG.debug("registering listener {}", listener);
65  
66          synchronized (latch) {
67              if (!isDone()) {
68                  LOG.debug("future is not done, adding listener to listener set");
69                  listeners.add(listener);
70                  listener = null;
71              }
72          }
73  
74          if (listener != null) {
75              LOG.debug("future is done calling listener");
76              Object object = result.get();
77  
78              if (object instanceof Throwable) {
79                  scheduleException(listener, (Throwable) object);
80              } else {
81                  scheduleResult(listener, (V) object);
82              }
83          }
84  
85          return this;
86      }
87  
88      /**
89       * {@inheritDoc}
90       */
91      @Override
92      public boolean cancel(boolean mayInterruptIfRunning) {
93  
94          LOG.debug("Attempting to cancel");
95  
96          CancellationException ce = null;
97          synchronized (latch) {
98              if (!isCancelled() && !isDone() && cancelOwner(mayInterruptIfRunning)) {
99  
100                 LOG.debug("Successfully cancelled");
101 
102                 ce = new CancellationException();
103                 result.set(ce);
104             } else {
105                 LOG.debug("Unable to cancel");
106             }
107 
108             latch.countDown();
109         }
110 
111         if (ce != null) {
112             LOG.debug("Calling listeners");
113 
114             for (IoFutureListener<V> listener : listeners) {
115                 scheduleException(listener, ce);
116             }
117         }
118 
119         return ce != null;
120     }
121 
122     /**
123      * {@inheritDoc}
124      */
125     @Override
126     public boolean isCancelled() {
127         return result.get() instanceof CancellationException;
128     }
129 
130     /**
131      * {@inheritDoc}
132      */
133     @Override
134     public boolean isDone() {
135         return latch.getCount() == 0;
136     }
137 
138     /**
139      * {@inheritDoc}
140      */
141     @Override
142     @SuppressWarnings({ "unchecked" })
143     public V get() throws InterruptedException, ExecutionException {
144 
145         LOG.trace("Entering wait");
146         latch.await();
147         LOG.trace("Wait completed");
148 
149         if (isCancelled()) {
150             throw new CancellationException();
151         }
152 
153         Object object = result.get();
154 
155         if (object instanceof ExecutionException) {
156             throw (ExecutionException) object;
157         } else {
158             return (V) object;
159         }
160     }
161 
162     /**
163      * {@inheritDoc}
164      */
165     @Override
166     @SuppressWarnings({ "unchecked" })
167     public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
168 
169         LOG.trace("Entering wait");
170 
171         if (!latch.await(timeout, unit)) {
172             throw new TimeoutException();
173         }
174 
175         LOG.trace("Wait completed");
176 
177         if (isCancelled()) {
178             throw new CancellationException();
179         }
180 
181         Object object = result.get();
182 
183         if (object instanceof ExecutionException) {
184             throw (ExecutionException) object;
185         } else {
186             return (V) object;
187         }
188     }
189 
190     /**
191      * Notify the owner of this future that a client is attempting to cancel. This attempt will fail if the task has
192      * already completed, has already been cancelled, or could not be cancelled for some other reason. If successful,
193      * and this task has not started when <tt>cancel</tt> is called, this task should never run. If the task has already
194      * started, then the <tt>mayInterruptIfRunning</tt> parameter determines whether the thread executing this task
195      * should be interrupted in an attempt to stop the task.
196      * <p/>
197      * <p>
198      * After this method returns, subsequent calls to {@link #isDone} will always return <tt>true</tt>. Subsequent calls
199      * to {@link #isCancelled} will always return <tt>true</tt> if this method returned <tt>true</tt>.
200      * <p/>
201      * <b>Note:</b> implementations must never throw an exception.
202      * 
203      * @param mayInterruptIfRunning <tt>true</tt> if the owner executing this task should be interrupted; otherwise,
204      *        in-progress tasks are allowed to complete
205      * @return <tt>false</tt> if the task could not be cancelled, typically because it has already completed normally;
206      *         <tt>true</tt> otherwise
207      */
208     protected abstract boolean cancelOwner(boolean mayInterruptIfRunning);
209 
210     /**
211      * Default implementation to call a listener's {@link IoFutureListener#completed(Object)} method. Owners may
212      * override this method so that the listener is called from a thread pool.
213      * 
214      * @param listener the listener to call
215      * @param result the result to pass to the listener
216      */
217     protected void scheduleResult(IoFutureListener<V> listener, V result) {
218         LOG.debug("Calling the default result scheduler");
219 
220         try {
221             listener.completed(result);
222         } catch (Exception e) {
223             LOG.warn("Listener threw an exception", e);
224         }
225     }
226 
227     /**
228      * Default implementation to call a listener's {@link IoFutureListener#exception(Throwable)} method. Owners may
229      * override this method so that the listener is called from a thread pool.
230      * 
231      * @param listener the listener to call
232      * @param throwable the exception to pass to the listener
233      */
234     protected void scheduleException(IoFutureListener<V> listener, Throwable throwable) {
235         LOG.debug("Calling the default exception scheduler");
236 
237         try {
238             listener.exception(throwable);
239         } catch (Exception e) {
240             LOG.warn("Listener threw an exception", e);
241         }
242     }
243 
244     /**
245      * Set the future result of the executing task. Any {@link IoFutureListener}s are notified of the
246      * 
247      * @param value the value returned by the executing task.
248      */
249     protected final void setResult(V value) {
250         assert !isDone();
251 
252         synchronized (latch) {
253             result.set(value);
254             latch.countDown();
255         }
256 
257         for (IoFutureListener<V> listener : listeners) {
258             scheduleResult(listener, value);
259         }
260 
261         listeners.clear();
262     }
263 
264     /**
265      * Set the future result as a {@link Throwable}, indicating that a throwable was thrown while executing the task.
266      * This value is usually set by the future result owner.
267      * <p/>
268      * Any {@link IoFutureListener}s are notified of the exception.
269      * 
270      * @param t the throwable that was thrown while executing the task.
271      */
272     protected final void setException(Throwable t) {
273         assert !isDone();
274 
275         ExecutionException ee = new ExecutionException(t);
276 
277         synchronized (latch) {
278             result.set(ee);
279             latch.countDown();
280         }
281 
282         for (IoFutureListener<V> listener : listeners) {
283             scheduleException(listener, ee);
284         }
285 
286         listeners.clear();
287     }
288 }