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