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 }