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  
62      private volatile long wakeupSocket;
63  
64      private volatile boolean toBeWakenUp;
65  
66      private volatile long pool;
67  
68      private volatile long pollset; // socket poller
69  
70      private final long[] polledSockets = new long[POLLSET_SIZE << 1];
71  
72      private final Queue<Long> polledHandles = new ConcurrentLinkedQueue<Long>();
73  
74      /**
75       * Constructor for {@link AprSocketAcceptor} using default parameters (multiple thread model).
76       */
77      public AprSocketAcceptor() {
78          super(new DefaultSocketSessionConfig(), AprIoProcessor.class);
79          ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
80      }
81  
82      /**
83       * Constructor for {@link AprSocketAcceptor} using default parameters, and 
84       * given number of {@link AprIoProcessor} for multithreading I/O operations.
85       * 
86       * @param processorCount the number of processor to create and place in a
87       * {@link SimpleIoProcessorPool} 
88       */
89      public AprSocketAcceptor(int processorCount) {
90          super(new DefaultSocketSessionConfig(), AprIoProcessor.class, processorCount);
91          ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
92      }
93  
94      /**
95       *  Constructor for {@link AprSocketAcceptor} with default configuration but a
96        *  specific {@link AprIoProcessor}, useful for sharing the same processor over multiple
97        *  {@link IoService} of the same type.
98        * @param processor the processor to use for managing I/O events
99        */
100     public AprSocketAcceptor(IoProcessor<AprSession> processor) {
101         super(new DefaultSocketSessionConfig(), processor);
102         ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
103     }
104 
105     /**
106      *  Constructor for {@link AprSocketAcceptor} with a given {@link Executor} for handling 
107      *  connection events and a given {@link AprIoProcessor} for handling I/O events, useful for 
108      *  sharing the same processor and executor over multiple {@link IoService} of the same type.
109      * @param executor the executor for connection
110      * @param processor the processor for I/O operations
111      */
112     public AprSocketAcceptor(Executor executor, IoProcessor<AprSession> processor) {
113         super(new DefaultSocketSessionConfig(), executor, processor);
114         ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
115     }
116 
117     /**
118      * {@inheritDoc}
119      */
120     @Override
121     protected AprSession accept(IoProcessor<AprSession> processor, Long handle) throws Exception {
122         long s = Socket.accept(handle);
123         boolean success = false;
124         try {
125             AprSession result = new AprSocketSession(this, processor, s);
126             success = true;
127             return result;
128         } finally {
129             if (!success) {
130                 Socket.close(s);
131             }
132         }
133     }
134 
135     /**
136      * {@inheritDoc}
137      */
138     @Override
139     protected Long open(SocketAddress localAddress) throws Exception {
140         InetSocketAddress la = (InetSocketAddress) localAddress;
141         long handle = Socket.create(Socket.APR_INET, Socket.SOCK_STREAM, Socket.APR_PROTO_TCP, pool);
142 
143         boolean success = false;
144         try {
145             int result = Socket.optSet(handle, Socket.APR_SO_NONBLOCK, 1);
146             if (result != Status.APR_SUCCESS) {
147                 throwException(result);
148             }
149             result = Socket.timeoutSet(handle, 0);
150             if (result != Status.APR_SUCCESS) {
151                 throwException(result);
152             }
153 
154             // Configure the server socket,
155             result = Socket.optSet(handle, Socket.APR_SO_REUSEADDR, isReuseAddress() ? 1 : 0);
156             if (result != Status.APR_SUCCESS) {
157                 throwException(result);
158             }
159             result = Socket.optSet(handle, Socket.APR_SO_RCVBUF, getSessionConfig().getReceiveBufferSize());
160             if (result != Status.APR_SUCCESS) {
161                 throwException(result);
162             }
163 
164             // and bind.
165             long sa;
166             if (la != null) {
167                 if (la.getAddress() == null) {
168                     sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, la.getPort(), 0, pool);
169                 } else {
170                     sa = Address.info(la.getAddress().getHostAddress(), Socket.APR_INET, la.getPort(), 0, pool);
171                 }
172             } else {
173                 sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, 0, 0, pool);
174             }
175 
176             result = Socket.bind(handle, sa);
177             if (result != Status.APR_SUCCESS) {
178                 throwException(result);
179             }
180             result = Socket.listen(handle, getBacklog());
181             if (result != Status.APR_SUCCESS) {
182                 throwException(result);
183             }
184 
185             result = Poll.add(pollset, handle, Poll.APR_POLLIN);
186             if (result != Status.APR_SUCCESS) {
187                 throwException(result);
188             }
189             success = true;
190         } finally {
191             if (!success) {
192                 close(handle);
193             }
194         }
195         return handle;
196     }
197 
198     /**
199      * {@inheritDoc}
200      */
201     @Override
202     protected void init() throws Exception {
203         // initialize a memory pool for APR functions
204         pool = Pool.create(AprLibrary.getInstance().getRootPool());
205 
206         wakeupSocket = Socket.create(Socket.APR_INET, Socket.SOCK_DGRAM, Socket.APR_PROTO_UDP, pool);
207 
208         pollset = Poll.create(POLLSET_SIZE, pool, Poll.APR_POLLSET_THREADSAFE, Long.MAX_VALUE);
209 
210         if (pollset <= 0) {
211             pollset = Poll.create(62, pool, Poll.APR_POLLSET_THREADSAFE, Long.MAX_VALUE);
212         }
213 
214         if (pollset <= 0) {
215             if (Status.APR_STATUS_IS_ENOTIMPL(-(int) pollset)) {
216                 throw new RuntimeIoException("Thread-safe pollset is not supported in this platform.");
217             }
218         }
219     }
220 
221     /**
222      * {@inheritDoc}
223      */
224     @Override
225     protected void destroy() throws Exception {
226         if (wakeupSocket > 0) {
227             Socket.close(wakeupSocket);
228         }
229         if (pollset > 0) {
230             Poll.destroy(pollset);
231         }
232         if (pool > 0) {
233             Pool.destroy(pool);
234         }
235     }
236 
237     /**
238      * {@inheritDoc}
239      */
240     @Override
241     protected SocketAddress localAddress(Long handle) throws Exception {
242         long la = Address.get(Socket.APR_LOCAL, handle);
243         return new InetSocketAddress(Address.getip(la), Address.getInfo(la).port);
244     }
245 
246     /**
247      * {@inheritDoc}
248      */
249     @Override
250     protected int select() throws Exception {
251         int rv = Poll.poll(pollset, Integer.MAX_VALUE, polledSockets, false);
252         if (rv <= 0) {
253             // We have had an error. It can simply be that we have reached
254             // the timeout (very unlikely, as we have set it to MAX_INTEGER)
255             if (rv != APR_TIMEUP_ERROR) {
256                 // It's not a timeout being exceeded. Throw the error
257                 throwException(rv);
258             }
259 
260             rv = Poll.maintain(pollset, polledSockets, true);
261             if (rv > 0) {
262                 for (int i = 0; i < rv; i++) {
263                     Poll.add(pollset, polledSockets[i], Poll.APR_POLLIN);
264                 }
265             } else if (rv < 0) {
266                 throwException(rv);
267             }
268 
269             return 0;
270         } else {
271             rv <<= 1;
272             if (!polledHandles.isEmpty()) {
273                 polledHandles.clear();
274             }
275 
276             for (int i = 0; i < rv; i++) {
277                 long flag = polledSockets[i];
278                 long socket = polledSockets[++i];
279                 if (socket == wakeupSocket) {
280                     synchronized (wakeupLock) {
281                         Poll.remove(pollset, wakeupSocket);
282                         toBeWakenUp = false;
283                     }
284                     continue;
285                 }
286 
287                 if ((flag & Poll.APR_POLLIN) != 0) {
288                     polledHandles.add(socket);
289                 }
290             }
291             return polledHandles.size();
292         }
293     }
294 
295     /**
296      * {@inheritDoc}
297      */
298     @Override
299     protected Iterator<Long> selectedHandles() {
300         return polledHandles.iterator();
301     }
302 
303     /**
304      * {@inheritDoc}
305      */
306     @Override
307     protected void close(Long handle) throws Exception {
308         Poll.remove(pollset, handle);
309         int result = Socket.close(handle);
310         if (result != Status.APR_SUCCESS) {
311             throwException(result);
312         }
313     }
314 
315     /**
316      * {@inheritDoc}
317      */
318     @Override
319     protected void wakeup() {
320         if (toBeWakenUp) {
321             return;
322         }
323 
324         // Add a dummy socket to the pollset.
325         synchronized (wakeupLock) {
326             toBeWakenUp = true;
327             Poll.add(pollset, wakeupSocket, Poll.APR_POLLOUT);
328         }
329     }
330 
331     /**
332      * {@inheritDoc}
333      */
334     @Override
335     public InetSocketAddress getLocalAddress() {
336         return (InetSocketAddress) super.getLocalAddress();
337     }
338 
339     /**
340      * {@inheritDoc}
341      */
342     @Override
343     public InetSocketAddress getDefaultLocalAddress() {
344         return (InetSocketAddress) super.getDefaultLocalAddress();
345     }
346 
347     /**
348      * {@inheritDoc}
349      */
350     public void setDefaultLocalAddress(InetSocketAddress localAddress) {
351         super.setDefaultLocalAddress(localAddress);
352     }
353 
354     /**
355      * {@inheritDoc}
356      */
357     public TransportMetadata getTransportMetadata() {
358         return AprSocketSession.METADATA;
359     }
360 
361     /**
362      * {@inheritDoc}
363      */
364     @Override
365     public SocketSessionConfig getSessionConfig() {
366         return (SocketSessionConfig) super.getSessionConfig();
367     }
368 
369     /**
370      * Convert an APR code into an Exception with the corresponding message
371      * @param code error number
372      * @throws IOException the generated exception
373      */
374     private void throwException(int code) throws IOException {
375         throw new IOException(org.apache.tomcat.jni.Error.strerror(-code) + " (code: " + code + ")");
376     }
377 }