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.nio.ByteBuffer;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.concurrent.Executor;
33  
34  import org.apache.mina.core.RuntimeIoException;
35  import org.apache.mina.core.polling.AbstractPollingIoConnector;
36  import org.apache.mina.core.service.IoConnector;
37  import org.apache.mina.core.service.IoProcessor;
38  import org.apache.mina.core.service.IoService;
39  import org.apache.mina.core.service.SimpleIoProcessorPool;
40  import org.apache.mina.core.service.TransportMetadata;
41  import org.apache.mina.transport.socket.DefaultSocketSessionConfig;
42  import org.apache.mina.transport.socket.SocketConnector;
43  import org.apache.mina.transport.socket.SocketSessionConfig;
44  import org.apache.mina.util.CircularQueue;
45  import org.apache.tomcat.jni.Address;
46  import org.apache.tomcat.jni.Poll;
47  import org.apache.tomcat.jni.Pool;
48  import org.apache.tomcat.jni.Socket;
49  import org.apache.tomcat.jni.Status;
50  
51  /**
52   * {@link IoConnector} for APR based socket transport (TCP/IP).
53   * 
54   * @author The Apache MINA Project (dev@mina.apache.org)
55   * @version $Rev: 757177 $, $Date: 2009-03-22 13:33:54 +0100 (Sun, 22 Mar 2009) $
56   */
57  public final class AprSocketConnector extends AbstractPollingIoConnector<AprSession, Long> implements SocketConnector {
58  
59      /** 
60       * This constant is deduced from the APR code. It is used when the timeout
61       * has expired while doing a poll() operation.
62       */ 
63      private static final int APR_TIMEUP_ERROR = -120001;
64  
65      private static final int POLLSET_SIZE = 1024;
66  
67      private final Map<Long, ConnectionRequest> requests =
68          new HashMap<Long, ConnectionRequest>(POLLSET_SIZE);
69  
70      private final Object wakeupLock = new Object();
71      private volatile long wakeupSocket;
72      private volatile boolean toBeWakenUp;
73  
74      private volatile long pool;
75      private volatile long pollset; // socket poller
76      private final long[] polledSockets = new long[POLLSET_SIZE << 1];
77      private final List<Long> polledHandles = new CircularQueue<Long>(POLLSET_SIZE);
78      private final Set<Long> failedHandles = new HashSet<Long>(POLLSET_SIZE);
79      private volatile ByteBuffer dummyBuffer;
80  
81      /**
82       * Create an {@link AprSocketConnector} with default configuration (multiple thread model).
83       */
84      public AprSocketConnector() {
85          super(new DefaultSocketSessionConfig(), AprIoProcessor.class);
86          ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
87      }
88  
89      /**
90       * Constructor for {@link AprSocketConnector} with default configuration, and 
91       * given number of {@link AprIoProcessor} for multithreading I/O operations
92       * @param processorCount the number of processor to create and place in a
93       * {@link SimpleIoProcessorPool} 
94       */
95      public AprSocketConnector(int processorCount) {
96          super(new DefaultSocketSessionConfig(), AprIoProcessor.class, processorCount);
97          ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
98      }
99  
100     /**
101      *  Constructor for {@link AprSocketConnector} with default configuration but a
102      *  specific {@link IoProcessor}, useful for sharing the same processor over multiple
103      *  {@link IoService} of the same type.
104      * @param processor the processor to use for managing I/O events
105      */
106     public AprSocketConnector(IoProcessor<AprSession> processor) {
107         super(new DefaultSocketSessionConfig(), processor);
108         ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
109     }
110 
111     /**
112      *  Constructor for {@link AprSocketConnector} with a given {@link Executor} for handling 
113      *  connection events and a given {@link IoProcessor} for handling I/O events, useful for sharing 
114      *  the same processor and executor over multiple {@link IoService} of the same type.
115      * @param executor the executor for connection
116      * @param processor the processor for I/O operations
117      */
118     public AprSocketConnector(Executor executor, IoProcessor<AprSession> processor) {
119         super(new DefaultSocketSessionConfig(), executor, processor);
120         ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
121     }
122 
123     /**
124      * {@inheritDoc}
125      */
126     @Override
127     protected void init() throws Exception {
128         // initialize a memory pool for APR functions
129         pool = Pool.create(AprLibrary.getInstance().getRootPool());
130 
131         wakeupSocket = Socket.create(
132                 Socket.APR_INET, Socket.SOCK_DGRAM, Socket.APR_PROTO_UDP, pool);
133 
134         dummyBuffer = Pool.alloc(pool, 1);
135 
136         pollset = Poll.create(
137                         POLLSET_SIZE,
138                         pool,
139                         Poll.APR_POLLSET_THREADSAFE,
140                         Long.MAX_VALUE);
141 
142         if (pollset <= 0) {
143             pollset = Poll.create(
144                     62,
145                     pool,
146                     Poll.APR_POLLSET_THREADSAFE,
147                     Long.MAX_VALUE);
148         }
149 
150         if (pollset <= 0) {
151             if (Status.APR_STATUS_IS_ENOTIMPL(- (int) pollset)) {
152                 throw new RuntimeIoException(
153                         "Thread-safe pollset is not supported in this platform.");
154             }
155         }
156     }
157 
158     /**
159      * {@inheritDoc}
160      */
161     @Override
162     protected void destroy() throws Exception {
163         if (wakeupSocket > 0) {
164             Socket.close(wakeupSocket);
165         }
166         if (pollset > 0) {
167             Poll.destroy(pollset);
168         }
169         if (pool > 0) {
170             Pool.destroy(pool);
171         }
172     }
173 
174     /**
175      * {@inheritDoc}
176      */
177     @Override
178     protected Iterator<Long> allHandles() {
179         return polledHandles.iterator();
180     }
181 
182     /**
183      * {@inheritDoc}
184      */
185     @Override
186     protected boolean connect(Long handle, SocketAddress remoteAddress)
187             throws Exception {
188         InetSocketAddress ra = (InetSocketAddress) remoteAddress;
189         long sa;
190         if (ra != null) {
191             if (ra.getAddress() == null) {
192                 sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, ra.getPort(), 0, pool);
193             } else {
194                 sa = Address.info(ra.getAddress().getHostAddress(), Socket.APR_INET, ra.getPort(), 0, pool);
195             }
196         } else {
197             sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, 0, 0, pool);
198         }
199 
200         int rv = Socket.connect(handle, sa);
201         if (rv == Status.APR_SUCCESS) {
202             return true;
203         }
204 
205         if (Status.APR_STATUS_IS_EINPROGRESS(rv)) {
206             return false;
207         }
208 
209         throwException(rv);
210         throw new InternalError(); // This sentence will never be executed.
211     }
212 
213     /**
214      * {@inheritDoc}
215      */
216     @Override
217     protected ConnectionRequest getConnectionRequest(Long handle) {
218         return requests.get(handle);
219     }
220 
221     /**
222      * {@inheritDoc}
223      */
224     @Override
225     protected void close(Long handle) throws Exception {
226         finishConnect(handle);
227         int rv = Socket.close(handle);
228         if (rv != Status.APR_SUCCESS) {
229             throwException(rv);
230         }
231     }
232     
233     /**
234      * {@inheritDoc}
235      */
236     @Override
237     protected boolean finishConnect(Long handle) throws Exception {
238         Poll.remove(pollset, handle);
239         requests.remove(handle);
240         if (failedHandles.remove(handle)) {
241             int rv = Socket.recvb(handle, dummyBuffer, 0, 1);
242             throwException(rv);
243             throw new InternalError("Shouldn't reach here.");
244         }
245         return true;
246     }
247 
248     /**
249      * {@inheritDoc}
250      */
251     @Override
252     protected Long newHandle(SocketAddress localAddress) throws Exception {
253         long handle = Socket.create(
254                 Socket.APR_INET, Socket.SOCK_STREAM, Socket.APR_PROTO_TCP, pool);
255         boolean success = false;
256         try {
257             int result = Socket.optSet(handle, Socket.APR_SO_NONBLOCK, 1);
258             if (result != Status.APR_SUCCESS) {
259                 throwException(result);
260             }
261             result = Socket.timeoutSet(handle, 0);
262             if (result != Status.APR_SUCCESS) {
263                 throwException(result);
264             }
265 
266             if (localAddress != null) {
267                 InetSocketAddress la = (InetSocketAddress) localAddress;
268                 long sa;
269 
270                 if (la.getAddress() == null) {
271                     sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, la.getPort(), 0, pool);
272                 } else {
273                     sa = Address.info(la.getAddress().getHostAddress(), Socket.APR_INET, la.getPort(), 0, pool);
274                 }
275 
276                 result = Socket.bind(handle, sa);
277                 if (result != Status.APR_SUCCESS) {
278                     throwException(result);
279                 }
280             }
281 
282             success = true;
283             return handle;
284         } finally {
285             if (!success) {
286                 int rv = Socket.close(handle);
287                 if (rv != Status.APR_SUCCESS) {
288                     throwException(rv);
289                 }
290             }
291         }
292     }
293 
294     /**
295      * {@inheritDoc}
296      */
297     @Override
298     protected AprSession newSession(IoProcessor<AprSession> processor,
299             Long handle) throws Exception {
300         return new AprSocketSession(this, processor, handle);
301     }
302 
303     /**
304      * {@inheritDoc}
305      */
306     @Override
307     protected void register(Long handle, ConnectionRequest request)
308             throws Exception {
309         int rv = Poll.add(pollset, handle, Poll.APR_POLLOUT);
310         if (rv != Status.APR_SUCCESS) {
311             throwException(rv);
312         }
313 
314         requests.put(handle, request);
315     }
316 
317     /**
318      * {@inheritDoc}
319      */
320     @Override
321     protected int select(int timeout) throws Exception {
322         int rv = Poll.poll(pollset, timeout * 1000, polledSockets, false);
323         if (rv <= 0) {
324             if (rv != APR_TIMEUP_ERROR) {
325                 throwException(rv);
326             }
327 
328             rv = Poll.maintain(pollset, polledSockets, true);
329             if (rv > 0) {
330                 for (int i = 0; i < rv; i ++) {
331                     Poll.add(pollset, polledSockets[i], Poll.APR_POLLOUT);
332                 }
333             } else if (rv < 0) {
334                 throwException(rv);
335             }
336 
337             return 0;
338         } else {
339             rv <<= 1;
340             if (!polledHandles.isEmpty()) {
341                 polledHandles.clear();
342             }
343 
344             for (int i = 0; i < rv; i ++) {
345                 long flag = polledSockets[i];
346                 long socket = polledSockets[++i];
347                 if (socket == wakeupSocket) {
348                     synchronized (wakeupLock) {
349                         Poll.remove(pollset, wakeupSocket);
350                         toBeWakenUp = false;
351                     }
352                     continue;
353                 }
354                 polledHandles.add(socket);
355                 if ((flag & Poll.APR_POLLOUT) == 0) {
356                     failedHandles.add(socket);
357                 }
358             }
359             return polledHandles.size();
360         }
361     }
362 
363     /**
364      * {@inheritDoc}
365      */
366     @Override
367     protected Iterator<Long> selectedHandles() {
368         return polledHandles.iterator();
369     }
370     
371     /**
372      * {@inheritDoc}
373      */
374     @Override
375     protected void wakeup() {
376         if (toBeWakenUp) {
377             return;
378         }
379 
380         // Add a dummy socket to the pollset.
381         synchronized (wakeupLock) {
382             toBeWakenUp = true;
383             Poll.add(pollset, wakeupSocket, Poll.APR_POLLOUT);
384         }
385     }
386 
387     /**
388      * {@inheritDoc}
389      */
390     public TransportMetadata getTransportMetadata() {
391         return AprSocketSession.METADATA;
392     }
393 
394     /**
395      * {@inheritDoc}
396      */
397     @Override
398     public SocketSessionConfig getSessionConfig() {
399         return (SocketSessionConfig) super.getSessionConfig();
400     }
401 
402     /**
403      * {@inheritDoc}
404      */
405     @Override
406     public InetSocketAddress getDefaultRemoteAddress() {
407         return (InetSocketAddress) super.getDefaultRemoteAddress();
408     }
409 
410     /**
411      * {@inheritDoc}
412      */
413     public void setDefaultRemoteAddress(InetSocketAddress defaultRemoteAddress) {
414         super.setDefaultRemoteAddress(defaultRemoteAddress);
415     }
416 
417     /**
418      * transform an APR error number in a more fancy exception
419      * @param code APR error code
420      * @throws IOException the produced exception for the given APR error number
421      */
422     private void throwException(int code) throws IOException {
423         throw new IOException(
424                 org.apache.tomcat.jni.Error.strerror(-code) +
425                 " (code: " + code + ")");
426     }
427 }