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.IoProcessor;
32  import org.apache.mina.core.service.TransportMetadata;
33  import org.apache.mina.transport.socket.DefaultSocketSessionConfig;
34  import org.apache.mina.transport.socket.SocketAcceptor;
35  import org.apache.mina.transport.socket.SocketSessionConfig;
36  import org.apache.mina.util.CircularQueue;
37  import org.apache.tomcat.jni.Address;
38  import org.apache.tomcat.jni.Poll;
39  import org.apache.tomcat.jni.Pool;
40  import org.apache.tomcat.jni.Socket;
41  import org.apache.tomcat.jni.Status;
42  
43  /**
44   * TODO Add documentation
45   *
46   * @author The Apache MINA Project (dev@mina.apache.org)
47   * @version $Rev: 706057 $, $Date: 2008-10-19 21:40:20 +0200 (Sun, 19 Oct 2008) $
48   */
49  public final class AprSocketAcceptor extends AbstractPollingIoAcceptor<AprSession, Long> implements SocketAcceptor {
50      /** 
51       * This constant is deduced from the APR code. It is used when the timeout
52       * has expired while doing a poll() operation.
53       */ 
54      private static final int APR_TIMEUP_ERROR = -120001;
55  
56      private static final int POLLSET_SIZE = 1024;
57  
58      private final Object wakeupLock = new Object();
59      private volatile long wakeupSocket;
60      private volatile boolean toBeWakenUp;
61  
62      private int backlog = 50;
63      private boolean reuseAddress = false;
64  
65      private volatile long pool;
66      private volatile long pollset; // socket poller
67      private final long[] polledSockets = new long[POLLSET_SIZE << 1];
68      private final List<Long> polledHandles =
69          new CircularQueue<Long>(POLLSET_SIZE);
70  
71      public AprSocketAcceptor() {
72          super(new DefaultSocketSessionConfig(), AprIoProcessor.class);
73          ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
74      }
75  
76      public AprSocketAcceptor(int processorCount) {
77          super(new DefaultSocketSessionConfig(), AprIoProcessor.class, processorCount);
78          ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
79      }
80  
81      public AprSocketAcceptor(IoProcessor<AprSession> processor) {
82          super(new DefaultSocketSessionConfig(), processor);
83          ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
84      }
85  
86      public AprSocketAcceptor(Executor executor,
87              IoProcessor<AprSession> processor) {
88          super(new DefaultSocketSessionConfig(), executor, processor);
89          ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
90      }
91  
92      @Override
93      protected AprSession accept(IoProcessor<AprSession> processor, Long handle) throws Exception {
94          long s = Socket.accept(handle);
95          boolean success = false;
96          try {
97              AprSession result = new AprSocketSession(this, processor, s);
98              success = true;
99              return result;
100         } finally {
101             if (!success) {
102                 Socket.close(s);
103             }
104         }
105     }
106 
107     @Override
108     protected Long open(SocketAddress localAddress) throws Exception {
109         InetSocketAddress la = (InetSocketAddress) localAddress;
110         long handle = Socket.create(
111                 Socket.APR_INET, Socket.SOCK_STREAM, Socket.APR_PROTO_TCP, pool);
112 
113         boolean success = false;
114         try {
115             int result = Socket.optSet(handle, Socket.APR_SO_NONBLOCK, 1);
116             if (result != Status.APR_SUCCESS) {
117                 throwException(result);
118             }
119             result = Socket.timeoutSet(handle, 0);
120             if (result != Status.APR_SUCCESS) {
121                 throwException(result);
122             }
123 
124             // Configure the server socket,
125             result = Socket.optSet(handle, Socket.APR_SO_REUSEADDR, isReuseAddress()? 1 : 0);
126             if (result != Status.APR_SUCCESS) {
127                 throwException(result);
128             }
129             result = Socket.optSet(handle, Socket.APR_SO_RCVBUF, getSessionConfig().getReceiveBufferSize());
130             if (result != Status.APR_SUCCESS) {
131                 throwException(result);
132             }
133 
134             // and bind.
135             long sa;
136             if (la != null) {
137                 if (la.getAddress() == null) {
138                     sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, la.getPort(), 0, pool);
139                 } else {
140                     sa = Address.info(la.getAddress().getHostAddress(), Socket.APR_INET, la.getPort(), 0, pool);
141                 }
142             } else {
143                 sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, 0, 0, pool);
144             }
145 
146             result = Socket.bind(handle, sa);
147             if (result != Status.APR_SUCCESS) {
148                 throwException(result);
149             }
150             result = Socket.listen(handle, getBacklog());
151             if (result != Status.APR_SUCCESS) {
152                 throwException(result);
153             }
154 
155             result = Poll.add(pollset, handle, Poll.APR_POLLIN);
156             if (result != Status.APR_SUCCESS) {
157                 throwException(result);
158             }
159             success = true;
160         } finally {
161             if (!success) {
162                 close(handle);
163             }
164         }
165         return handle;
166     }
167 
168     @Override
169     protected void init() throws Exception {
170         // initialize a memory pool for APR functions
171         pool = Pool.create(AprLibrary.getInstance().getRootPool());
172 
173         wakeupSocket = Socket.create(
174                 Socket.APR_INET, Socket.SOCK_DGRAM, Socket.APR_PROTO_UDP, pool);
175 
176         pollset = Poll.create(
177                         POLLSET_SIZE,
178                         pool,
179                         Poll.APR_POLLSET_THREADSAFE,
180                         Long.MAX_VALUE);
181 
182         if (pollset <= 0) {
183             pollset = Poll.create(
184                     62,
185                     pool,
186                     Poll.APR_POLLSET_THREADSAFE,
187                     Long.MAX_VALUE);
188         }
189 
190         if (pollset <= 0) {
191             if (Status.APR_STATUS_IS_ENOTIMPL(- (int) pollset)) {
192                 throw new RuntimeIoException(
193                         "Thread-safe pollset is not supported in this platform.");
194             }
195         }
196     }
197 
198     @Override
199     protected void destroy() throws Exception {
200         if (wakeupSocket > 0) {
201             Socket.close(wakeupSocket);
202         }
203         if (pollset > 0) {
204             Poll.destroy(pollset);
205         }
206         if (pool > 0) {
207             Pool.destroy(pool);
208         }
209     }
210 
211     @Override
212     protected SocketAddress localAddress(Long handle) throws Exception {
213         long la = Address.get(Socket.APR_LOCAL, handle);
214         return new InetSocketAddress(Address.getip(la), Address.getInfo(la).port);
215     }
216 
217     @Override
218     protected int select() throws Exception {
219         int rv = Poll.poll(pollset, Integer.MAX_VALUE, polledSockets, false);
220         if (rv <= 0) {
221             // We have had an error. It can simply be that we have reached
222             // the timeout (very unlikely, as we have set it to MAX_INTEGER)
223             if (rv != APR_TIMEUP_ERROR) {
224                 // It's not a timeout being exceeded. Throw the error
225                 throwException(rv);
226             }
227 
228             rv = Poll.maintain(pollset, polledSockets, true);
229             if (rv > 0) {
230                 for (int i = 0; i < rv; i ++) {
231                     Poll.add(pollset, polledSockets[i], Poll.APR_POLLIN);
232                 }
233             } else if (rv < 0) {
234                 throwException(rv);
235             }
236 
237             return 0;
238         } else {
239             rv <<= 1;
240             if (!polledHandles.isEmpty()) {
241                 polledHandles.clear();
242             }
243 
244             for (int i = 0; i < rv; i ++) {
245                 long flag = polledSockets[i];
246                 long socket = polledSockets[++i];
247                 if (socket == wakeupSocket) {
248                     synchronized (wakeupLock) {
249                         Poll.remove(pollset, wakeupSocket);
250                         toBeWakenUp = false;
251                     }
252                     continue;
253                 }
254 
255                 if ((flag & Poll.APR_POLLIN) != 0) {
256                     polledHandles.add(socket);
257                 }
258             }
259             return polledHandles.size();
260         }
261     }
262 
263     @Override
264     protected Iterator<Long> selectedHandles() {
265         return polledHandles.iterator();
266     }
267 
268     @Override
269     protected void close(Long handle) throws Exception {
270         Poll.remove(pollset, handle);
271         int result = Socket.close(handle);
272         if (result != Status.APR_SUCCESS) {
273             throwException(result);
274         }
275     }
276 
277     @Override
278     protected void wakeup() {
279         if (toBeWakenUp) {
280             return;
281         }
282 
283         // Add a dummy socket to the pollset.
284         synchronized (wakeupLock) {
285             toBeWakenUp = true;
286             Poll.add(pollset, wakeupSocket, Poll.APR_POLLOUT);
287         }
288     }
289 
290     public int getBacklog() {
291         return backlog;
292     }
293 
294     public boolean isReuseAddress() {
295         return reuseAddress;
296     }
297 
298     public void setBacklog(int backlog) {
299         synchronized (bindLock) {
300             if (isActive()) {
301                 throw new IllegalStateException(
302                         "backlog can't be set while the acceptor is bound.");
303             }
304 
305             this.backlog = backlog;
306         }
307     }
308 
309     @Override
310     public InetSocketAddress getLocalAddress() {
311         return (InetSocketAddress) super.getLocalAddress();
312     }
313 
314     @Override
315     public InetSocketAddress getDefaultLocalAddress() {
316         return (InetSocketAddress) super.getDefaultLocalAddress();
317     }
318 
319     public void setDefaultLocalAddress(InetSocketAddress localAddress) {
320         super.setDefaultLocalAddress(localAddress);
321     }
322 
323     public void setReuseAddress(boolean reuseAddress) {
324         synchronized (bindLock) {
325             if (isActive()) {
326                 throw new IllegalStateException(
327                         "backlog can't be set while the acceptor is bound.");
328             }
329 
330             this.reuseAddress = reuseAddress;
331         }
332     }
333 
334     public TransportMetadata getTransportMetadata() {
335         return AprSocketSession.METADATA;
336     }
337 
338     @Override
339     public SocketSessionConfig getSessionConfig() {
340         return (SocketSessionConfig) super.getSessionConfig();
341     }
342 
343     private void throwException(int code) throws IOException {
344         throw new IOException(
345                 org.apache.tomcat.jni.Error.strerror(-code) +
346                 " (code: " + code + ")");
347     }
348 }