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.common.AbstractPollingIoConnector;
35  import org.apache.mina.common.IoProcessor;
36  import org.apache.mina.common.RuntimeIoException;
37  import org.apache.mina.common.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   * @author The Apache MINA Project (dev@mina.apache.org)
50   * @version $Rev: 600804 $, $Date: 2007-12-03 23:43:01 -0700 (Mon, 03 Dec 2007) $
51   */
52  public final class AprSocketConnector extends AbstractPollingIoConnector<AprSession, Long> implements SocketConnector {
53  
54      private static final int POLLSET_SIZE = 1024;
55  
56      private final Map<Long, ConnectionRequest> requests =
57          new HashMap<Long, ConnectionRequest>(POLLSET_SIZE);
58      
59      private final Object wakeupLock = new Object();
60      private long wakeupSocket;
61      private volatile boolean toBeWakenUp;
62  
63      private volatile long pool;
64      private volatile long pollset; // socket poller
65      private final long[] polledSockets = new long[POLLSET_SIZE << 1];
66      private final List<Long> polledHandles = new CircularQueue<Long>(POLLSET_SIZE);
67      private final Set<Long> failedHandles = new HashSet<Long>(POLLSET_SIZE);
68      private volatile ByteBuffer dummyBuffer;
69  
70      public AprSocketConnector() {
71          super(new DefaultSocketSessionConfig(), AprIoProcessor.class);
72      }
73      
74      public AprSocketConnector(int processorCount) {
75          super(new DefaultSocketSessionConfig(), AprIoProcessor.class, processorCount);
76      }
77      
78      public AprSocketConnector(IoProcessor<AprSession> processor) {
79          super(new DefaultSocketSessionConfig(), processor);
80      }
81  
82      public AprSocketConnector(Executor executor, IoProcessor<AprSession> processor) {
83          super(new DefaultSocketSessionConfig(), executor, processor);
84      }
85      
86      @Override
87      protected void init() throws Exception {
88          wakeupSocket = Socket.create(
89                  Socket.APR_INET, Socket.SOCK_DGRAM, Socket.APR_PROTO_UDP, AprLibrary
90                  .getInstance().getRootPool());
91  
92          // initialize a memory pool for APR functions
93          pool = Pool.create(AprLibrary.getInstance().getRootPool());
94          dummyBuffer = Pool.alloc(pool, 1);
95  
96          pollset = Poll.create(
97                          POLLSET_SIZE,
98                          pool,
99                          Poll.APR_POLLSET_THREADSAFE,
100                         Long.MAX_VALUE);
101         
102         if (pollset <= 0) {
103             pollset = Poll.create(
104                     62,
105                     pool,
106                     Poll.APR_POLLSET_THREADSAFE,
107                     Long.MAX_VALUE);
108         }
109 
110         if (pollset <= 0) {
111             if (Status.APR_STATUS_IS_ENOTIMPL(- (int) pollset)) {
112                 throw new RuntimeIoException(
113                         "Thread-safe pollset is not supported in this platform.");
114             }
115         }
116     }
117 
118     @Override
119     protected void destroy() throws Exception {
120         if (wakeupSocket > 0) {
121             Socket.close(wakeupSocket);
122         }
123         if (pollset > 0) {
124             Poll.destroy(pollset);
125         }
126         if (pool > 0) {
127             Pool.destroy(pool);
128         }
129     }
130 
131     @Override
132     protected Iterator<Long> allHandles() {
133         return polledHandles.iterator();
134     }
135 
136     @Override
137     protected boolean connect(Long handle, SocketAddress remoteAddress)
138             throws Exception {
139         InetSocketAddress ra = (InetSocketAddress) remoteAddress;
140         long sa;
141         if (ra != null) {
142             if (ra.getAddress() == null) {
143                 sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, ra.getPort(), 0, pool);
144             } else {
145                 sa = Address.info(ra.getAddress().getHostAddress(), Socket.APR_INET, ra.getPort(), 0, pool);
146             }
147         } else {
148             sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, 0, 0, pool);
149         }
150         
151         int rv = Socket.connect(handle, sa);
152         if (rv == Status.APR_SUCCESS) {
153             return true;
154         }
155         
156         if (Status.APR_STATUS_IS_EINPROGRESS(rv)) {
157             return false;
158         }
159 
160         throwException(rv);
161         throw new InternalError(); // This sentence will never be executed.
162     }
163 
164     @Override
165     protected ConnectionRequest connectionRequest(Long handle) {
166         return requests.get(handle);
167     }
168 
169     @Override
170     protected void close(Long handle) throws Exception {
171         finishConnect(handle);
172         int rv = Socket.close(handle);
173         if (rv != Status.APR_SUCCESS) {
174             throwException(rv);
175         }
176     }
177 
178     @Override
179     protected boolean finishConnect(Long handle) throws Exception {
180         Poll.remove(pollset, handle);
181         requests.remove(handle);
182         if (failedHandles.remove(handle)) {
183             int rv = Socket.recvb(handle, dummyBuffer, 0, 1);
184             throwException(rv);
185             throw new InternalError("Shouldn't reach here.");
186         }
187         return true;
188     }
189 
190     @Override
191     protected Long newHandle(SocketAddress localAddress) throws Exception {
192         long handle = Socket.create(
193                 Socket.APR_INET, Socket.SOCK_STREAM, Socket.APR_PROTO_TCP, pool);
194         boolean success = false;
195         try {
196             Socket.optSet(handle, Socket.APR_SO_REUSEADDR, 1);
197             
198             if (localAddress != null) {
199                 InetSocketAddress la = (InetSocketAddress) localAddress;
200                 long sa;
201                 if (la != null) {
202                     if (la.getAddress() == null) {
203                         sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, la.getPort(), 0, pool);
204                     } else {
205                         sa = Address.info(la.getAddress().getHostAddress(), Socket.APR_INET, la.getPort(), 0, pool);
206                     }
207                 } else {
208                     sa = Address.info(Address.APR_ANYADDR, Socket.APR_INET, 0, 0, pool);
209                 }
210                 
211                 int result = Socket.bind(handle, sa);
212                 if (result != Status.APR_SUCCESS) {
213                     throwException(result);
214                 }            
215             }
216             
217             Socket.optSet(handle, Socket.APR_SO_NONBLOCK, 1);
218             success = true;
219             return handle;
220         } finally {
221             if (!success) {
222                 int rv = Socket.close(handle);
223                 if (rv != Status.APR_SUCCESS) {
224                     throwException(rv);
225                 }
226             }
227         }
228     }
229 
230     @Override
231     protected AprSession newSession(IoProcessor<AprSession> processor,
232             Long handle) throws Exception {
233         return new AprSocketSession(this, processor, handle);
234     }
235 
236     @Override
237     protected void register(Long handle, ConnectionRequest request)
238             throws Exception {
239         int rv = Poll.add(pollset, handle, Poll.APR_POLLOUT);
240         if (rv != Status.APR_SUCCESS) {
241             throwException(rv);
242         }
243         
244         requests.put(handle, request);
245     }
246 
247     @Override
248     protected boolean select(int timeout) throws Exception {
249         int rv = Poll.poll(pollset, timeout * 1000, polledSockets, false);
250         if (rv <= 0) {
251             if (rv != -120001) {
252                 throwException(rv);
253             }
254             
255             rv = Poll.maintain(pollset, polledSockets, true);
256             if (rv > 0) {
257                 for (int i = 0; i < rv; i ++) {
258                     Poll.add(pollset, polledSockets[i], Poll.APR_POLLOUT);
259                 }
260             } else if (rv < 0) {
261                 throwException(rv);
262             }
263             
264             return false;
265         } else {
266             rv <<= 1;
267             if (!polledHandles.isEmpty()) {
268                 polledHandles.clear();
269             }
270 
271             for (int i = 0; i < rv; i ++) {
272                 long flag = polledSockets[i];
273                 long socket = polledSockets[++i];
274                 if (socket == wakeupSocket) {
275                     synchronized (wakeupLock) {
276                         Poll.remove(pollset, wakeupSocket);
277                         toBeWakenUp = false;
278                     }
279                     continue;
280                 }
281                 polledHandles.add(socket);
282                 if ((flag & Poll.APR_POLLOUT) == 0) {
283                     failedHandles.add(socket);
284                 }
285             }
286             return !polledHandles.isEmpty();
287         }
288     }
289 
290     @Override
291     protected Iterator<Long> selectedHandles() {
292         return polledHandles.iterator();
293     }
294 
295     @Override
296     protected void wakeup() {
297         if (toBeWakenUp) {
298             return;
299         }
300         
301         // Add a dummy socket to the pollset.
302         synchronized (wakeupLock) {
303             toBeWakenUp = true;
304             Poll.add(pollset, wakeupSocket, Poll.APR_POLLOUT);
305         }
306     }
307 
308     public TransportMetadata getTransportMetadata() {
309         return AprSocketSession.METADATA;
310     }
311     
312     @Override
313     public SocketSessionConfig getSessionConfig() {
314         return (SocketSessionConfig) super.getSessionConfig();
315     }
316     
317     @Override
318     public InetSocketAddress getDefaultRemoteAddress() {
319         return (InetSocketAddress) super.getDefaultRemoteAddress();
320     }
321     
322     public void setDefaultRemoteAddress(InetSocketAddress defaultRemoteAddress) {
323         super.setDefaultRemoteAddress(defaultRemoteAddress);
324     }
325 
326     private void throwException(int code) throws IOException {
327         throw new IOException(
328                 org.apache.tomcat.jni.Error.strerror(-code) +
329                 " (code: " + code + ")");
330     }
331 }