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 }