View Javadoc

    1   /*
    2    * Copyright 2012 The Netty Project
    3    *
    4    * The Netty Project licenses this file to you under the Apache License,
    5    * version 2.0 (the "License"); you may not use this file except in compliance
    6    * with the License. You may obtain a copy of the License at:
    7    *
    8    *   http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    13   * License for the specific language governing permissions and limitations
    14   * under the License.
    15   */
    16  package org.jboss.netty.bootstrap;
    17
    18  import static org.jboss.netty.channel.Channels.*;
    19
    20  import java.net.InetSocketAddress;
    21  import java.net.SocketAddress;
    22  import java.util.HashMap;
    23  import java.util.Map;
    24  import java.util.Map.Entry;
    25  import java.util.concurrent.BlockingQueue;
    26  import java.util.concurrent.LinkedBlockingQueue;
    27  import java.util.concurrent.TimeUnit;
    28
    29  import org.jboss.netty.channel.Channel;
    30  import org.jboss.netty.channel.ChannelConfig;
    31  import org.jboss.netty.channel.ChannelException;
    32  import org.jboss.netty.channel.ChannelFactory;
    33  import org.jboss.netty.channel.ChannelFuture;
    34  import org.jboss.netty.channel.ChannelHandler;
    35  import org.jboss.netty.channel.ChannelHandlerContext;
    36  import org.jboss.netty.channel.ChannelPipeline;
    37  import org.jboss.netty.channel.ChannelPipelineFactory;
    38  import org.jboss.netty.channel.ChannelStateEvent;
    39  import org.jboss.netty.channel.Channels;
    40  import org.jboss.netty.channel.ChildChannelStateEvent;
    41  import org.jboss.netty.channel.ExceptionEvent;
    42  import org.jboss.netty.channel.ServerChannelFactory;
    43  import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
    44
    45  /**
    46   * A helper class which creates a new server-side {@link Channel} and accepts
    47   * incoming connections.
    48   *
    49   * <h3>Only for connection oriented transports</h3>
    50   *
    51   * This bootstrap is for connection oriented transports only such as TCP/IP
    52   * and local transport.  Use {@link ConnectionlessBootstrap} instead for
    53   * connectionless transports.  Do not use this helper if you are using a
    54   * connectionless transport such as UDP/IP which does not accept an incoming
    55   * connection but receives messages by itself without creating a child channel.
    56   *
    57   * <h3>Parent channel and its children</h3>
    58   *
    59   * A parent channel is a channel which is supposed to accept incoming
    60   * connections.  It is created by this bootstrap's {@link ChannelFactory} via
    61   * {@link #bind()} and {@link #bind(SocketAddress)}.
    62   * <p>
    63   * Once successfully bound, the parent channel starts to accept incoming
    64   * connections, and the accepted connections become the children of the
    65   * parent channel.
    66   *
    67   * <h3>Configuring channels</h3>
    68   *
    69   * {@link #setOption(String, Object) Options} are used to configure both a
    70   * parent channel and its child channels.  To configure the child channels,
    71   * prepend {@code "child."} prefix to the actual option names of a child
    72   * channel:
    73   *
    74   * <pre>
    75   * {@link ServerBootstrap} b = ...;
    76   *
    77   * // Options for a parent channel
    78   * b.setOption("localAddress", new {@link InetSocketAddress}(8080));
    79   * b.setOption("reuseAddress", true);
    80   *
    81   * // Options for its children
    82   * b.setOption("child.tcpNoDelay", true);
    83   * b.setOption("child.receiveBufferSize", 1048576);
    84   * </pre>
    85   *
    86   * For the detailed list of available options, please refer to
    87   * {@link ChannelConfig} and its sub-types.
    88   *
    89   * <h3>Configuring a parent channel pipeline</h3>
    90   *
    91   * It is rare to customize the pipeline of a parent channel because what it is
    92   * supposed to do is very typical.  However, you might want to add a handler
    93   * to deal with some special needs such as degrading the process
    94   * <a href="http://en.wikipedia.org/wiki/User_identifier_(Unix)">UID</a> from
    95   * a <a href="http://en.wikipedia.org/wiki/Superuser">superuser</a> to a
    96   * normal user and changing the current VM security manager for better
    97   * security.  To support such a case,
    98   * the {@link #setParentHandler(ChannelHandler) parentHandler} property is
    99   * provided.
    100  *
    101  * <h3>Configuring a child channel pipeline</h3>
    102  *
    103  * Every channel has its own {@link ChannelPipeline} and you can configure it
    104  * in two ways.
    105  *
    106  * The recommended approach is to specify a {@link ChannelPipelineFactory} by
    107  * calling {@link #setPipelineFactory(ChannelPipelineFactory)}.
    108  *
    109  * <pre>
    110  * {@link ServerBootstrap} b = ...;
    111  * b.setPipelineFactory(new MyPipelineFactory());
    112  *
    113  * public class MyPipelineFactory implements {@link ChannelPipelineFactory} {
    114  *   public {@link ChannelPipeline} getPipeline() throws Exception {
    115  *     // Create and configure a new pipeline for a new channel.
    116  *     {@link ChannelPipeline} p = {@link Channels}.pipeline();
    117  *     p.addLast("encoder", new EncodingHandler());
    118  *     p.addLast("decoder", new DecodingHandler());
    119  *     p.addLast("logic",   new LogicHandler());
    120  *     return p;
    121  *   }
    122  * }
    123  * </pre>
    124
    125  * <p>
    126  * The alternative approach, which works only in a certain situation, is to use
    127  * the default pipeline and let the bootstrap to shallow-copy the default
    128  * pipeline for each new channel:
    129  *
    130  * <pre>
    131  * {@link ServerBootstrap} b = ...;
    132  * {@link ChannelPipeline} p = b.getPipeline();
    133  *
    134  * // Add handlers to the default pipeline.
    135  * p.addLast("encoder", new EncodingHandler());
    136  * p.addLast("decoder", new DecodingHandler());
    137  * p.addLast("logic",   new LogicHandler());
    138  * </pre>
    139  *
    140  * Please note 'shallow-copy' here means that the added {@link ChannelHandler}s
    141  * are not cloned but only their references are added to the new pipeline.
    142  * Therefore, you cannot use this approach if you are going to open more than
    143  * one {@link Channel}s or run a server that accepts incoming connections to
    144  * create its child channels.
    145  *
    146  * <h3>Applying different settings for different {@link Channel}s</h3>
    147  *
    148  * {@link ServerBootstrap} is just a helper class.  It neither allocates nor
    149  * manages any resources.  What manages the resources is the
    150  * {@link ChannelFactory} implementation you specified in the constructor of
    151  * {@link ServerBootstrap}.  Therefore, it is OK to create as many
    152  * {@link ServerBootstrap} instances as you want with the same
    153  * {@link ChannelFactory} to apply different settings for different
    154  * {@link Channel}s.
    155  *
    156  * @apiviz.landmark
    157  */
    158 public class ServerBootstrap extends Bootstrap {
    159
    160     private volatile ChannelHandler parentHandler;
    161
    162     /**
    163      * Creates a new instance with no {@link ChannelFactory} set.
    164      * {@link #setFactory(ChannelFactory)} must be called before any I/O
    165      * operation is requested.
    166      */
    167     public ServerBootstrap() {
    168         super();
    169     }
    170
    171     /**
    172      * Creates a new instance with the specified initial {@link ChannelFactory}.
    173      */
    174     public ServerBootstrap(ChannelFactory channelFactory) {
    175         super(channelFactory);
    176     }
    177
    178     /**
    179      * {@inheritDoc}
    180      *
    181      * @throws IllegalArgumentException
    182      *         if the specified {@code factory} is not a
    183      *         {@link ServerChannelFactory}
    184      */
    185     @Override
    186     public void setFactory(ChannelFactory factory) {
    187         if (factory == null) {
    188             throw new NullPointerException("factory");
    189         }
    190         if (!(factory instanceof ServerChannelFactory)) {
    191             throw new IllegalArgumentException(
    192                     "factory must be a " +
    193                     ServerChannelFactory.class.getSimpleName() + ": " +
    194                     factory.getClass());
    195         }
    196         super.setFactory(factory);
    197     }
    198
    199     /**
    200      * Returns an optional {@link ChannelHandler} which intercepts an event
    201      * of a newly bound server-side channel which accepts incoming connections.
    202      *
    203      * @return the parent channel handler.
    204      *         {@code null} if no parent channel handler is set.
    205      */
    206     public ChannelHandler getParentHandler() {
    207         return parentHandler;
    208     }
    209
    210     /**
    211      * Sets an optional {@link ChannelHandler} which intercepts an event of
    212      * a newly bound server-side channel which accepts incoming connections.
    213      *
    214      * @param parentHandler
    215      *        the parent channel handler.
    216      *        {@code null} to unset the current parent channel handler.
    217      */
    218     public void setParentHandler(ChannelHandler parentHandler) {
    219         this.parentHandler = parentHandler;
    220     }
    221
    222     /**
    223      * Creates a new channel which is bound to the local address which was
    224      * specified in the current {@code "localAddress"} option.  This method is
    225      * similar to the following code:
    226      *
    227      * <pre>
    228      * {@link ServerBootstrap} b = ...;
    229      * b.bind(b.getOption("localAddress"));
    230      * </pre>
    231      *
    232      * @return a new bound channel which accepts incoming connections
    233      *
    234      * @throws IllegalStateException
    235      *         if {@code "localAddress"} option was not set
    236      * @throws ClassCastException
    237      *         if {@code "localAddress"} option's value is
    238      *         neither a {@link SocketAddress} nor {@code null}
    239      * @throws ChannelException
    240      *         if failed to create a new channel and
    241      *                      bind it to the local address
    242      */
    243     public Channel bind() {
    244         SocketAddress localAddress = (SocketAddress) getOption("localAddress");
    245         if (localAddress == null) {
    246             throw new IllegalStateException("localAddress option is not set.");
    247         }
    248         return bind(localAddress);
    249     }
    250
    251     /**
    252      * Creates a new channel which is bound to the specified local address.
    253      *
    254      * @return a new bound channel which accepts incoming connections
    255      *
    256      * @throws ChannelException
    257      *         if failed to create a new channel and
    258      *                      bind it to the local address
    259      */
    260     public Channel bind(final SocketAddress localAddress) {
    261         if (localAddress == null) {
    262             throw new NullPointerException("localAddress");
    263         }
    264
    265         final BlockingQueue<ChannelFuture> futureQueue =
    266             new LinkedBlockingQueue<ChannelFuture>();
    267
    268         ChannelHandler binder = new Binder(localAddress, futureQueue);
    269         ChannelHandler parentHandler = getParentHandler();
    270
    271         ChannelPipeline bossPipeline = pipeline();
    272         bossPipeline.addLast("binder", binder);
    273         if (parentHandler != null) {
    274             bossPipeline.addLast("userHandler", parentHandler);
    275         }
    276
    277         Channel channel = getFactory().newChannel(bossPipeline);
    278
    279         // Wait until the future is available.
    280         ChannelFuture future = null;
    281         boolean interrupted = false;
    282         do {
    283             try {
    284                 future = futureQueue.poll(Integer.MAX_VALUE, TimeUnit.SECONDS);
    285             } catch (InterruptedException e) {
    286                 interrupted = true;
    287             }
    288         } while (future == null);
    289
    290         if (interrupted) {
    291             Thread.currentThread().interrupt();
    292         }
    293
    294         // Wait for the future.
    295         future.awaitUninterruptibly();
    296         if (!future.isSuccess()) {
    297             future.getChannel().close().awaitUninterruptibly();
    298             throw new ChannelException("Failed to bind to: " + localAddress, future.getCause());
    299         }
    300
    301         return channel;
    302     }
    303
    304     private final class Binder extends SimpleChannelUpstreamHandler {
    305
    306         private final SocketAddress localAddress;
    307         private final BlockingQueue<ChannelFuture> futureQueue;
    308         private final Map<String, Object> childOptions =
    309             new HashMap<String, Object>();
    310
    311         Binder(SocketAddress localAddress, BlockingQueue<ChannelFuture> futureQueue) {
    312             this.localAddress = localAddress;
    313             this.futureQueue = futureQueue;
    314         }
    315
    316         @Override
    317         public void channelOpen(
    318                 ChannelHandlerContext ctx,
    319                 ChannelStateEvent evt) {
    320
    321             try {
    322                 evt.getChannel().getConfig().setPipelineFactory(getPipelineFactory());
    323
    324                 // Split options into two categories: parent and child.
    325                 Map<String, Object> allOptions = getOptions();
    326                 Map<String, Object> parentOptions = new HashMap<String, Object>();
    327                 for (Entry<String, Object> e: allOptions.entrySet()) {
    328                     if (e.getKey().startsWith("child.")) {
    329                         childOptions.put(
    330                                 e.getKey().substring(6),
    331                                 e.getValue());
    332                     } else if (!e.getKey().equals("pipelineFactory")) {
    333                         parentOptions.put(e.getKey(), e.getValue());
    334                     }
    335                 }
    336
    337                 // Apply parent options.
    338                 evt.getChannel().getConfig().setOptions(parentOptions);
    339             } finally {
    340                 ctx.sendUpstream(evt);
    341             }
    342
    343             boolean finished = futureQueue.offer(evt.getChannel().bind(localAddress));
    344             assert finished;
    345         }
    346
    347         @Override
    348         public void childChannelOpen(
    349                 ChannelHandlerContext ctx,
    350                 ChildChannelStateEvent e) throws Exception {
    351             // Apply child options.
    352             try {
    353                 e.getChildChannel().getConfig().setOptions(childOptions);
    354             } catch (Throwable t) {
    355                 Channels.fireExceptionCaught(e.getChildChannel(), t);
    356             }
    357             ctx.sendUpstream(e);
    358         }
    359
    360         @Override
    361         public void exceptionCaught(
    362                 ChannelHandlerContext ctx, ExceptionEvent e)
    363                 throws Exception {
    364             boolean finished = futureQueue.offer(failedFuture(e.getChannel(), e.getCause()));
    365             assert finished;
    366             ctx.sendUpstream(e);
    367         }
    368     }
    369 }