View Javadoc

    1   /*
    2    * Copyright 2009 Red Hat, Inc.
    3    *
    4    * Red Hat licenses this file to you under the Apache License, version 2.0
    5    * (the "License"); you may not use this file except in compliance with the
    6    * 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  * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
    116  * @author <a href="http://gleamynode.net/">Trustin Lee</a>
    117  *
    118  * @version $Rev: 2338 $, $Date: 2010-07-07 13:33:47 +0900 (Wed, 07 Jul 2010) $
    119  *
    120  * @apiviz.landmark
    121  */
    122 public class ConnectionlessBootstrap extends Bootstrap {
    123
    124     /**
    125      * Creates a new instance with no {@link ChannelFactory} set.
    126      * {@link #setFactory(ChannelFactory)} must be called before any I/O
    127      * operation is requested.
    128      */
    129     public ConnectionlessBootstrap() {
    130         super();
    131     }
    132
    133     /**
    134      * Creates a new instance with the specified initial {@link ChannelFactory}.
    135      */
    136     public ConnectionlessBootstrap(ChannelFactory channelFactory) {
    137         super(channelFactory);
    138     }
    139
    140     /**
    141      * Creates a new channel which is bound to the local address which was
    142      * specified in the current {@code "localAddress"} option.  This method is
    143      * similar to the following code:
    144      *
    145      * <pre>
    146      * {@link ConnectionlessBootstrap} b = ...;
    147      * b.bind(b.getOption("localAddress"));
    148      * </pre>
    149      *
    150      * @return a new bound channel which accepts incoming connections
    151      *
    152      * @throws IllegalStateException
    153      *         if {@code "localAddress"} option was not set
    154      * @throws ClassCastException
    155      *         if {@code "localAddress"} option's value is
    156      *         neither a {@link SocketAddress} nor {@code null}
    157      * @throws ChannelException
    158      *         if failed to create a new channel and
    159      *                      bind it to the local address
    160      */
    161     public Channel bind() {
    162         SocketAddress localAddress = (SocketAddress) getOption("localAddress");
    163         if (localAddress == null) {
    164             throw new IllegalStateException("localAddress option is not set.");
    165         }
    166         return bind(localAddress);
    167     }
    168
    169     /**
    170      * Creates a new channel which is bound to the specified local address.
    171      *
    172      * @return a new bound channel which accepts incoming connections
    173      *
    174      * @throws ChannelException
    175      *         if failed to create a new channel and
    176      *                      bind it to the local address
    177      */
    178     public Channel bind(final SocketAddress localAddress) {
    179         if (localAddress == null) {
    180             throw new NullPointerException("localAddress");
    181         }
    182
    183         ChannelPipeline pipeline;
    184         try {
    185             pipeline = getPipelineFactory().getPipeline();
    186         } catch (Exception e) {
    187             throw new ChannelPipelineException("Failed to initialize a pipeline.", e);
    188         }
    189
    190         Channel ch = getFactory().newChannel(pipeline);
    191
    192         // Apply options.
    193         ch.getConfig().setPipelineFactory(getPipelineFactory());
    194         ch.getConfig().setOptions(getOptions());
    195
    196         // Bind
    197         ChannelFuture future = ch.bind(localAddress);
    198
    199         // Wait for the future.
    200         future.awaitUninterruptibly();
    201         if (!future.isSuccess()) {
    202             future.getChannel().close().awaitUninterruptibly();
    203             throw new ChannelException("Failed to bind to: " + localAddress, future.getCause());
    204         }
    205
    206         return ch;
    207     }
    208
    209     /**
    210      * Creates a new connected channel with the current {@code "remoteAddress"}
    211      * and {@code "localAddress"} option.  If the {@code "localAddress"} option
    212      * is not set, the local address of a new channel is determined
    213      * automatically. This method is similar to the following code:
    214      *
    215      * <pre>
    216      * {@link ConnectionlessBootstrap} b = ...;
    217      * b.connect(b.getOption("remoteAddress"), b.getOption("localAddress"));
    218      * </pre>
    219      *
    220      * @return a future object which notifies when the creation of the connected
    221      *         channel succeeds or fails
    222      *
    223      * @throws IllegalStateException
    224      *         if {@code "remoteAddress"} option was not set
    225      * @throws ClassCastException
    226      *         if {@code "remoteAddress"} or {@code "localAddress"} option's
    227      *            value is neither a {@link SocketAddress} nor {@code null}
    228      * @throws ChannelPipelineException
    229      *         if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory}
    230      *            failed to create a new {@link ChannelPipeline}
    231      */
    232     public ChannelFuture connect() {
    233         SocketAddress remoteAddress = (SocketAddress) getOption("remoteAddress");
    234         if (remoteAddress == null) {
    235             throw new IllegalStateException("remoteAddress option is not set.");
    236         }
    237         return connect(remoteAddress);
    238     }
    239
    240     /**
    241      * Creates a new connected channel with the specified
    242      * {@code "remoteAddress"} and the current {@code "localAddress"} option.
    243      * If the {@code "localAddress"} option is not set, the local address of
    244      * a new channel is determined automatically.  This method is identical
    245      * with the following code:
    246      *
    247      * <pre>
    248      * {@link ConnectionlessBootstrap} b = ...;
    249      * b.connect(remoteAddress, b.getOption("localAddress"));
    250      * </pre>
    251      *
    252      * @return a future object which notifies when the creation of the connected
    253      *         channel succeeds or fails
    254      *
    255      * @throws ClassCastException
    256      *         if {@code "localAddress"} option's value is
    257      *            neither a {@link SocketAddress} nor {@code null}
    258      * @throws ChannelPipelineException
    259      *         if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory}
    260      *            failed to create a new {@link ChannelPipeline}
    261      */
    262     public ChannelFuture connect(SocketAddress remoteAddress) {
    263         if (remoteAddress == null) {
    264             throw new NullPointerException("remotedAddress");
    265         }
    266         SocketAddress localAddress = (SocketAddress) getOption("localAddress");
    267         return connect(remoteAddress, localAddress);
    268     }
    269
    270     /**
    271      * Creates a new connected channel with the specified
    272      * {@code "remoteAddress"} and the specified {@code "localAddress"}.
    273      * If the specified local address is {@code null}, the local address of a
    274      * new channel is determined automatically.
    275      *
    276      * @return a future object which notifies when the creation of the connected
    277      *         channel succeeds or fails
    278      *
    279      * @throws ChannelPipelineException
    280      *         if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory}
    281      *            failed to create a new {@link ChannelPipeline}
    282      */
    283     public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
    284
    285         if (remoteAddress == null) {
    286             throw new NullPointerException("remoteAddress");
    287         }
    288
    289         ChannelPipeline pipeline;
    290         try {
    291             pipeline = getPipelineFactory().getPipeline();
    292         } catch (Exception e) {
    293             throw new ChannelPipelineException("Failed to initialize a pipeline.", e);
    294         }
    295
    296         // Set the options.
    297         Channel ch = getFactory().newChannel(pipeline);
    298         ch.getConfig().setOptions(getOptions());
    299
    300         // Bind.
    301         if (localAddress != null) {
    302             ch.bind(localAddress);
    303         }
    304
    305         // Connect.
    306         return ch.connect(remoteAddress);
    307     }
    308 }