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