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 }