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("remoteAddress");
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 }