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   */
20  package org.apache.mina.common.support;
21  
22  import java.util.ArrayList;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import org.apache.mina.common.ExceptionMonitor;
27  import org.apache.mina.common.IoFuture;
28  import org.apache.mina.common.IoFutureListener;
29  import org.apache.mina.common.IoSession;
30  
31  /**
32   * A default implementation of {@link IoFuture}.
33   *  
34   * @author The Apache Directory Project (mina-dev@directory.apache.org)
35   * @version $Rev: 599822 $, $Date: 2007-11-30 22:54:07 +0900 (Fri, 30 Nov 2007) $
36   */
37  public class DefaultIoFuture implements IoFuture {
38  
39      private final IoSession session;
40      private final Object lock;
41      private IoFutureListener firstListener;
42      private List otherListeners;
43      private Object result;
44      private boolean ready;
45      private int waiters;
46  
47      /**
48       * Creates a new instance.
49       *
50       * @param session an {@link IoSession} which is associated with this future
51       */
52      public DefaultIoFuture(IoSession session) {
53          this.session = session;
54          this.lock = this;
55      }
56  
57      /**
58       * Creates a new instance.
59       *
60       * @param session an {@link IoSession} which is associated with this future
61       */
62      public DefaultIoFuture(IoSession session, Object lock) {
63          this.session = session;
64          this.lock = lock;
65      }
66  
67      public IoSession getSession() {
68          return session;
69      }
70      
71      public Object getLock() {
72          return lock;
73      }
74  
75      public void join() {
76          awaitUninterruptibly();
77      }
78  
79      public boolean join(long timeoutMillis) {
80          return awaitUninterruptibly(timeoutMillis);
81      }
82  
83      private IoFuture awaitUninterruptibly() {
84          synchronized (lock) {
85              while (!ready) {
86                  waiters++;
87                  try {
88                      lock.wait();
89                  } catch (InterruptedException e) {
90                  } finally {
91                      waiters--;
92                  }
93              }
94          }
95  
96          return this;
97      }
98  
99      private boolean awaitUninterruptibly(long timeoutMillis) {
100         try {
101             return await0(timeoutMillis, false);
102         } catch (InterruptedException e) {
103             throw new InternalError();
104         }
105     }
106 
107     private boolean await0(long timeoutMillis, boolean interruptable) throws InterruptedException {
108         long startTime = timeoutMillis <= 0 ? 0 : System.currentTimeMillis();
109         long waitTime = timeoutMillis;
110 
111         synchronized (lock) {
112             if (ready) {
113                 return ready;
114             } else if (waitTime <= 0) {
115                 return ready;
116             }
117 
118             waiters++;
119             try {
120                 for (;;) {
121                     try {
122                         lock.wait(waitTime);
123                     } catch (InterruptedException e) {
124                         if (interruptable) {
125                             throw e;
126                         }
127                     }
128 
129                     if (ready) {
130                         return true;
131                     } else {
132                         waitTime = timeoutMillis
133                                 - (System.currentTimeMillis() - startTime);
134                         if (waitTime <= 0) {
135                             return ready;
136                         }
137                     }
138                 }
139             } finally {
140                 waiters--;
141             }
142         }
143     }
144 
145     public boolean isReady() {
146         synchronized (lock) {
147             return ready;
148         }
149     }
150 
151     /**
152      * Sets the result of the asynchronous operation, and mark it as finished.
153      */
154     protected void setValue(Object newValue) {
155         synchronized (lock) {
156             // Allow only once.
157             if (ready) {
158                 return;
159             }
160 
161             result = newValue;
162             ready = true;
163             if (waiters > 0) {
164                 lock.notifyAll();
165             }
166         }
167 
168         notifyListeners();
169     }
170 
171     /**
172      * Returns the result of the asynchronous operation.
173      */
174     protected Object getValue() {
175         synchronized (lock) {
176             return result;
177         }
178     }
179 
180     public void addListener(IoFutureListener listener) {
181         if (listener == null) {
182             throw new NullPointerException("listener");
183         }
184 
185         boolean notifyNow = false;
186         synchronized (lock) {
187             if (ready) {
188                 notifyNow = true;
189             } else {
190                 if (firstListener == null) {
191                     firstListener = listener;
192                 } else {
193                     if (otherListeners == null) {
194                         otherListeners = new ArrayList(1);
195                     }
196                     otherListeners.add(listener);
197                 }
198             }
199         }
200 
201         if (notifyNow) {
202             notifyListener(listener);
203         }
204     }
205 
206     public void removeListener(IoFutureListener listener) {
207         if (listener == null) {
208             throw new NullPointerException("listener");
209         }
210 
211         synchronized (lock) {
212             if (!ready) {
213                 if (listener == firstListener) {
214                     if (otherListeners != null && !otherListeners.isEmpty()) {
215                         firstListener = (IoFutureListener) otherListeners.remove(0);
216                     } else {
217                         firstListener = null;
218                     }
219                 } else if (otherListeners != null) {
220                     otherListeners.remove(listener);
221                 }
222             }
223         }
224     }
225 
226     private void notifyListeners() {
227         // There won't be any visibility problem or concurrent modification
228         // because 'ready' flag will be checked against both addListener and
229         // removeListener calls.
230         if (firstListener != null) {
231             notifyListener(firstListener);
232             firstListener = null;
233 
234             if (otherListeners != null) {
235                 for (Iterator i = otherListeners.iterator(); i.hasNext(); ) {
236                     notifyListener((IoFutureListener) i.next());
237                 }
238                 otherListeners = null;
239             }
240         }
241     }
242 
243     private void notifyListener(IoFutureListener l) {
244         try {
245             l.operationComplete(this);
246         } catch (Throwable t) {
247             ExceptionMonitor.getInstance().exceptionCaught(t);
248         }
249     }
250 }