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