1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
39
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
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
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
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 }