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     }
169 
170     /**
171      * Creates a new instance with the specified initial {@link ChannelFactory}.
172      */
173     public ServerBootstrap(ChannelFactory channelFactory) {
174         super(channelFactory);
175     }
176 
177     /**
178      * {@inheritDoc}
179      *
180      * @throws IllegalArgumentException
181      *         if the specified {@code factory} is not a
182      *         {@link ServerChannelFactory}
183      */
184     @Override
185     public void setFactory(ChannelFactory factory) {
186         if (factory == null) {
187             throw new NullPointerException("factory");
188         }
189         if (!(factory instanceof ServerChannelFactory)) {
190             throw new IllegalArgumentException(
191                     "factory must be a " +
192                     ServerChannelFactory.class.getSimpleName() + ": " +
193                     factory.getClass());
194         }
195         super.setFactory(factory);
196     }
197 
198     /**
199      * Returns an optional {@link ChannelHandler} which intercepts an event
200      * of a newly bound server-side channel which accepts incoming connections.
201      *
202      * @return the parent channel handler.
203      *         {@code null} if no parent channel handler is set.
204      */
205     public ChannelHandler getParentHandler() {
206         return parentHandler;
207     }
208 
209     /**
210      * Sets an optional {@link ChannelHandler} which intercepts an event of
211      * a newly bound server-side channel which accepts incoming connections.
212      *
213      * @param parentHandler
214      *        the parent channel handler.
215      *        {@code null} to unset the current parent channel handler.
216      */
217     public void setParentHandler(ChannelHandler parentHandler) {
218         this.parentHandler = parentHandler;
219     }
220 
221     /**
222      * Creates a new channel which is bound to the local address which was
223      * specified in the current {@code "localAddress"} option.  This method is
224      * similar to the following code:
225      *
226      * <pre>
227      * {@link ServerBootstrap} b = ...;
228      * b.bind(b.getOption("localAddress"));
229      * </pre>
230      *
231      * @return a new bound channel which accepts incoming connections
232      *
233      * @throws IllegalStateException
234      *         if {@code "localAddress"} option was not set
235      * @throws ClassCastException
236      *         if {@code "localAddress"} option's value is
237      *         neither a {@link SocketAddress} nor {@code null}
238      * @throws ChannelException
239      *         if failed to create a new channel and
240      *                      bind it to the local address
241      */
242     public Channel bind() {
243         SocketAddress localAddress = (SocketAddress) getOption("localAddress");
244         if (localAddress == null) {
245             throw new IllegalStateException("localAddress option is not set.");
246         }
247         return bind(localAddress);
248     }
249 
250     /**
251      * Creates a new channel which is bound to the specified local address.
252      *
253      * @return a new bound channel which accepts incoming connections
254      *
255      * @throws ChannelException
256      *         if failed to create a new channel and
257      *                      bind it to the local address
258      */
259     public Channel bind(final SocketAddress localAddress) {
260         if (localAddress == null) {
261             throw new NullPointerException("localAddress");
262         }
263 
264         final BlockingQueue<ChannelFuture> futureQueue =
265             new LinkedBlockingQueue<ChannelFuture>();
266 
267         ChannelHandler binder = new Binder(localAddress, futureQueue);
268         ChannelHandler parentHandler = getParentHandler();
269 
270         ChannelPipeline bossPipeline = pipeline();
271         bossPipeline.addLast("binder", binder);
272         if (parentHandler != null) {
273             bossPipeline.addLast("userHandler", parentHandler);
274         }
275 
276         Channel channel = getFactory().newChannel(bossPipeline);
277 
278         // Wait until the future is available.
279         ChannelFuture future = null;
280         boolean interrupted = false;
281         do {
282             try {
283                 future = futureQueue.poll(Integer.MAX_VALUE, TimeUnit.SECONDS);
284             } catch (InterruptedException e) {
285                 interrupted = true;
286             }
287         } while (future == null);
288 
289         if (interrupted) {
290             Thread.currentThread().interrupt();
291         }
292 
293         // Wait for the future.
294         future.awaitUninterruptibly();
295         if (!future.isSuccess()) {
296             future.getChannel().close().awaitUninterruptibly();
297             throw new ChannelException("Failed to bind to: " + localAddress, future.getCause());
298         }
299 
300         return channel;
301     }
302 
303     private final class Binder extends SimpleChannelUpstreamHandler {
304 
305         private final SocketAddress localAddress;
306         private final BlockingQueue<ChannelFuture> futureQueue;
307         private final Map<String, Object> childOptions =
308             new HashMap<String, Object>();
309 
310         Binder(SocketAddress localAddress, BlockingQueue<ChannelFuture> futureQueue) {
311             this.localAddress = localAddress;
312             this.futureQueue = futureQueue;
313         }
314 
315         @Override
316         public void channelOpen(
317                 ChannelHandlerContext ctx,
318                 ChannelStateEvent evt) {
319 
320             try {
321                 evt.getChannel().getConfig().setPipelineFactory(getPipelineFactory());
322 
323                 // Split options into two categories: parent and child.
324                 Map<String, Object> allOptions = getOptions();
325                 Map<String, Object> parentOptions = new HashMap<String, Object>();
326                 for (Entry<String, Object> e: allOptions.entrySet()) {
327                     if (e.getKey().startsWith("child.")) {
328                         childOptions.put(
329                                 e.getKey().substring(6),
330                                 e.getValue());
331                     } else if (!e.getKey().equals("pipelineFactory")) {
332                         parentOptions.put(e.getKey(), e.getValue());
333                     }
334                 }
335 
336                 // Apply parent options.
337                 evt.getChannel().getConfig().setOptions(parentOptions);
338             } finally {
339                 ctx.sendUpstream(evt);
340             }
341 
342             boolean finished = futureQueue.offer(evt.getChannel().bind(localAddress));
343             assert finished;
344         }
345 
346         @Override
347         public void childChannelOpen(
348                 ChannelHandlerContext ctx,
349                 ChildChannelStateEvent e) throws Exception {
350             // Apply child options.
351             try {
352                 e.getChildChannel().getConfig().setOptions(childOptions);
353             } catch (Throwable t) {
354                 fireExceptionCaught(e.getChildChannel(), t);
355             }
356             ctx.sendUpstream(e);
357         }
358 
359         @Override
360         public void exceptionCaught(
361                 ChannelHandlerContext ctx, ExceptionEvent e)
362                 throws Exception {
363             boolean finished = futureQueue.offer(failedFuture(e.getChannel(), e.getCause()));
364             assert finished;
365             ctx.sendUpstream(e);
366         }
367     }
368 }