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