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 java.net.InetSocketAddress;
    19  import java.net.SocketAddress;
    20
    21  import org.jboss.netty.channel.Channel;
    22  import org.jboss.netty.channel.ChannelConfig;
    23  import org.jboss.netty.channel.ChannelException;
    24  import org.jboss.netty.channel.ChannelFactory;
    25  import org.jboss.netty.channel.ChannelFuture;
    26  import org.jboss.netty.channel.ChannelHandler;
    27  import org.jboss.netty.channel.ChannelPipeline;
    28  import org.jboss.netty.channel.ChannelPipelineException;
    29  import org.jboss.netty.channel.ChannelPipelineFactory;
    30  import org.jboss.netty.channel.Channels;
    31
    32  /**
    33   * A helper class which creates a new server-side {@link Channel} for a
    34   * connectionless transport.
    35   *
    36   * <h3>Only for connectionless transports</h3>
    37   *
    38   * This bootstrap is for connectionless transports only such as UDP/IP.
    39   * Use {@link ServerBootstrap} instead for connection oriented transports.
    40   * Do not use this helper if you are using a connection oriented transport such
    41   * as TCP/IP and local transport which accepts an incoming connection and lets
    42   * the accepted child channels handle received messages.
    43   *
    44   * <h3>Configuring channels</h3>
    45   *
    46   * {@link #setOption(String, Object) Options} are used to configure a channel:
    47   *
    48   * <pre>
    49   * {@link ConnectionlessBootstrap} b = ...;
    50   *
    51   * // Options for a new channel
    52   * b.setOption("localAddress", new {@link InetSocketAddress}(8080));
    53   * b.setOption("tcpNoDelay", true);
    54   * b.setOption("receiveBufferSize", 1048576);
    55   * </pre>
    56   *
    57   * For the detailed list of available options, please refer to
    58   * {@link ChannelConfig} and its sub-types.
    59   *
    60   * <h3>Configuring a channel pipeline</h3>
    61   *
    62   * Every channel has its own {@link ChannelPipeline} and you can configure it
    63   * in two ways.
    64   *
    65   * The recommended approach is to specify a {@link ChannelPipelineFactory} by
    66   * calling {@link #setPipelineFactory(ChannelPipelineFactory)}.
    67   *
    68   * <pre>
    69   * {@link ConnectionlessBootstrap} b = ...;
    70   * b.setPipelineFactory(new MyPipelineFactory());
    71   *
    72   * public class MyPipelineFactory implements {@link ChannelPipelineFactory} {
    73   *   public {@link ChannelPipeline} getPipeline() throws Exception {
    74   *     // Create and configure a new pipeline for a new channel.
    75   *     {@link ChannelPipeline} p = {@link Channels}.pipeline();
    76   *     p.addLast("encoder", new EncodingHandler());
    77   *     p.addLast("decoder", new DecodingHandler());
    78   *     p.addLast("logic",   new LogicHandler());
    79   *     return p;
    80   *   }
    81   * }
    82   * </pre>
    83
    84   * <p>
    85   * The alternative approach, which works only in a certain situation, is to use
    86   * the default pipeline and let the bootstrap to shallow-copy the default
    87   * pipeline for each new channel:
    88   *
    89   * <pre>
    90   * {@link ConnectionlessBootstrap} b = ...;
    91   * {@link ChannelPipeline} p = b.getPipeline();
    92   *
    93   * // Add handlers to the default pipeline.
    94   * p.addLast("encoder", new EncodingHandler());
    95   * p.addLast("decoder", new DecodingHandler());
    96   * p.addLast("logic",   new LogicHandler());
    97   * </pre>
    98   *
    99   * Please note 'shallow-copy' here means that the added {@link ChannelHandler}s
    100  * are not cloned but only their references are added to the new pipeline.
    101  * Therefore, you cannot use this approach if you are going to open more than
    102  * one {@link Channel}s or run a server that accepts incoming connections to
    103  * create its child channels.
    104  *
    105  * <h3>Applying different settings for different {@link Channel}s</h3>
    106  *
    107  * {@link ConnectionlessBootstrap} is just a helper class.  It neither
    108  * allocates nor manages any resources.  What manages the resources is the
    109  * {@link ChannelFactory} implementation you specified in the constructor of
    110  * {@link ConnectionlessBootstrap}.  Therefore, it is OK to create as
    111  * many {@link ConnectionlessBootstrap} instances as you want with the same
    112  * {@link ChannelFactory} to apply different settings for different
    113  * {@link Channel}s.
    114  *
    115  * @apiviz.landmark
    116  */
    117 public class ConnectionlessBootstrap extends Bootstrap {
    118
    119     /**
    120      * Creates a new instance with no {@link ChannelFactory} set.
    121      * {@link #setFactory(ChannelFactory)} must be called before any I/O
    122      * operation is requested.
    123      */
    124     public ConnectionlessBootstrap() {
    125         super();
    126     }
    127
    128     /**
    129      * Creates a new instance with the specified initial {@link ChannelFactory}.
    130      */
    131     public ConnectionlessBootstrap(ChannelFactory channelFactory) {
    132         super(channelFactory);
    133     }
    134
    135     /**
    136      * Creates a new channel which is bound to the local address which was
    137      * specified in the current {@code "localAddress"} option.  This method is
    138      * similar to the following code:
    139      *
    140      * <pre>
    141      * {@link ConnectionlessBootstrap} b = ...;
    142      * b.bind(b.getOption("localAddress"));
    143      * </pre>
    144      *
    145      * @return a new bound channel which accepts incoming connections
    146      *
    147      * @throws IllegalStateException
    148      *         if {@code "localAddress"} option was not set
    149      * @throws ClassCastException
    150      *         if {@code "localAddress"} option's value is
    151      *         neither a {@link SocketAddress} nor {@code null}
    152      * @throws ChannelException
    153      *         if failed to create a new channel and
    154      *                      bind it to the local address
    155      */
    156     public Channel bind() {
    157         SocketAddress localAddress = (SocketAddress) getOption("localAddress");
    158         if (localAddress == null) {
    159             throw new IllegalStateException("localAddress option is not set.");
    160         }
    161         return bind(localAddress);
    162     }
    163
    164     /**
    165      * Creates a new channel which is bound to the specified local address.
    166      *
    167      * @return a new bound channel which accepts incoming connections
    168      *
    169      * @throws ChannelException
    170      *         if failed to create a new channel and
    171      *                      bind it to the local address
    172      */
    173     public Channel bind(final SocketAddress localAddress) {
    174         if (localAddress == null) {
    175             throw new NullPointerException("localAddress");
    176         }
    177
    178         ChannelPipeline pipeline;
    179         try {
    180             pipeline = getPipelineFactory().getPipeline();
    181         } catch (Exception e) {
    182             throw new ChannelPipelineException("Failed to initialize a pipeline.", e);
    183         }
    184
    185         Channel ch = getFactory().newChannel(pipeline);
    186
    187         // Apply options.
    188         boolean success = false;
    189         try {
    190             ch.getConfig().setOptions(getOptions());
    191             success = true;
    192         } finally {
    193             if (!success) {
    194                 ch.close();
    195             }
    196         }
    197
    198         // Bind
    199         ChannelFuture future = ch.bind(localAddress);
    200
    201         // Wait for the future.
    202         future.awaitUninterruptibly();
    203         if (!future.isSuccess()) {
    204             future.getChannel().close().awaitUninterruptibly();
    205             throw new ChannelException("Failed to bind to: " + localAddress, future.getCause());
    206         }
    207
    208         return ch;
    209     }
    210
    211     /**
    212      * Creates a new connected channel with the current {@code "remoteAddress"}
    213      * and {@code "localAddress"} option.  If the {@code "localAddress"} option
    214      * is not set, the local address of a new channel is determined
    215      * automatically. This method is similar to the following code:
    216      *
    217      * <pre>
    218      * {@link ConnectionlessBootstrap} b = ...;
    219      * b.connect(b.getOption("remoteAddress"), b.getOption("localAddress"));
    220      * </pre>
    221      *
    222      * @return a future object which notifies when the creation of the connected
    223      *         channel succeeds or fails
    224      *
    225      * @throws IllegalStateException
    226      *         if {@code "remoteAddress"} option was not set
    227      * @throws ClassCastException
    228      *         if {@code "remoteAddress"} or {@code "localAddress"} option's
    229      *            value is neither a {@link SocketAddress} nor {@code null}
    230      * @throws ChannelPipelineException
    231      *         if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory}
    232      *            failed to create a new {@link ChannelPipeline}
    233      */
    234     public ChannelFuture connect() {
    235         SocketAddress remoteAddress = (SocketAddress) getOption("remoteAddress");
    236         if (remoteAddress == null) {
    237             throw new IllegalStateException("remoteAddress option is not set.");
    238         }
    239         return connect(remoteAddress);
    240     }
    241
    242     /**
    243      * Creates a new connected channel with the specified
    244      * {@code "remoteAddress"} and the current {@code "localAddress"} option.
    245      * If the {@code "localAddress"} option is not set, the local address of
    246      * a new channel is determined automatically.  This method is identical
    247      * with the following code:
    248      *
    249      * <pre>
    250      * {@link ConnectionlessBootstrap} b = ...;
    251      * b.connect(remoteAddress, b.getOption("localAddress"));
    252      * </pre>
    253      *
    254      * @return a future object which notifies when the creation of the connected
    255      *         channel succeeds or fails
    256      *
    257      * @throws ClassCastException
    258      *         if {@code "localAddress"} option's value is
    259      *            neither a {@link SocketAddress} nor {@code null}
    260      * @throws ChannelPipelineException
    261      *         if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory}
    262      *            failed to create a new {@link ChannelPipeline}
    263      */
    264     public ChannelFuture connect(SocketAddress remoteAddress) {
    265         if (remoteAddress == null) {
    266             throw new NullPointerException("remotedAddress");
    267         }
    268         SocketAddress localAddress = (SocketAddress) getOption("localAddress");
    269         return connect(remoteAddress, localAddress);
    270     }
    271
    272     /**
    273      * Creates a new connected channel with the specified
    274      * {@code "remoteAddress"} and the specified {@code "localAddress"}.
    275      * If the specified local address is {@code null}, the local address of a
    276      * new channel is determined automatically.
    277      *
    278      * @return a future object which notifies when the creation of the connected
    279      *         channel succeeds or fails
    280      *
    281      * @throws ChannelPipelineException
    282      *         if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory}
    283      *            failed to create a new {@link ChannelPipeline}
    284      */
    285     public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
    286
    287         if (remoteAddress == null) {
    288             throw new NullPointerException("remoteAddress");
    289         }
    290
    291         ChannelPipeline pipeline;
    292         try {
    293             pipeline = getPipelineFactory().getPipeline();
    294         } catch (Exception e) {
    295             throw new ChannelPipelineException("Failed to initialize a pipeline.", e);
    296         }
    297
    298         // Set the options.
    299         Channel ch = getFactory().newChannel(pipeline);
    300         boolean success = false;
    301         try {
    302             ch.getConfig().setOptions(getOptions());
    303             success = true;
    304         } finally {
    305             if (!success) {
    306                 ch.close();
    307             }
    308         }
    309
    310         // Bind.
    311         if (localAddress != null) {
    312             ch.bind(localAddress);
    313         }
    314
    315         // Connect.
    316         return ch.connect(remoteAddress);
    317     }
    318 }