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