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;
21  
22  import java.net.ConnectException;
23  import java.net.SocketAddress;
24  import java.util.Iterator;
25  import java.util.Queue;
26  import java.util.concurrent.ConcurrentLinkedQueue;
27  import java.util.concurrent.Executor;
28  import java.util.concurrent.ExecutorService;
29  import java.util.concurrent.LinkedBlockingQueue;
30  import java.util.concurrent.RejectedExecutionException;
31  import java.util.concurrent.ThreadPoolExecutor;
32  import java.util.concurrent.TimeUnit;
33  import java.util.concurrent.atomic.AtomicInteger;
34  
35  import org.apache.mina.util.NamePreservingRunnable;
36  
37  /**
38   * @author The Apache MINA Project (dev@mina.apache.org)
39   * @version $Rev: 607163 $, $Date: 2007-12-27 20:20:07 -0700 (Thu, 27 Dec 2007) $
40   */
41  public abstract class AbstractPollingIoConnector<T extends AbstractIoSession, H>
42          extends AbstractIoConnector {
43  
44      private static final AtomicInteger id = new AtomicInteger();
45  
46      private final Object lock = new Object();
47      private final String threadName;
48      private final Executor executor;
49      private final boolean createdExecutor;
50      private final Queue<ConnectionRequest> connectQueue = new ConcurrentLinkedQueue<ConnectionRequest>();
51      private final Queue<ConnectionRequest> cancelQueue = new ConcurrentLinkedQueue<ConnectionRequest>();
52      private final IoProcessor<T> processor;
53      private final boolean createdProcessor;
54  
55      private final ServiceOperationFuture disposalFuture =
56          new ServiceOperationFuture();
57      private volatile boolean selectable;
58      private Worker worker;
59  
60      protected AbstractPollingIoConnector(IoSessionConfig sessionConfig, Class<? extends IoProcessor<T>> processorClass) {
61          this(sessionConfig, null, new SimpleIoProcessorPool<T>(processorClass), true);
62      }
63  
64      protected AbstractPollingIoConnector(IoSessionConfig sessionConfig, Class<? extends IoProcessor<T>> processorClass, int processorCount) {
65          this(sessionConfig, null, new SimpleIoProcessorPool<T>(processorClass, processorCount), true);
66      }
67  
68      protected AbstractPollingIoConnector(IoSessionConfig sessionConfig, IoProcessor<T> processor) {
69          this(sessionConfig, null, processor, false);
70      }
71  
72      protected AbstractPollingIoConnector(IoSessionConfig sessionConfig, Executor executor, IoProcessor<T> processor) {
73          this(sessionConfig, executor, processor, false);
74      }
75  
76      private AbstractPollingIoConnector(IoSessionConfig sessionConfig, Executor executor, IoProcessor<T> processor, boolean createdProcessor) {
77          super(sessionConfig);
78          
79          if (processor == null) {
80              throw new NullPointerException("processor");
81          }
82          
83          if (executor == null) {
84              this.executor = new ThreadPoolExecutor(
85                      1, 1, 1L, TimeUnit.SECONDS,
86                      new LinkedBlockingQueue<Runnable>());
87              this.createdExecutor = true;
88          } else {
89              this.executor = executor;
90              this.createdExecutor = false;
91          }
92  
93          this.threadName = getClass().getSimpleName() + '-' + id.incrementAndGet();
94          this.processor = processor;
95          this.createdProcessor = createdProcessor;
96  
97          try {
98              init();
99              selectable = true;
100         } catch (RuntimeException e){
101             throw e;
102         } catch (Exception e) {
103             throw new RuntimeIoException("Failed to initialize.", e);
104         } finally {
105             if (!selectable) {
106                 try {
107                     destroy();
108                 } catch (Exception e) {
109                     ExceptionMonitor.getInstance().exceptionCaught(e);
110                 }
111             }
112         }
113     }
114 
115     protected abstract void init() throws Exception;
116     protected abstract void destroy() throws Exception;
117     protected abstract H newHandle(SocketAddress localAddress) throws Exception;
118     protected abstract boolean connect(H handle, SocketAddress remoteAddress) throws Exception;
119     protected abstract boolean finishConnect(H handle) throws Exception;
120     protected abstract T newSession(IoProcessor<T> processor, H handle) throws Exception;
121     protected abstract void close(H handle) throws Exception;
122     protected abstract void wakeup();
123     protected abstract boolean select(int timeout) throws Exception;
124     protected abstract Iterator<H> selectedHandles();
125     protected abstract Iterator<H> allHandles();
126     protected abstract void register(H handle, ConnectionRequest request) throws Exception;
127     protected abstract ConnectionRequest connectionRequest(H handle);
128 
129     @Override
130     protected final IoFuture dispose0() throws Exception {
131         if (!disposalFuture.isDone()) {
132             try {
133                 startupWorker();
134                 wakeup();
135             } catch (RejectedExecutionException e) {
136                 if (createdExecutor) {
137                     // Ignore.
138                 } else {
139                     throw e;
140                 }
141             }
142         }
143         return disposalFuture;
144     }
145 
146     @Override
147     @SuppressWarnings("unchecked")
148     protected final ConnectFuture connect0(
149             SocketAddress remoteAddress, SocketAddress localAddress,
150             IoSessionInitializer<? extends ConnectFuture> sessionInitializer) {
151         H handle = null;
152         boolean success = false;
153         try {
154             handle = newHandle(localAddress);
155             if (connect(handle, remoteAddress)) {
156                 ConnectFuture future = new DefaultConnectFuture();
157                 T session = newSession(processor, handle);
158                 finishSessionInitialization(session, future, sessionInitializer);
159                 // Forward the remaining process to the IoProcessor.
160                 session.getProcessor().add(session);
161                 success = true;
162                 return future;
163             }
164 
165             success = true;
166         } catch (Exception e) {
167             return DefaultConnectFuture.newFailedFuture(e);
168         } finally {
169             if (!success && handle != null) {
170                 try {
171                     close(handle);
172                 } catch (Exception e) {
173                     ExceptionMonitor.getInstance().exceptionCaught(e);
174                 }
175             }
176         }
177 
178         ConnectionRequest request = new ConnectionRequest(handle, sessionInitializer);
179         connectQueue.add(request);
180         startupWorker();
181         wakeup();
182 
183         return request;
184     }
185 
186     private void startupWorker() {
187         if (!selectable) {
188             connectQueue.clear();
189             cancelQueue.clear();
190         }
191 
192         synchronized (lock) {
193             if (worker == null) {
194                 worker = new Worker();
195                 executor.execute(new NamePreservingRunnable(worker, threadName));
196             }
197         }
198     }
199 
200     private int registerNew() {
201         int nHandles = 0;
202         for (; ;) {
203             ConnectionRequest req = connectQueue.poll();
204             if (req == null) {
205                 break;
206             }
207 
208             H handle = req.handle;
209             try {
210                 register(handle, req);
211                 nHandles ++;
212             } catch (Exception e) {
213                 req.setException(e);
214                 try {
215                     close(handle);
216                 } catch (Exception e2) {
217                     ExceptionMonitor.getInstance().exceptionCaught(e2);
218                 }
219             }
220         }
221         return nHandles;
222     }
223 
224     private int cancelKeys() {
225         int nHandles = 0;
226         for (; ;) {
227             ConnectionRequest req = cancelQueue.poll();
228             if (req == null) {
229                 break;
230             }
231 
232             H handle = req.handle;
233             try {
234                 close(handle);
235             } catch (Exception e) {
236                 ExceptionMonitor.getInstance().exceptionCaught(e);
237             } finally {
238                 nHandles ++;
239             }
240         }
241         return nHandles;
242     }
243 
244     @SuppressWarnings("unchecked")
245     private int processSessions(Iterator<H> handlers) {
246         int nHandles = 0;
247         while (handlers.hasNext()) {
248             H handle = handlers.next();
249             handlers.remove();
250 
251             ConnectionRequest entry = connectionRequest(handle);
252             boolean success = false;
253             try {
254                 if (finishConnect(handle)) {
255                     T session = newSession(processor, handle);
256                     finishSessionInitialization(session, entry, entry.getSessionInitializer());
257                     // Forward the remaining process to the IoProcessor.
258                     session.getProcessor().add(session);
259                     nHandles ++;
260                 }
261                 success = true;
262             } catch (Throwable e) {
263                 entry.setException(e);
264             } finally {
265                 if (!success) {
266                     cancelQueue.offer(entry);
267                 }
268             }
269         }
270         return nHandles;
271     }
272 
273     private void processTimedOutSessions(Iterator<H> handles) {
274         long currentTime = System.currentTimeMillis();
275 
276         while (handles.hasNext()) {
277             H handle = handles.next();
278             ConnectionRequest entry = connectionRequest(handle);
279 
280             if (currentTime >= entry.deadline) {
281                 entry.setException(
282                         new ConnectException("Connection timed out."));
283                 cancelQueue.offer(entry);
284             }
285         }
286     }
287 
288     private class Worker implements Runnable {
289 
290         public void run() {
291             int nHandles = 0;
292             while (selectable) {
293                 try {
294                     boolean selected = select(1000);
295 
296                     nHandles += registerNew();
297 
298                     if (selected) {
299                         nHandles -= processSessions(selectedHandles());
300                     }
301 
302                     processTimedOutSessions(allHandles());
303 
304                     nHandles -= cancelKeys();
305 
306                     if (nHandles == 0) {
307                         synchronized (lock) {
308                             if (connectQueue.isEmpty()) {
309                                 worker = null;
310                                 break;
311                             }
312                         }
313                     }
314                 } catch (Throwable e) {
315                     ExceptionMonitor.getInstance().exceptionCaught(e);
316 
317                     try {
318                         Thread.sleep(1000);
319                     } catch (InterruptedException e1) {
320                         ExceptionMonitor.getInstance().exceptionCaught(e1);
321                     }
322                 }
323             }
324             
325             if (selectable && isDisposing()) {
326                 selectable = false;
327                 try {
328                     if (createdProcessor) {
329                         processor.dispose();
330                     }
331                 } finally {
332                     try {
333                         destroy();
334                     } catch (Exception e) {
335                         ExceptionMonitor.getInstance().exceptionCaught(e);
336                     } finally {
337                         disposalFuture.setDone();
338                         if (createdExecutor) {
339                             ((ExecutorService) executor).shutdown();
340                         }
341                     }
342                 }
343             }
344         }
345     }
346 
347     protected final class ConnectionRequest extends DefaultConnectFuture {
348         private final H handle;
349         private final long deadline;
350         private final IoSessionInitializer<? extends ConnectFuture> sessionInitializer;
351 
352         public ConnectionRequest(H handle, IoSessionInitializer<? extends ConnectFuture> callback) {
353             this.handle = handle;
354             long timeout = getConnectTimeoutMillis();
355             if (timeout <= 0L) {
356                 this.deadline = Long.MAX_VALUE;
357             } else {
358                 this.deadline = System.currentTimeMillis() + timeout;
359             }
360             this.sessionInitializer = callback;
361         }
362 
363         public H getHandle() {
364             return handle;
365         }
366 
367         public long getDeadline() {
368             return deadline;
369         }
370 
371         public IoSessionInitializer<? extends ConnectFuture> getSessionInitializer() {
372             return sessionInitializer;
373         }
374         
375         @Override
376         public void cancel() {
377             super.cancel();
378             cancelQueue.add(this);
379             startupWorker();
380             wakeup();
381         }
382     }
383 }