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.List;
27  import java.util.concurrent.Executor;
28  
29  import org.apache.mina.core.RuntimeIoException;
30  import org.apache.mina.core.polling.AbstractPollingIoAcceptor;
31  import org.apache.mina.core.service.IoAcceptor;
32  import org.apache.mina.core.service.IoProcessor;
33  import org.apache.mina.core.service.IoService;
34  import org.apache.mina.core.service.SimpleIoProcessorPool;
35  import org.apache.mina.core.service.TransportMetadata;
36  import org.apache.mina.transport.socket.DefaultSocketSessionConfig;
37  import org.apache.mina.transport.socket.SocketAcceptor;
38  import org.apache.mina.transport.socket.SocketSessionConfig;
39  import org.apache.mina.util.CircularQueue;
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 The Apache MINA Project (dev@mina.apache.org)
50   * @version $Rev: 757178 $, $Date: 2009-03-22 13:34:23 +0100 (Sun, 22 Mar 2009) $
51   */
52  public final class AprSocketAcceptor extends AbstractPollingIoAcceptor<AprSession, Long> implements SocketAcceptor {
53      /** 
54       * This constant is deduced from the APR code. It is used when the timeout
55       * has expired while doing a poll() operation.
56       */ 
57      private static final int APR_TIMEUP_ERROR = -120001;
58  
59      private static final int POLLSET_SIZE = 1024;
60  
61      private final Object wakeupLock = new Object();
62      private volatile long wakeupSocket;
63      private volatile boolean toBeWakenUp;
64  
65      private int backlog = 50;
66      private boolean reuseAddress = false;
67  
68      private volatile long pool;
69      private volatile long pollset; // socket poller
70      private final long[] polledSockets = new long[POLLSET_SIZE << 1];
71      private final List<Long> polledHandles =
72          new CircularQueue<Long>(POLLSET_SIZE);
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,
113             IoProcessor<AprSession> processor) {
114         super(new DefaultSocketSessionConfig(), executor, processor);
115         ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
116     }
117 
118     /**
119      * {@inheritDoc}
120      */
121     @Override
122     protected AprSession accept(IoProcessor<AprSession> processor, Long handle) throws Exception {
123         long s = Socket.accept(handle);
124         boolean success = false;
125         try {
126             AprSession result = new AprSocketSession(this, processor, s);
127             success = true;
128             return result;
129         } finally {
130             if (!success) {
131                 Socket.close(s);
132             }
133         }
134     }
135 
136     /**
137      * {@inheritDoc}
138      */
139     @Override
140     protected Long open(SocketAddress localAddress) throws Exception {
141         InetSocketAddress la = (InetSocketAddress) localAddress;
142         long handle = Socket.create(
143                 Socket.APR_INET, Socket.SOCK_STREAM, Socket.APR_PROTO_TCP, pool);
144 
145         boolean success = false;
146         try {
147             int result = Socket.optSet(handle, Socket.APR_SO_NONBLOCK, 1);
148             if (result != Status.APR_SUCCESS) {
149                 throwException(result);
150             }
151             result = Socket.timeoutSet(handle, 0);
152             if (result != Status.APR_SUCCESS) {
153                 throwException(result);
154             }
155 
156             // Configure the server socket,
157             result = Socket.optSet(handle, Socket.APR_SO_REUSEADDR, isReuseAddress()? 1 : 0);
158             if (result != Status.APR_SUCCESS) {
159                 throwException(result);
160             }
161             result = Socket.optSet(handle, Socket.APR_SO_RCVBUF, getSessionConfig().getReceiveBufferSize());
162             if (result != Status.APR_SUCCESS) {
163                 throwException(result);
164             }
165 
166             // and bind.
167             long sa;
168             if (la != null) {
169                 if (la.getAddress() == null) {
170                     sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, la.getPort(), 0, pool);
171                 } else {
172                     sa = Address.info(la.getAddress().getHostAddress(), Socket.APR_INET, la.getPort(), 0, pool);
173                 }
174             } else {
175                 sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, 0, 0, pool);
176             }
177 
178             result = Socket.bind(handle, sa);
179             if (result != Status.APR_SUCCESS) {
180                 throwException(result);
181             }
182             result = Socket.listen(handle, getBacklog());
183             if (result != Status.APR_SUCCESS) {
184                 throwException(result);
185             }
186 
187             result = Poll.add(pollset, handle, Poll.APR_POLLIN);
188             if (result != Status.APR_SUCCESS) {
189                 throwException(result);
190             }
191             success = true;
192         } finally {
193             if (!success) {
194                 close(handle);
195             }
196         }
197         return handle;
198     }
199 
200     /**
201      * {@inheritDoc}
202      */
203     @Override
204     protected void init() throws Exception {
205         // initialize a memory pool for APR functions
206         pool = Pool.create(AprLibrary.getInstance().getRootPool());
207 
208         wakeupSocket = Socket.create(
209                 Socket.APR_INET, Socket.SOCK_DGRAM, Socket.APR_PROTO_UDP, pool);
210 
211         pollset = Poll.create(
212                         POLLSET_SIZE,
213                         pool,
214                         Poll.APR_POLLSET_THREADSAFE,
215                         Long.MAX_VALUE);
216 
217         if (pollset <= 0) {
218             pollset = Poll.create(
219                     62,
220                     pool,
221                     Poll.APR_POLLSET_THREADSAFE,
222                     Long.MAX_VALUE);
223         }
224 
225         if (pollset <= 0) {
226             if (Status.APR_STATUS_IS_ENOTIMPL(- (int) pollset)) {
227                 throw new RuntimeIoException(
228                         "Thread-safe pollset is not supported in this platform.");
229             }
230         }
231     }
232 
233     /**
234      * {@inheritDoc}
235      */
236     @Override
237     protected void destroy() throws Exception {
238         if (wakeupSocket > 0) {
239             Socket.close(wakeupSocket);
240         }
241         if (pollset > 0) {
242             Poll.destroy(pollset);
243         }
244         if (pool > 0) {
245             Pool.destroy(pool);
246         }
247     }
248 
249     /**
250      * {@inheritDoc}
251      */
252     @Override
253     protected SocketAddress localAddress(Long handle) throws Exception {
254         long la = Address.get(Socket.APR_LOCAL, handle);
255         return new InetSocketAddress(Address.getip(la), Address.getInfo(la).port);
256     }
257 
258     /**
259      * {@inheritDoc}
260      */
261     @Override
262     protected int select() throws Exception {
263         int rv = Poll.poll(pollset, Integer.MAX_VALUE, polledSockets, false);
264         if (rv <= 0) {
265             // We have had an error. It can simply be that we have reached
266             // the timeout (very unlikely, as we have set it to MAX_INTEGER)
267             if (rv != APR_TIMEUP_ERROR) {
268                 // It's not a timeout being exceeded. Throw the error
269                 throwException(rv);
270             }
271 
272             rv = Poll.maintain(pollset, polledSockets, true);
273             if (rv > 0) {
274                 for (int i = 0; i < rv; i ++) {
275                     Poll.add(pollset, polledSockets[i], Poll.APR_POLLIN);
276                 }
277             } else if (rv < 0) {
278                 throwException(rv);
279             }
280 
281             return 0;
282         } else {
283             rv <<= 1;
284             if (!polledHandles.isEmpty()) {
285                 polledHandles.clear();
286             }
287 
288             for (int i = 0; i < rv; i ++) {
289                 long flag = polledSockets[i];
290                 long socket = polledSockets[++i];
291                 if (socket == wakeupSocket) {
292                     synchronized (wakeupLock) {
293                         Poll.remove(pollset, wakeupSocket);
294                         toBeWakenUp = false;
295                     }
296                     continue;
297                 }
298 
299                 if ((flag & Poll.APR_POLLIN) != 0) {
300                     polledHandles.add(socket);
301                 }
302             }
303             return polledHandles.size();
304         }
305     }
306 
307     /**
308      * {@inheritDoc}
309      */
310     @Override
311     protected Iterator<Long> selectedHandles() {
312         return polledHandles.iterator();
313     }
314 
315     /**
316      * {@inheritDoc}
317      */
318     @Override
319     protected void close(Long handle) throws Exception {
320         Poll.remove(pollset, handle);
321         int result = Socket.close(handle);
322         if (result != Status.APR_SUCCESS) {
323             throwException(result);
324         }
325     }
326 
327     /**
328      * {@inheritDoc}
329      */
330     @Override
331     protected void wakeup() {
332         if (toBeWakenUp) {
333             return;
334         }
335 
336         // Add a dummy socket to the pollset.
337         synchronized (wakeupLock) {
338             toBeWakenUp = true;
339             Poll.add(pollset, wakeupSocket, Poll.APR_POLLOUT);
340         }
341     }
342 
343     /**
344      * {@inheritDoc}
345      */
346     public int getBacklog() {
347         return backlog;
348     }
349 
350     /**
351      * {@inheritDoc}
352      */
353     public boolean isReuseAddress() {
354         return reuseAddress;
355     }
356 
357     /**
358      * {@inheritDoc}
359      */
360     public void setBacklog(int backlog) {
361         synchronized (bindLock) {
362             if (isActive()) {
363                 throw new IllegalStateException(
364                         "backlog can't be set while the acceptor is bound.");
365             }
366 
367             this.backlog = backlog;
368         }
369     }
370 
371     /**
372      * {@inheritDoc}
373      */
374     @Override
375     public InetSocketAddress getLocalAddress() {
376         return (InetSocketAddress) super.getLocalAddress();
377     }
378 
379     /**
380      * {@inheritDoc}
381      */
382     @Override
383     public InetSocketAddress getDefaultLocalAddress() {
384         return (InetSocketAddress) super.getDefaultLocalAddress();
385     }
386 
387     /**
388      * {@inheritDoc}
389      */
390     public void setDefaultLocalAddress(InetSocketAddress localAddress) {
391         super.setDefaultLocalAddress(localAddress);
392     }
393 
394     public void setReuseAddress(boolean reuseAddress) {
395         synchronized (bindLock) {
396             if (isActive()) {
397                 throw new IllegalStateException(
398                         "backlog can't be set while the acceptor is bound.");
399             }
400 
401             this.reuseAddress = reuseAddress;
402         }
403     }
404 
405     /**
406      * {@inheritDoc}
407      */
408     public TransportMetadata getTransportMetadata() {
409         return AprSocketSession.METADATA;
410     }
411 
412     /**
413      * {@inheritDoc}
414      */
415     @Override
416     public SocketSessionConfig getSessionConfig() {
417         return (SocketSessionConfig) super.getSessionConfig();
418     }
419 
420     /**
421      * Convert an APR code into an Exception with the corresponding message
422      * @param code error number
423      * @throws IOException the generated exception
424      */
425     private void throwException(int code) throws IOException {
426         throw new IOException(
427                 org.apache.tomcat.jni.Error.strerror(-code) +
428                 " (code: " + code + ")");
429     }
430 }