1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.transport.socket.apr;
21
22 import java.io.IOException;
23 import java.nio.ByteBuffer;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.concurrent.Executor;
29
30 import org.apache.mina.core.RuntimeIoException;
31 import org.apache.mina.core.buffer.IoBuffer;
32 import org.apache.mina.core.file.FileRegion;
33 import org.apache.mina.core.polling.AbstractPollingIoProcessor;
34 import org.apache.mina.util.CircularQueue;
35 import org.apache.tomcat.jni.Poll;
36 import org.apache.tomcat.jni.Pool;
37 import org.apache.tomcat.jni.Socket;
38 import org.apache.tomcat.jni.Status;
39
40
41
42
43
44
45
46 public final class AprIoProcessor extends AbstractPollingIoProcessor<AprSession> {
47 private static final int POLLSET_SIZE = 1024;
48
49 private final Map<Long, AprSession> allSessions =
50 new HashMap<Long, AprSession>(POLLSET_SIZE);
51
52 private final Object wakeupLock = new Object();
53 private final long wakeupSocket;
54 private volatile boolean toBeWakenUp;
55
56 private final long pool;
57 private final long bufferPool;
58 private final long pollset;
59 private final long[] polledSockets = new long[POLLSET_SIZE << 1];
60 private final List<AprSession> polledSessions =
61 new CircularQueue<AprSession>(POLLSET_SIZE);
62
63
64
65
66
67
68
69 public AprIoProcessor(Executor executor) {
70 super(executor);
71
72
73 pool = Pool.create(AprLibrary.getInstance().getRootPool());
74 bufferPool = Pool.create(AprLibrary.getInstance().getRootPool());
75
76 try {
77 wakeupSocket = Socket.create(
78 Socket.APR_INET, Socket.SOCK_DGRAM, Socket.APR_PROTO_UDP, pool);
79 } catch (RuntimeException e) {
80 throw e;
81 } catch (Error e) {
82 throw e;
83 } catch (Exception e) {
84 throw new RuntimeIoException("Failed to create a wakeup socket.", e);
85 }
86
87 boolean success = false;
88 long newPollset;
89 try {
90 newPollset = Poll.create(
91 POLLSET_SIZE,
92 pool,
93 Poll.APR_POLLSET_THREADSAFE,
94 Long.MAX_VALUE);
95
96 if (newPollset == 0) {
97 newPollset = Poll.create(
98 62,
99 pool,
100 Poll.APR_POLLSET_THREADSAFE,
101 Long.MAX_VALUE);
102 }
103
104 pollset = newPollset;
105 if (pollset < 0) {
106 if (Status.APR_STATUS_IS_ENOTIMPL(- (int) pollset)) {
107 throw new RuntimeIoException(
108 "Thread-safe pollset is not supported in this platform.");
109 }
110 }
111 success = true;
112 } catch (RuntimeException e) {
113 throw e;
114 } catch (Error e) {
115 throw e;
116 } catch (Exception e) {
117 throw new RuntimeIoException("Failed to create a pollset.", e);
118 } finally {
119 if (!success) {
120 dispose();
121 }
122 }
123 }
124
125
126
127
128 @Override
129 protected void dispose0() {
130 Poll.destroy(pollset);
131 Socket.close(wakeupSocket);
132 Pool.destroy(bufferPool);
133 Pool.destroy(pool);
134 }
135
136
137
138
139 @Override
140 protected int select() throws Exception {
141 return select(Integer.MAX_VALUE);
142 }
143
144
145
146
147 @Override
148 protected int select(long timeout) throws Exception {
149 int rv = Poll.poll(pollset, 1000 * timeout, polledSockets, false);
150 if (rv <= 0) {
151 if (rv != -120001) {
152 throwException(rv);
153 }
154
155 rv = Poll.maintain(pollset, polledSockets, true);
156 if (rv > 0) {
157 for (int i = 0; i < rv; i ++) {
158 long socket = polledSockets[i];
159 AprSession session = allSessions.get(socket);
160 if (session == null) {
161 continue;
162 }
163
164 int flag = (session.isInterestedInRead()? Poll.APR_POLLIN : 0) |
165 (session.isInterestedInWrite()? Poll.APR_POLLOUT : 0);
166
167 Poll.add(pollset, socket, flag);
168 }
169 } else if (rv < 0) {
170 throwException(rv);
171 }
172
173 return 0;
174 } else {
175 rv <<= 1;
176 if (!polledSessions.isEmpty()) {
177 polledSessions.clear();
178 }
179 for (int i = 0; i < rv; i ++) {
180 long flag = polledSockets[i];
181 long socket = polledSockets[++i];
182 if (socket == wakeupSocket) {
183 synchronized (wakeupLock) {
184 Poll.remove(pollset, wakeupSocket);
185 toBeWakenUp = false;
186 }
187 continue;
188 }
189 AprSession session = allSessions.get(socket);
190 if (session == null) {
191 continue;
192 }
193
194 session.setReadable((flag & Poll.APR_POLLIN) != 0);
195 session.setWritable((flag & Poll.APR_POLLOUT) != 0);
196
197 polledSessions.add(session);
198 }
199
200 return polledSessions.size();
201 }
202 }
203
204
205
206
207 @Override
208 protected boolean isSelectorEmpty() {
209 return allSessions.isEmpty();
210 }
211
212
213
214
215 @Override
216 protected void wakeup() {
217 if (toBeWakenUp) {
218 return;
219 }
220
221
222 synchronized (wakeupLock) {
223 toBeWakenUp = true;
224 Poll.add(pollset, wakeupSocket, Poll.APR_POLLOUT);
225 }
226 }
227
228
229
230
231 @Override
232 protected Iterator<AprSession> allSessions() {
233 return allSessions.values().iterator();
234 }
235
236
237
238
239 @Override
240 protected Iterator<AprSession> selectedSessions() {
241 return polledSessions.iterator();
242 }
243
244 @Override
245 protected void init(AprSession session) throws Exception {
246 long s = session.getDescriptor();
247 Socket.optSet(s, Socket.APR_SO_NONBLOCK, 1);
248 Socket.timeoutSet(s, 0);
249
250 int rv = Poll.add(pollset, s, Poll.APR_POLLIN);
251 if (rv != Status.APR_SUCCESS) {
252 throwException(rv);
253 }
254
255 session.setInterestedInRead(true);
256 allSessions.put(s, session);
257 }
258
259
260
261
262 @Override
263 protected void destroy(AprSession session) throws Exception {
264 if (allSessions.remove(session.getDescriptor()) == null) {
265
266 return;
267 }
268
269 int ret = Poll.remove(pollset, session.getDescriptor());
270 try {
271 if (ret != Status.APR_SUCCESS) {
272 throwException(ret);
273 }
274 } finally {
275 ret = Socket.close(session.getDescriptor());
276
277
278
279 Socket.destroy(session.getDescriptor());
280 session.setDescriptor(0);
281
282 if (ret != Status.APR_SUCCESS) {
283 throwException(ret);
284 }
285 }
286 }
287
288
289
290
291 @Override
292 protected SessionState state(AprSession session) {
293 long socket = session.getDescriptor();
294 if (socket != 0) {
295 return SessionState.OPEN;
296 } else if (allSessions.get(socket) != null) {
297 return SessionState.PREPARING;
298 } else {
299 return SessionState.CLOSED;
300 }
301 }
302
303
304
305
306 @Override
307 protected boolean isReadable(AprSession session) {
308 return session.isReadable();
309 }
310
311
312
313
314 @Override
315 protected boolean isWritable(AprSession session) {
316 return session.isWritable();
317 }
318
319
320
321
322 @Override
323 protected boolean isInterestedInRead(AprSession session) {
324 return session.isInterestedInRead();
325 }
326
327
328
329
330 @Override
331 protected boolean isInterestedInWrite(AprSession session) {
332 return session.isInterestedInWrite();
333 }
334
335
336
337
338 @Override
339 protected void setInterestedInRead(AprSession session, boolean value) throws Exception {
340 if (session.isInterestedInRead() == value) {
341 return;
342 }
343
344 int rv = Poll.remove(pollset, session.getDescriptor());
345 if (rv != Status.APR_SUCCESS) {
346 throwException(rv);
347 }
348
349 int flags = (value ? Poll.APR_POLLIN : 0)
350 | (session.isInterestedInWrite() ? Poll.APR_POLLOUT : 0);
351
352 rv = Poll.add(pollset, session.getDescriptor(), flags);
353 if (rv == Status.APR_SUCCESS) {
354 session.setInterestedInRead(value);
355 } else {
356 throwException(rv);
357 }
358 }
359
360
361
362
363 @Override
364 protected void setInterestedInWrite(AprSession session, boolean value) throws Exception {
365 if (session.isInterestedInWrite() == value) {
366 return;
367 }
368
369 int rv = Poll.remove(pollset, session.getDescriptor());
370 if (rv != Status.APR_SUCCESS) {
371 throwException(rv);
372 }
373
374 int flags = (session.isInterestedInRead() ? Poll.APR_POLLIN : 0)
375 | (value ? Poll.APR_POLLOUT : 0);
376
377 rv = Poll.add(pollset, session.getDescriptor(), flags);
378 if (rv == Status.APR_SUCCESS) {
379 session.setInterestedInWrite(value);
380 } else {
381 throwException(rv);
382 }
383 }
384
385
386
387
388 @Override
389 protected int read(AprSession session, IoBuffer buffer) throws Exception {
390 int bytes;
391 int capacity = buffer.remaining();
392
393 ByteBuffer b = Pool.alloc(bufferPool, capacity);
394 try {
395 bytes = Socket.recvb(
396 session.getDescriptor(), b, 0, capacity);
397 if (bytes > 0) {
398 b.position(0);
399 b.limit(bytes);
400 buffer.put(b);
401 } else if (bytes < 0) {
402 if (Status.APR_STATUS_IS_EOF(-bytes)) {
403 bytes = -1;
404 } else if (Status.APR_STATUS_IS_EAGAIN(-bytes)) {
405 bytes = 0;
406 } else {
407 throwException(bytes);
408 }
409 }
410 } finally {
411 Pool.clear(bufferPool);
412 }
413 return bytes;
414 }
415
416
417
418
419 @Override
420 protected int write(AprSession session, IoBuffer buf, int length) throws Exception {
421 int writtenBytes;
422 if (buf.isDirect()) {
423 writtenBytes = Socket.sendb(
424 session.getDescriptor(), buf.buf(), buf.position(), length);
425 } else {
426 writtenBytes = Socket.send(
427 session.getDescriptor(), buf.array(), buf.position(), length);
428 if (writtenBytes > 0) {
429 buf.skip(writtenBytes);
430 }
431 }
432
433 if (writtenBytes < 0) {
434 if (Status.APR_STATUS_IS_EAGAIN(-writtenBytes)) {
435 writtenBytes = 0;
436 } else if (Status.APR_STATUS_IS_EOF(-writtenBytes)) {
437 writtenBytes = 0;
438 } else {
439 throwException(writtenBytes);
440 }
441 }
442 return writtenBytes;
443 }
444
445
446
447
448 @Override
449 protected int transferFile(AprSession session, FileRegion region, int length)
450 throws Exception {
451 throw new UnsupportedOperationException();
452 }
453
454 private void throwException(int code) throws IOException {
455 throw new IOException(
456 org.apache.tomcat.jni.Error.strerror(-code) +
457 " (code: " + code + ")");
458 }
459 }