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 }