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.core.service;
    21
    22  import java.io.IOException;
    23  import java.net.SocketAddress;
    24  import java.util.ArrayList;
    25  import java.util.Collection;
    26  import java.util.Collections;
    27  import java.util.HashSet;
    28  import java.util.List;
    29  import java.util.Set;
    30  import java.util.concurrent.Executor;
    31  import java.util.concurrent.Executors;
    32
    33  import org.apache.mina.core.RuntimeIoException;
    34  import org.apache.mina.core.session.IoSession;
    35  import org.apache.mina.core.session.IoSessionConfig;
    36
    37  /**
    38   * A base implementation of {@link IoAcceptor}.
    39   *
    40   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    41   * @org.apache.xbean.XBean
    42   */
    43  public abstract class AbstractIoAcceptor extends AbstractIoService implements IoAcceptor {
    44
    45      private final List<SocketAddress> defaultLocalAddresses = new ArrayList<SocketAddress>();
    46
    47      private final List<SocketAddress> unmodifiableDefaultLocalAddresses = Collections
    48              .unmodifiableList(defaultLocalAddresses);
    49
    50      private final Set<SocketAddress> boundAddresses = new HashSet<SocketAddress>();
    51
    52      private boolean disconnectOnUnbind = true;
    53
    54      /**
    55       * The lock object which is acquired while bind or unbind operation is performed.
    56       * Acquire this lock in your property setters which shouldn't be changed while
    57       * the service is bound.
    58       */
    59      protected final Object bindLock = new Object();
    60
    61      /**
    62       * Constructor for {@link AbstractIoAcceptor}. You need to provide a default
    63       * session configuration and an {@link Executor} for handling I/O events. If
    64       * null {@link Executor} is provided, a default one will be created using
    65       * {@link Executors#newCachedThreadPool()}.
    66       *
    67       * {@see AbstractIoService#AbstractIoService(IoSessionConfig, Executor)}
    68       *
    69       * @param sessionConfig
    70       *            the default configuration for the managed {@link IoSession}
    71       * @param executor
    72       *            the {@link Executor} used for handling execution of I/O
    73       *            events. Can be <code>null</code>.
    74       */
    75      protected AbstractIoAcceptor(IoSessionConfig sessionConfig, Executor executor) {
    76          super(sessionConfig, executor);
    77          defaultLocalAddresses.add(null);
    78      }
    79
    80      /**
    81       * {@inheritDoc}
    82       */
    83      public SocketAddress getLocalAddress() {
    84          Set<SocketAddress> localAddresses = getLocalAddresses();
    85          if (localAddresses.isEmpty()) {
    86              return null;
    87          }
    88
    89          return localAddresses.iterator().next();
    90      }
    91
    92      /**
    93       * {@inheritDoc}
    94       */
    95      public final Set<SocketAddress> getLocalAddresses() {
    96          Set<SocketAddress> localAddresses = new HashSet<SocketAddress>();
    97
    98          synchronized (boundAddresses) {
    99              localAddresses.addAll(boundAddresses);
    100         }
    101
    102         return localAddresses;
    103     }
    104
    105     /**
    106      * {@inheritDoc}
    107      */
    108     public SocketAddress getDefaultLocalAddress() {
    109         if (defaultLocalAddresses.isEmpty()) {
    110             return null;
    111         }
    112         return defaultLocalAddresses.iterator().next();
    113     }
    114
    115     /**
    116      * {@inheritDoc}
    117      */
    118     public final void setDefaultLocalAddress(SocketAddress localAddress) {
    119         setDefaultLocalAddresses(localAddress);
    120     }
    121
    122     /**
    123      * {@inheritDoc}
    124      */
    125     public final List<SocketAddress> getDefaultLocalAddresses() {
    126         return unmodifiableDefaultLocalAddresses;
    127     }
    128
    129     /**
    130      * {@inheritDoc}
    131      * @org.apache.xbean.Property nestedType="java.net.SocketAddress"
    132      */
    133     public final void setDefaultLocalAddresses(List<? extends SocketAddress> localAddresses) {
    134         if (localAddresses == null) {
    135             throw new IllegalArgumentException("localAddresses");
    136         }
    137         setDefaultLocalAddresses((Iterable<? extends SocketAddress>) localAddresses);
    138     }
    139
    140     /**
    141      * {@inheritDoc}
    142      */
    143     public final void setDefaultLocalAddresses(Iterable<? extends SocketAddress> localAddresses) {
    144         if (localAddresses == null) {
    145             throw new IllegalArgumentException("localAddresses");
    146         }
    147
    148         synchronized (bindLock) {
    149             synchronized (boundAddresses) {
    150                 if (!boundAddresses.isEmpty()) {
    151                     throw new IllegalStateException("localAddress can't be set while the acceptor is bound.");
    152                 }
    153
    154                 Collection<SocketAddress> newLocalAddresses = new ArrayList<SocketAddress>();
    155
    156                 for (SocketAddress a : localAddresses) {
    157                     checkAddressType(a);
    158                     newLocalAddresses.add(a);
    159                 }
    160
    161                 if (newLocalAddresses.isEmpty()) {
    162                     throw new IllegalArgumentException("empty localAddresses");
    163                 }
    164
    165                 this.defaultLocalAddresses.clear();
    166                 this.defaultLocalAddresses.addAll(newLocalAddresses);
    167             }
    168         }
    169     }
    170
    171     /**
    172      * {@inheritDoc}
    173      * @org.apache.xbean.Property nestedType="java.net.SocketAddress"
    174      */
    175     public final void setDefaultLocalAddresses(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
    176         if (otherLocalAddresses == null) {
    177             otherLocalAddresses = new SocketAddress[0];
    178         }
    179
    180         Collection<SocketAddress> newLocalAddresses = new ArrayList<SocketAddress>(otherLocalAddresses.length + 1);
    181
    182         newLocalAddresses.add(firstLocalAddress);
    183         for (SocketAddress a : otherLocalAddresses) {
    184             newLocalAddresses.add(a);
    185         }
    186
    187         setDefaultLocalAddresses(newLocalAddresses);
    188     }
    189
    190     /**
    191      * {@inheritDoc}
    192      */
    193     public final boolean isCloseOnDeactivation() {
    194         return disconnectOnUnbind;
    195     }
    196
    197     /**
    198      * {@inheritDoc}
    199      */
    200     public final void setCloseOnDeactivation(boolean disconnectClientsOnUnbind) {
    201         this.disconnectOnUnbind = disconnectClientsOnUnbind;
    202     }
    203
    204     /**
    205      * {@inheritDoc}
    206      */
    207     public final void bind() throws IOException {
    208         bind(getDefaultLocalAddresses());
    209     }
    210
    211     /**
    212      * {@inheritDoc}
    213      */
    214     public final void bind(SocketAddress localAddress) throws IOException {
    215         if (localAddress == null) {
    216             throw new IllegalArgumentException("localAddress");
    217         }
    218
    219         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
    220         localAddresses.add(localAddress);
    221         bind(localAddresses);
    222     }
    223
    224     /**
    225      * {@inheritDoc}
    226      */
    227     public final void bind(SocketAddress... addresses) throws IOException {
    228         if ((addresses == null) || (addresses.length == 0)) {
    229             bind(getDefaultLocalAddresses());
    230             return;
    231         }
    232
    233         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(2);
    234
    235         for (SocketAddress address : addresses) {
    236             localAddresses.add(address);
    237         }
    238
    239         bind(localAddresses);
    240     }
    241
    242     /**
    243      * {@inheritDoc}
    244      */
    245     public final void bind(SocketAddress firstLocalAddress, SocketAddress... addresses) throws IOException {
    246         if (firstLocalAddress == null) {
    247             bind(getDefaultLocalAddresses());
    248         }
    249
    250         if ((addresses == null) || (addresses.length == 0)) {
    251             bind(getDefaultLocalAddresses());
    252             return;
    253         }
    254
    255         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(2);
    256         localAddresses.add(firstLocalAddress);
    257
    258         for (SocketAddress address : addresses) {
    259             localAddresses.add(address);
    260         }
    261
    262         bind(localAddresses);
    263     }
    264
    265     /**
    266      * {@inheritDoc}
    267      */
    268     public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {
    269         if (isDisposing()) {
    270             throw new IllegalStateException("Already disposed.");
    271         }
    272
    273         if (localAddresses == null) {
    274             throw new IllegalArgumentException("localAddresses");
    275         }
    276
    277         List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
    278
    279         for (SocketAddress a : localAddresses) {
    280             checkAddressType(a);
    281             localAddressesCopy.add(a);
    282         }
    283
    284         if (localAddressesCopy.isEmpty()) {
    285             throw new IllegalArgumentException("localAddresses is empty.");
    286         }
    287
    288         boolean activate = false;
    289         synchronized (bindLock) {
    290             synchronized (boundAddresses) {
    291                 if (boundAddresses.isEmpty()) {
    292                     activate = true;
    293                 }
    294             }
    295
    296             if (getHandler() == null) {
    297                 throw new IllegalStateException("handler is not set.");
    298             }
    299
    300             try {
    301                 Set<SocketAddress> addresses = bindInternal(localAddressesCopy);
    302
    303                 synchronized (boundAddresses) {
    304                     boundAddresses.addAll(addresses);
    305                 }
    306             } catch (IOException e) {
    307                 throw e;
    308             } catch (RuntimeException e) {
    309                 throw e;
    310             } catch (Throwable e) {
    311                 throw new RuntimeIoException("Failed to bind to: " + getLocalAddresses(), e);
    312             }
    313         }
    314
    315         if (activate) {
    316             getListeners().fireServiceActivated();
    317         }
    318     }
    319
    320     /**
    321      * {@inheritDoc}
    322      */
    323     public final void unbind() {
    324         unbind(getLocalAddresses());
    325     }
    326
    327     /**
    328      * {@inheritDoc}
    329      */
    330     public final void unbind(SocketAddress localAddress) {
    331         if (localAddress == null) {
    332             throw new IllegalArgumentException("localAddress");
    333         }
    334
    335         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
    336         localAddresses.add(localAddress);
    337         unbind(localAddresses);
    338     }
    339
    340     /**
    341      * {@inheritDoc}
    342      */
    343     public final void unbind(SocketAddress firstLocalAddress, SocketAddress... otherLocalAddresses) {
    344         if (firstLocalAddress == null) {
    345             throw new IllegalArgumentException("firstLocalAddress");
    346         }
    347         if (otherLocalAddresses == null) {
    348             throw new IllegalArgumentException("otherLocalAddresses");
    349         }
    350
    351         List<SocketAddress> localAddresses = new ArrayList<SocketAddress>();
    352         localAddresses.add(firstLocalAddress);
    353         Collections.addAll(localAddresses, otherLocalAddresses);
    354         unbind(localAddresses);
    355     }
    356
    357     /**
    358      * {@inheritDoc}
    359      */
    360     public final void unbind(Iterable<? extends SocketAddress> localAddresses) {
    361         if (localAddresses == null) {
    362             throw new IllegalArgumentException("localAddresses");
    363         }
    364
    365         boolean deactivate = false;
    366         synchronized (bindLock) {
    367             synchronized (boundAddresses) {
    368                 if (boundAddresses.isEmpty()) {
    369                     return;
    370                 }
    371
    372                 List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
    373                 int specifiedAddressCount = 0;
    374
    375                 for (SocketAddress a : localAddresses) {
    376                     specifiedAddressCount++;
    377
    378                     if ((a != null) && boundAddresses.contains(a)) {
    379                         localAddressesCopy.add(a);
    380                     }
    381                 }
    382
    383                 if (specifiedAddressCount == 0) {
    384                     throw new IllegalArgumentException("localAddresses is empty.");
    385                 }
    386
    387                 if (!localAddressesCopy.isEmpty()) {
    388                     try {
    389                         unbind0(localAddressesCopy);
    390                     } catch (RuntimeException e) {
    391                         throw e;
    392                     } catch (Throwable e) {
    393                         throw new RuntimeIoException("Failed to unbind from: " + getLocalAddresses(), e);
    394                     }
    395
    396                     boundAddresses.removeAll(localAddressesCopy);
    397
    398                     if (boundAddresses.isEmpty()) {
    399                         deactivate = true;
    400                     }
    401                 }
    402             }
    403         }
    404
    405         if (deactivate) {
    406             getListeners().fireServiceDeactivated();
    407         }
    408     }
    409
    410     /**
    411      * Starts the acceptor, and register the given addresses
    412      * @return the {@link Set} of the local addresses which is bound actually
    413      */
    414     protected abstract Set<SocketAddress> bindInternal(List<? extends SocketAddress> localAddresses) throws Exception;
    415
    416     /**
    417      * Implement this method to perform the actual unbind operation.
    418      */
    419     protected abstract void unbind0(List<? extends SocketAddress> localAddresses) throws Exception;
    420
    421     @Override
    422     public String toString() {
    423         TransportMetadata m = getTransportMetadata();
    424         return '('
    425                 + m.getProviderName()
    426                 + ' '
    427                 + m.getName()
    428                 + " acceptor: "
    429                 + (isActive() ? "localAddress(es): " + getLocalAddresses() + ", managedSessionCount: "
    430                         + getManagedSessionCount() : "not bound") + ')';
    431     }
    432
    433     private void checkAddressType(SocketAddress a) {
    434         if (a != null && !getTransportMetadata().getAddressType().isAssignableFrom(a.getClass())) {
    435             throw new IllegalArgumentException("localAddress type: " + a.getClass().getSimpleName() + " (expected: "
    436                     + getTransportMetadata().getAddressType().getSimpleName() + ")");
    437         }
    438     }
    439
    440     public static class AcceptorOperationFuture extends ServiceOperationFuture {
    441         private final List<SocketAddress> localAddresses;
    442
    443         public AcceptorOperationFuture(List<? extends SocketAddress> localAddresses) {
    444             this.localAddresses = new ArrayList<SocketAddress>(localAddresses);
    445         }
    446
    447         public final List<SocketAddress> getLocalAddresses() {
    448             return Collections.unmodifiableList(localAddresses);
    449         }
    450
    451         /**
    452          * @see Object#toString()
    453          */
    454         public String toString() {
    455             StringBuilder sb = new StringBuilder();
    456
    457             sb.append("Acceptor operation : ");
    458
    459             if (localAddresses != null) {
    460                 boolean isFirst = true;
    461
    462                 for (SocketAddress address : localAddresses) {
    463                     if (isFirst) {
    464                         isFirst = false;
    465                     } else {
    466                         sb.append(", ");
    467                     }
    468
    469                     sb.append(address);
    470                 }
    471             }
    472             return sb.toString();
    473         }
    474     }
    475 }