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