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.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  * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
    107  * @author <a href="http://gleamynode.net/">Trustin Lee</a>
    108  *
    109  * @version $Rev: 2337 $, $Date: 2010-07-07 13:32:10 +0900 (Wed, 07 Jul 2010) $
    110  *
    111  * @apiviz.landmark
    112  */
    113 public class ClientBootstrap extends Bootstrap {
    114
    115     /**
    116      * Creates a new instance with no {@link ChannelFactory} set.
    117      * {@link #setFactory(ChannelFactory)} must be called before any I/O
    118      * operation is requested.
    119      */
    120     public ClientBootstrap() {
    121         super();
    122     }
    123
    124     /**
    125      * Creates a new instance with the specified initial {@link ChannelFactory}.
    126      */
    127     public ClientBootstrap(ChannelFactory channelFactory) {
    128         super(channelFactory);
    129     }
    130
    131     /**
    132      * Attempts a new connection with the current {@code "remoteAddress"} and
    133      * {@code "localAddress"} option.  If the {@code "localAddress"} option is
    134      * not set, the local address of a new channel is determined automatically.
    135      * This method is similar to the following code:
    136      *
    137      * <pre>
    138      * {@link ClientBootstrap} b = ...;
    139      * b.connect(b.getOption("remoteAddress"), b.getOption("localAddress"));
    140      * </pre>
    141      *
    142      * @return a future object which notifies when this connection attempt
    143      *         succeeds or fails
    144      *
    145      * @throws IllegalStateException
    146      *         if {@code "remoteAddress"} option was not set
    147      * @throws ClassCastException
    148      *         if {@code "remoteAddress"} or {@code "localAddress"} option's
    149      *            value is neither a {@link SocketAddress} nor {@code null}
    150      * @throws ChannelPipelineException
    151      *         if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory}
    152      *            failed to create a new {@link ChannelPipeline}
    153      */
    154     public ChannelFuture connect() {
    155         SocketAddress remoteAddress = (SocketAddress) getOption("remoteAddress");
    156         if (remoteAddress == null) {
    157             throw new IllegalStateException("remoteAddress option is not set.");
    158         }
    159         return connect(remoteAddress);
    160     }
    161
    162     /**
    163      * Attempts a new connection with the specified {@code remoteAddress} and
    164      * the current {@code "localAddress"} option. If the {@code "localAddress"}
    165      * option is not set, the local address of a new channel is determined
    166      * automatically.  This method is identical with the following code:
    167      *
    168      * <pre>
    169      * {@link ClientBootstrap} b = ...;
    170      * b.connect(remoteAddress, b.getOption("localAddress"));
    171      * </pre>
    172      *
    173      * @return a future object which notifies when this connection attempt
    174      *         succeeds or fails
    175      *
    176      * @throws ClassCastException
    177      *         if {@code "localAddress"} option's value is
    178      *            neither a {@link SocketAddress} nor {@code null}
    179      * @throws ChannelPipelineException
    180      *         if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory}
    181      *            failed to create a new {@link ChannelPipeline}
    182      */
    183     public ChannelFuture connect(SocketAddress remoteAddress) {
    184         if (remoteAddress == null) {
    185             throw new NullPointerException("remotedAddress");
    186         }
    187         SocketAddress localAddress = (SocketAddress) getOption("localAddress");
    188         return connect(remoteAddress, localAddress);
    189     }
    190
    191     /**
    192      * Attempts a new connection with the specified {@code remoteAddress} and
    193      * the specified {@code localAddress}.  If the specified local address is
    194      * {@code null}, the local address of a new channel is determined
    195      * automatically.
    196      *
    197      * @return a future object which notifies when this connection attempt
    198      *         succeeds or fails
    199      *
    200      * @throws ChannelPipelineException
    201      *         if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory}
    202      *            failed to create a new {@link ChannelPipeline}
    203      */
    204     public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
    205
    206         if (remoteAddress == null) {
    207             throw new NullPointerException("remoteAddress");
    208         }
    209
    210         ChannelPipeline pipeline;
    211         try {
    212             pipeline = getPipelineFactory().getPipeline();
    213         } catch (Exception e) {
    214             throw new ChannelPipelineException("Failed to initialize a pipeline.", e);
    215         }
    216
    217         // Set the options.
    218         Channel ch = getFactory().newChannel(pipeline);
    219         ch.getConfig().setOptions(getOptions());
    220
    221         // Bind.
    222         if (localAddress != null) {
    223             ch.bind(localAddress);
    224         }
    225
    226         // Connect.
    227         return ch.connect(remoteAddress);
    228     }
    229 }