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 org.jboss.netty.channel.Channel;
19  import org.jboss.netty.channel.ChannelConfig;
20  import org.jboss.netty.channel.ChannelException;
21  import org.jboss.netty.channel.ChannelFactory;
22  import org.jboss.netty.channel.ChannelFuture;
23  import org.jboss.netty.channel.ChannelFutureListener;
24  import org.jboss.netty.channel.ChannelHandler;
25  import org.jboss.netty.channel.ChannelHandlerContext;
26  import org.jboss.netty.channel.ChannelPipeline;
27  import org.jboss.netty.channel.ChannelPipelineFactory;
28  import org.jboss.netty.channel.ChannelStateEvent;
29  import org.jboss.netty.channel.Channels;
30  import org.jboss.netty.channel.ChildChannelStateEvent;
31  import org.jboss.netty.channel.DefaultChannelFuture;
32  import org.jboss.netty.channel.ExceptionEvent;
33  import org.jboss.netty.channel.ServerChannelFactory;
34  import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
35  
36  import java.net.InetSocketAddress;
37  import java.net.SocketAddress;
38  import java.util.HashMap;
39  import java.util.Map;
40  import java.util.Map.Entry;
41  
42  import static org.jboss.netty.channel.Channels.*;
43  
44  /**
45   * A helper class which creates a new server-side {@link Channel} and accepts
46   * incoming connections.
47   *
48   * <h3>Only for connection oriented transports</h3>
49   *
50   * This bootstrap is for connection oriented transports only such as TCP/IP
51   * and local transport.  Use {@link ConnectionlessBootstrap} instead for
52   * connectionless transports.  Do not use this helper if you are using a
53   * connectionless transport such as UDP/IP which does not accept an incoming
54   * connection but receives messages by itself without creating a child channel.
55   *
56   * <h3>Parent channel and its children</h3>
57   *
58   * A parent channel is a channel which is supposed to accept incoming
59   * connections.  It is created by this bootstrap's {@link ChannelFactory} via
60   * {@link #bind()} and {@link #bind(SocketAddress)}.
61   * <p>
62   * Once successfully bound, the parent channel starts to accept incoming
63   * connections, and the accepted connections become the children of the
64   * parent channel.
65   *
66   * <h3>Configuring channels</h3>
67   *
68   * {@link #setOption(String, Object) Options} are used to configure both a
69   * parent channel and its child channels.  To configure the child channels,
70   * prepend {@code "child."} prefix to the actual option names of a child
71   * channel:
72   *
73   * <pre>
74   * {@link ServerBootstrap} b = ...;
75   *
76   * // Options for a parent channel
77   * b.setOption("localAddress", new {@link InetSocketAddress}(8080));
78   * b.setOption("reuseAddress", true);
79   *
80   * // Options for its children
81   * b.setOption("child.tcpNoDelay", true);
82   * b.setOption("child.receiveBufferSize", 1048576);
83   * </pre>
84   *
85   * For the detailed list of available options, please refer to
86   * {@link ChannelConfig} and its sub-types.
87   *
88   * <h3>Configuring a parent channel pipeline</h3>
89   *
90   * It is rare to customize the pipeline of a parent channel because what it is
91   * supposed to do is very typical.  However, you might want to add a handler
92   * to deal with some special needs such as degrading the process
93   * <a href="http://en.wikipedia.org/wiki/User_identifier_(Unix)">UID</a> from
94   * a <a href="http://en.wikipedia.org/wiki/Superuser">superuser</a> to a
95   * normal user and changing the current VM security manager for better
96   * security.  To support such a case,
97   * the {@link #setParentHandler(ChannelHandler) parentHandler} property is
98   * provided.
99   *
100  * <h3>Configuring a child channel pipeline</h3>
101  *
102  * Every channel has its own {@link ChannelPipeline} and you can configure it
103  * in two ways.
104  *
105  * The recommended approach is to specify a {@link ChannelPipelineFactory} by
106  * calling {@link #setPipelineFactory(ChannelPipelineFactory)}.
107  *
108  * <pre>
109  * {@link ServerBootstrap} b = ...;
110  * b.setPipelineFactory(new MyPipelineFactory());
111  *
112  * public class MyPipelineFactory implements {@link ChannelPipelineFactory} {
113  *   public {@link ChannelPipeline} getPipeline() throws Exception {
114  *     // Create and configure a new pipeline for a new channel.
115  *     {@link ChannelPipeline} p = {@link Channels}.pipeline();
116  *     p.addLast("encoder", new EncodingHandler());
117  *     p.addLast("decoder", new DecodingHandler());
118  *     p.addLast("logic",   new LogicHandler());
119  *     return p;
120  *   }
121  * }
122  * </pre>
123 
124  * <p>
125  * The alternative approach, which works only in a certain situation, is to use
126  * the default pipeline and let the bootstrap to shallow-copy the default
127  * pipeline for each new channel:
128  *
129  * <pre>
130  * {@link ServerBootstrap} b = ...;
131  * {@link ChannelPipeline} p = b.getPipeline();
132  *
133  * // Add handlers to the default pipeline.
134  * p.addLast("encoder", new EncodingHandler());
135  * p.addLast("decoder", new DecodingHandler());
136  * p.addLast("logic",   new LogicHandler());
137  * </pre>
138  *
139  * Please note 'shallow-copy' here means that the added {@link ChannelHandler}s
140  * are not cloned but only their references are added to the new pipeline.
141  * Therefore, you cannot use this approach if you are going to open more than
142  * one {@link Channel}s or run a server that accepts incoming connections to
143  * create its child channels.
144  *
145  * <h3>Applying different settings for different {@link Channel}s</h3>
146  *
147  * {@link ServerBootstrap} is just a helper class.  It neither allocates nor
148  * manages any resources.  What manages the resources is the
149  * {@link ChannelFactory} implementation you specified in the constructor of
150  * {@link ServerBootstrap}.  Therefore, it is OK to create as many
151  * {@link ServerBootstrap} instances as you want with the same
152  * {@link ChannelFactory} to apply different settings for different
153  * {@link Channel}s.
154  *
155  * @apiviz.landmark
156  */
157 public class ServerBootstrap extends Bootstrap {
158 
159     private volatile ChannelHandler parentHandler;
160 
161     /**
162      * Creates a new instance with no {@link ChannelFactory} set.
163      * {@link #setFactory(ChannelFactory)} must be called before any I/O
164      * operation is requested.
165      */
166     public ServerBootstrap() {
167     }
168 
169     /**
170      * Creates a new instance with the specified initial {@link ChannelFactory}.
171      */
172     public ServerBootstrap(ChannelFactory channelFactory) {
173         super(channelFactory);
174     }
175 
176     /**
177      * Sets the {@link ServerChannelFactory} that will be used to perform an I/O
178      * operation.  This method can be called only once and can't be called at
179      * all if the factory was specified in the constructor.
180      *
181      * @throws IllegalStateException
182      *         if the factory is already set
183      * @throws IllegalArgumentException
184      *         if the specified {@code factory} is not a
185      *         {@link ServerChannelFactory}
186      */
187     @Override
188     public void setFactory(ChannelFactory factory) {
189         if (factory == null) {
190             throw new NullPointerException("factory");
191         }
192         if (!(factory instanceof ServerChannelFactory)) {
193             throw new IllegalArgumentException(
194                     "factory must be a " +
195                     ServerChannelFactory.class.getSimpleName() + ": " +
196                     factory.getClass());
197         }
198         super.setFactory(factory);
199     }
200 
201     /**
202      * Returns an optional {@link ChannelHandler} which intercepts an event
203      * of a newly bound server-side channel which accepts incoming connections.
204      *
205      * @return the parent channel handler.
206      *         {@code null} if no parent channel handler is set.
207      */
208     public ChannelHandler getParentHandler() {
209         return parentHandler;
210     }
211 
212     /**
213      * Sets an optional {@link ChannelHandler} which intercepts an event of
214      * a newly bound server-side channel which accepts incoming connections.
215      *
216      * @param parentHandler
217      *        the parent channel handler.
218      *        {@code null} to unset the current parent channel handler.
219      */
220     public void setParentHandler(ChannelHandler parentHandler) {
221         this.parentHandler = parentHandler;
222     }
223 
224     /**
225      * Creates a new channel which is bound to the local address which was
226      * specified in the current {@code "localAddress"} option.  This method is
227      * similar to the following code:
228      *
229      * <pre>
230      * {@link ServerBootstrap} b = ...;
231      * b.bind(b.getOption("localAddress"));
232      * </pre>
233      *
234      * This operation will block until the channel is bound.
235      *
236      * @return a new bound channel which accepts incoming connections
237      *
238      * @throws IllegalStateException
239      *         if {@code "localAddress"} option was not set
240      * @throws ClassCastException
241      *         if {@code "localAddress"} option's value is
242      *         neither a {@link SocketAddress} nor {@code null}
243      * @throws ChannelException
244      *         if failed to create a new channel and
245      *                      bind it to the local address
246      */
247     public Channel bind() {
248         SocketAddress localAddress = (SocketAddress) getOption("localAddress");
249         if (localAddress == null) {
250             throw new IllegalStateException("localAddress option is not set.");
251         }
252         return bind(localAddress);
253     }
254 
255     /**
256      * Creates a new channel which is bound to the specified local address. This operation will block until
257      * the channel is bound.
258      *
259      * @return a new bound channel which accepts incoming connections
260      *
261      * @throws ChannelException
262      *         if failed to create a new channel and
263      *                      bind it to the local address
264      */
265     public Channel bind(final SocketAddress localAddress) {
266         ChannelFuture future = bindAsync(localAddress);
267 
268         // Wait for the future.
269         future.awaitUninterruptibly();
270         if (!future.isSuccess()) {
271             future.getChannel().close().awaitUninterruptibly();
272             throw new ChannelException("Failed to bind to: " + localAddress, future.getCause());
273         }
274 
275         return future.getChannel();
276     }
277 
278     /**
279      * Bind a channel asynchronous to the local address
280      * specified in the current {@code "localAddress"} option.  This method is
281      * similar to the following code:
282      *
283      * <pre>
284      * {@link ServerBootstrap} b = ...;
285      * b.bindAsync(b.getOption("localAddress"));
286      * </pre>
287      *
288      *
289      * @return a new {@link ChannelFuture} which will be notified once the Channel is
290      * bound and accepts incoming connections
291      *
292      * @throws IllegalStateException
293      *         if {@code "localAddress"} option was not set
294      * @throws ClassCastException
295      *         if {@code "localAddress"} option's value is
296      *         neither a {@link SocketAddress} nor {@code null}
297      * @throws ChannelException
298      *         if failed to create a new channel and
299      *                      bind it to the local address
300      */
301     public ChannelFuture bindAsync() {
302         SocketAddress localAddress = (SocketAddress) getOption("localAddress");
303         if (localAddress == null) {
304             throw new IllegalStateException("localAddress option is not set.");
305         }
306         return bindAsync(localAddress);
307     }
308 
309     /**
310      * Bind a channel asynchronous to the specified local address.
311      *
312      * @return a new {@link ChannelFuture} which will be notified once the Channel is
313      * bound and accepts incoming connections
314      *
315      */
316     public ChannelFuture bindAsync(final SocketAddress localAddress) {
317         if (localAddress == null) {
318             throw new NullPointerException("localAddress");
319         }
320         Binder binder = new Binder(localAddress);
321         ChannelHandler parentHandler = getParentHandler();
322 
323         ChannelPipeline bossPipeline = pipeline();
324         bossPipeline.addLast("binder", binder);
325         if (parentHandler != null) {
326             bossPipeline.addLast("userHandler", parentHandler);
327         }
328 
329         Channel channel = getFactory().newChannel(bossPipeline);
330         final ChannelFuture bfuture = new DefaultChannelFuture(channel, false);
331         binder.bindFuture.addListener(new ChannelFutureListener() {
332             public void operationComplete(ChannelFuture future) throws Exception {
333                 if (future.isSuccess()) {
334                     bfuture.setSuccess();
335                 } else {
336                     // Call close on bind failure
337                     bfuture.getChannel().close();
338                     bfuture.setFailure(future.getCause());
339                 }
340             }
341         });
342         return bfuture;
343     }
344 
345     private final class Binder extends SimpleChannelUpstreamHandler {
346 
347         private final SocketAddress localAddress;
348         private final Map<String, Object> childOptions =
349             new HashMap<String, Object>();
350         private final DefaultChannelFuture bindFuture = new DefaultChannelFuture(null, false);
351         Binder(SocketAddress localAddress) {
352             this.localAddress = localAddress;
353         }
354 
355         @Override
356         public void channelOpen(
357                 ChannelHandlerContext ctx,
358                 ChannelStateEvent evt) {
359 
360             try {
361                 evt.getChannel().getConfig().setPipelineFactory(getPipelineFactory());
362 
363                 // Split options into two categories: parent and child.
364                 Map<String, Object> allOptions = getOptions();
365                 Map<String, Object> parentOptions = new HashMap<String, Object>();
366                 for (Entry<String, Object> e: allOptions.entrySet()) {
367                     if (e.getKey().startsWith("child.")) {
368                         childOptions.put(
369                                 e.getKey().substring(6),
370                                 e.getValue());
371                     } else if (!"pipelineFactory".equals(e.getKey())) {
372                         parentOptions.put(e.getKey(), e.getValue());
373                     }
374                 }
375 
376                 // Apply parent options.
377                 evt.getChannel().getConfig().setOptions(parentOptions);
378             } finally {
379                 ctx.sendUpstream(evt);
380             }
381 
382             evt.getChannel().bind(localAddress).addListener(new ChannelFutureListener() {
383                 public void operationComplete(ChannelFuture future) throws Exception {
384                     if (future.isSuccess()) {
385                         bindFuture.setSuccess();
386                     } else {
387                         bindFuture.setFailure(future.getCause());
388                     }
389                 }
390             });
391         }
392 
393         @Override
394         public void childChannelOpen(
395                 ChannelHandlerContext ctx,
396                 ChildChannelStateEvent e) throws Exception {
397             // Apply child options.
398             try {
399                 e.getChildChannel().getConfig().setOptions(childOptions);
400             } catch (Throwable t) {
401                 fireExceptionCaught(e.getChildChannel(), t);
402             }
403             ctx.sendUpstream(e);
404         }
405 
406         @Override
407         public void exceptionCaught(
408                 ChannelHandlerContext ctx, ExceptionEvent e)
409                 throws Exception {
410             bindFuture.setFailure(e.getCause());
411             ctx.sendUpstream(e);
412         }
413     }
414 }