View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.transport.socket.apr;
21  
22  import java.io.IOException;
23  import java.net.InetSocketAddress;
24  import java.net.SocketAddress;
25  import java.util.Iterator;
26  import java.util.Queue;
27  import java.util.concurrent.ConcurrentLinkedQueue;
28  import java.util.concurrent.Executor;
29  
30  import org.apache.mina.core.RuntimeIoException;
31  import org.apache.mina.core.polling.AbstractPollingIoAcceptor;
32  import org.apache.mina.core.service.IoAcceptor;
33  import org.apache.mina.core.service.IoProcessor;
34  import org.apache.mina.core.service.IoService;
35  import org.apache.mina.core.service.SimpleIoProcessorPool;
36  import org.apache.mina.core.service.TransportMetadata;
37  import org.apache.mina.transport.socket.DefaultSocketSessionConfig;
38  import org.apache.mina.transport.socket.SocketAcceptor;
39  import org.apache.mina.transport.socket.SocketSessionConfig;
40  import org.apache.tomcat.jni.Address;
41  import org.apache.tomcat.jni.Poll;
42  import org.apache.tomcat.jni.Pool;
43  import org.apache.tomcat.jni.Socket;
44  import org.apache.tomcat.jni.Status;
45  
46  /**
47   * {@link IoAcceptor} for APR based socket transport (TCP/IP).
48   *
49   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
50   */
51  public final class AprSocketAcceptor extends AbstractPollingIoAcceptor<AprSession, Long> implements SocketAcceptor {
52      /** 
53       * This constant is deduced from the APR code. It is used when the timeout
54       * has expired while doing a poll() operation.
55       */ 
56      private static final int APR_TIMEUP_ERROR = -120001;
57  
58      private static final int POLLSET_SIZE = 1024;
59  
60      private final Object wakeupLock = new Object();
61      private volatile long wakeupSocket;
62      private volatile boolean toBeWakenUp;
63  
64      private volatile long pool;
65      private volatile long pollset; // socket poller
66      private final long[] polledSockets = new long[POLLSET_SIZE << 1];
67      private final Queue<Long> polledHandles =
68          new ConcurrentLinkedQueue<Long>();
69  
70      /**
71       * Constructor for {@link AprSocketAcceptor} using default parameters (multiple thread model).
72       */
73      public AprSocketAcceptor() {
74          super(new DefaultSocketSessionConfig(), AprIoProcessor.class);
75          ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
76      }
77  
78      /**
79       * Constructor for {@link AprSocketAcceptor} using default parameters, and 
80       * given number of {@link AprIoProcessor} for multithreading I/O operations.
81       * 
82       * @param processorCount the number of processor to create and place in a
83       * {@link SimpleIoProcessorPool} 
84       */
85      public AprSocketAcceptor(int processorCount) {
86          super(new DefaultSocketSessionConfig(), AprIoProcessor.class, processorCount);
87          ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
88      }
89  
90      /**
91       *  Constructor for {@link AprSocketAcceptor} with default configuration but a
92        *  specific {@link AprIoProcessor}, useful for sharing the same processor over multiple
93        *  {@link IoService} of the same type.
94        * @param processor the processor to use for managing I/O events
95        */
96      public AprSocketAcceptor(IoProcessor<AprSession> processor) {
97          super(new DefaultSocketSessionConfig(), processor);
98          ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
99      }
100 
101     /**
102      *  Constructor for {@link AprSocketAcceptor} with a given {@link Executor} for handling 
103      *  connection events and a given {@link AprIoProcessor} for handling I/O events, useful for 
104      *  sharing the same processor and executor over multiple {@link IoService} of the same type.
105      * @param executor the executor for connection
106      * @param processor the processor for I/O operations
107      */
108     public AprSocketAcceptor(Executor executor,
109             IoProcessor<AprSession> processor) {
110         super(new DefaultSocketSessionConfig(), executor, processor);
111         ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
112     }
113 
114     /**
115      * {@inheritDoc}
116      */
117     @Override
118     protected AprSession accept(IoProcessor<AprSession> processor, Long handle) throws Exception {
119         long s = Socket.accept(handle);
120         boolean success = false;
121         try {
122             AprSession result = new AprSocketSession(this, processor, s);
123             success = true;
124             return result;
125         } finally {
126             if (!success) {
127                 Socket.close(s);
128             }
129         }
130     }
131 
132     /**
133      * {@inheritDoc}
134      */
135     @Override
136     protected Long open(SocketAddress localAddress) throws Exception {
137         InetSocketAddress la = (InetSocketAddress) localAddress;
138         long handle = Socket.create(
139                 Socket.APR_INET, Socket.SOCK_STREAM, Socket.APR_PROTO_TCP, pool);
140 
141         boolean success = false;
142         try {
143             int result = Socket.optSet(handle, Socket.APR_SO_NONBLOCK, 1);
144             if (result != Status.APR_SUCCESS) {
145                 throwException(result);
146             }
147             result = Socket.timeoutSet(handle, 0);
148             if (result != Status.APR_SUCCESS) {
149                 throwException(result);
150             }
151 
152             // Configure the server socket,
153             result = Socket.optSet(handle, Socket.APR_SO_REUSEADDR, isReuseAddress()? 1 : 0);
154             if (result != Status.APR_SUCCESS) {
155                 throwException(result);
156             }
157             result = Socket.optSet(handle, Socket.APR_SO_RCVBUF, getSessionConfig().getReceiveBufferSize());
158             if (result != Status.APR_SUCCESS) {
159                 throwException(result);
160             }
161 
162             // and bind.
163             long sa;
164             if (la != null) {
165                 if (la.getAddress() == null) {
166                     sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, la.getPort(), 0, pool);
167                 } else {
168                     sa = Address.info(la.getAddress().getHostAddress(), Socket.APR_INET, la.getPort(), 0, pool);
169                 }
170             } else {
171                 sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, 0, 0, pool);
172             }
173 
174             result = Socket.bind(handle, sa);
175             if (result != Status.APR_SUCCESS) {
176                 throwException(result);
177             }
178             result = Socket.listen(handle, getBacklog());
179             if (result != Status.APR_SUCCESS) {
180                 throwException(result);
181             }
182 
183             result = Poll.add(pollset, handle, Poll.APR_POLLIN);
184             if (result != Status.APR_SUCCESS) {
185                 throwException(result);
186             }
187             success = true;
188         } finally {
189             if (!success) {
190                 close(handle);
191             }
192         }
193         return handle;
194     }
195 
196     /**
197      * {@inheritDoc}
198      */
199     @Override
200     protected void init() throws Exception {
201         // initialize a memory pool for APR functions
202         pool = Pool.create(AprLibrary.getInstance().getRootPool());
203 
204         wakeupSocket = Socket.create(
205                 Socket.APR_INET, Socket.SOCK_DGRAM, Socket.APR_PROTO_UDP, pool);
206 
207         pollset = Poll.create(
208                         POLLSET_SIZE,
209                         pool,
210                         Poll.APR_POLLSET_THREADSAFE,
211                         Long.MAX_VALUE);
212 
213         if (pollset <= 0) {
214             pollset = Poll.create(
215                     62,
216                     pool,
217                     Poll.APR_POLLSET_THREADSAFE,
218                     Long.MAX_VALUE);
219         }
220 
221         if (pollset <= 0) {
222             if (Status.APR_STATUS_IS_ENOTIMPL(- (int) pollset)) {
223                 throw new RuntimeIoException(
224                         "Thread-safe pollset is not supported in this platform.");
225             }
226         }
227     }
228 
229     /**
230      * {@inheritDoc}
231      */
232     @Override
233     protected void destroy() throws Exception {
234         if (wakeupSocket > 0) {
235             Socket.close(wakeupSocket);
236         }
237         if (pollset > 0) {
238             Poll.destroy(pollset);
239         }
240         if (pool > 0) {
241             Pool.destroy(pool);
242         }
243     }
244 
245     /**
246      * {@inheritDoc}
247      */
248     @Override
249     protected SocketAddress localAddress(Long handle) throws Exception {
250         long la = Address.get(Socket.APR_LOCAL, handle);
251         return new InetSocketAddress(Address.getip(la), Address.getInfo(la).port);
252     }
253 
254     /**
255      * {@inheritDoc}
256      */
257     @Override
258     protected int select() throws Exception {
259         int rv = Poll.poll(pollset, Integer.MAX_VALUE, polledSockets, false);
260         if (rv <= 0) {
261             // We have had an error. It can simply be that we have reached
262             // the timeout (very unlikely, as we have set it to MAX_INTEGER)
263             if (rv != APR_TIMEUP_ERROR) {
264                 // It's not a timeout being exceeded. Throw the error
265                 throwException(rv);
266             }
267 
268             rv = Poll.maintain(pollset, polledSockets, true);
269             if (rv > 0) {
270                 for (int i = 0; i < rv; i ++) {
271                     Poll.add(pollset, polledSockets[i], Poll.APR_POLLIN);
272                 }
273             } else if (rv < 0) {
274                 throwException(rv);
275             }
276 
277             return 0;
278         } else {
279             rv <<= 1;
280             if (!polledHandles.isEmpty()) {
281                 polledHandles.clear();
282             }
283 
284             for (int i = 0; i < rv; i ++) {
285                 long flag = polledSockets[i];
286                 long socket = polledSockets[++i];
287                 if (socket == wakeupSocket) {
288                     synchronized (wakeupLock) {
289                         Poll.remove(pollset, wakeupSocket);
290                         toBeWakenUp = false;
291                     }
292                     continue;
293                 }
294 
295                 if ((flag & Poll.APR_POLLIN) != 0) {
296                     polledHandles.add(socket);
297                 }
298             }
299             return polledHandles.size();
300         }
301     }
302 
303     /**
304      * {@inheritDoc}
305      */
306     @Override
307     protected Iterator<Long> selectedHandles() {
308         return polledHandles.iterator();
309     }
310 
311     /**
312      * {@inheritDoc}
313      */
314     @Override
315     protected void close(Long handle) throws Exception {
316         Poll.remove(pollset, handle);
317         int result = Socket.close(handle);
318         if (result != Status.APR_SUCCESS) {
319             throwException(result);
320         }
321     }
322 
323     /**
324      * {@inheritDoc}
325      */
326     @Override
327     protected void wakeup() {
328         if (toBeWakenUp) {
329             return;
330         }
331 
332         // Add a dummy socket to the pollset.
333         synchronized (wakeupLock) {
334             toBeWakenUp = true;
335             Poll.add(pollset, wakeupSocket, Poll.APR_POLLOUT);
336         }
337     }
338 
339     /**
340      * {@inheritDoc}
341      */
342     @Override
343     public InetSocketAddress getLocalAddress() {
344         return (InetSocketAddress) super.getLocalAddress();
345     }
346 
347     /**
348      * {@inheritDoc}
349      */
350     @Override
351     public InetSocketAddress getDefaultLocalAddress() {
352         return (InetSocketAddress) super.getDefaultLocalAddress();
353     }
354 
355     /**
356      * {@inheritDoc}
357      */
358     public void setDefaultLocalAddress(InetSocketAddress localAddress) {
359         super.setDefaultLocalAddress(localAddress);
360     }
361 
362     /**
363      * {@inheritDoc}
364      */
365     public TransportMetadata getTransportMetadata() {
366         return AprSocketSession.METADATA;
367     }
368 
369     /**
370      * {@inheritDoc}
371      */
372     @Override
373     public SocketSessionConfig getSessionConfig() {
374         return (SocketSessionConfig) super.getSessionConfig();
375     }
376 
377     /**
378      * Convert an APR code into an Exception with the corresponding message
379      * @param code error number
380      * @throws IOException the generated exception
381      */
382     private void throwException(int code) throws IOException {
383         throw new IOException(
384                 org.apache.tomcat.jni.Error.strerror(-code) +
385                 " (code: " + code + ")");
386     }
387 }