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