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 }