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