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