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 }