1 /* 2 * Copyright 2009 Red Hat, Inc. 3 * 4 * Red Hat licenses this file to you under the Apache License, version 2.0 5 * (the "License"); you may not use this file except in compliance with the 6 * 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 * @author <a href="http://www.jboss.org/netty/">The Netty Project</a> 157 * @author <a href="http://gleamynode.net/">Trustin Lee</a> 158 * 159 * @version $Rev: 2344 $, $Date: 2010-07-07 16:55:37 +0900 (Wed, 07 Jul 2010) $ 160 * 161 * @apiviz.landmark 162 */ 163 public class ServerBootstrap extends Bootstrap { 164 165 private volatile ChannelHandler parentHandler; 166 167 /** 168 * Creates a new instance with no {@link ChannelFactory} set. 169 * {@link #setFactory(ChannelFactory)} must be called before any I/O 170 * operation is requested. 171 */ 172 public ServerBootstrap() { 173 super(); 174 } 175 176 /** 177 * Creates a new instance with the specified initial {@link ChannelFactory}. 178 */ 179 public ServerBootstrap(ChannelFactory channelFactory) { 180 super(channelFactory); 181 } 182 183 /** 184 * {@inheritDoc} 185 * 186 * @throws IllegalArgumentException 187 * if the specified {@code factory} is not a 188 * {@link ServerChannelFactory} 189 */ 190 @Override 191 public void setFactory(ChannelFactory factory) { 192 if (factory == null) { 193 throw new NullPointerException("factory"); 194 } 195 if (!(factory instanceof ServerChannelFactory)) { 196 throw new IllegalArgumentException( 197 "factory must be a " + 198 ServerChannelFactory.class.getSimpleName() + ": " + 199 factory.getClass()); 200 } 201 super.setFactory(factory); 202 } 203 204 /** 205 * Returns an optional {@link ChannelHandler} which intercepts an event 206 * of a newly bound server-side channel which accepts incoming connections. 207 * 208 * @return the parent channel handler. 209 * {@code null} if no parent channel handler is set. 210 */ 211 public ChannelHandler getParentHandler() { 212 return parentHandler; 213 } 214 215 /** 216 * Sets an optional {@link ChannelHandler} which intercepts an event of 217 * a newly bound server-side channel which accepts incoming connections. 218 * 219 * @param parentHandler 220 * the parent channel handler. 221 * {@code null} to unset the current parent channel handler. 222 */ 223 public void setParentHandler(ChannelHandler parentHandler) { 224 this.parentHandler = parentHandler; 225 } 226 227 /** 228 * Creates a new channel which is bound to the local address which was 229 * specified in the current {@code "localAddress"} option. This method is 230 * similar to the following code: 231 * 232 * <pre> 233 * {@link ServerBootstrap} b = ...; 234 * b.bind(b.getOption("localAddress")); 235 * </pre> 236 * 237 * @return a new bound channel which accepts incoming connections 238 * 239 * @throws IllegalStateException 240 * if {@code "localAddress"} option was not set 241 * @throws ClassCastException 242 * if {@code "localAddress"} option's value is 243 * neither a {@link SocketAddress} nor {@code null} 244 * @throws ChannelException 245 * if failed to create a new channel and 246 * bind it to the local address 247 */ 248 public Channel bind() { 249 SocketAddress localAddress = (SocketAddress) getOption("localAddress"); 250 if (localAddress == null) { 251 throw new IllegalStateException("localAddress option is not set."); 252 } 253 return bind(localAddress); 254 } 255 256 /** 257 * Creates a new channel which is bound to the specified local address. 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 if (localAddress == null) { 267 throw new NullPointerException("localAddress"); 268 } 269 270 final BlockingQueue<ChannelFuture> futureQueue = 271 new LinkedBlockingQueue<ChannelFuture>(); 272 273 ChannelHandler binder = new Binder(localAddress, futureQueue); 274 ChannelHandler parentHandler = getParentHandler(); 275 276 ChannelPipeline bossPipeline = pipeline(); 277 bossPipeline.addLast("binder", binder); 278 if (parentHandler != null) { 279 bossPipeline.addLast("userHandler", parentHandler); 280 } 281 282 Channel channel = getFactory().newChannel(bossPipeline); 283 284 // Wait until the future is available. 285 ChannelFuture future = null; 286 boolean interrupted = false; 287 do { 288 try { 289 future = futureQueue.poll(Integer.MAX_VALUE, TimeUnit.SECONDS); 290 } catch (InterruptedException e) { 291 interrupted = true; 292 } 293 } while (future == null); 294 295 if (interrupted) { 296 Thread.currentThread().interrupt(); 297 } 298 299 // Wait for the future. 300 future.awaitUninterruptibly(); 301 if (!future.isSuccess()) { 302 future.getChannel().close().awaitUninterruptibly(); 303 throw new ChannelException("Failed to bind to: " + localAddress, future.getCause()); 304 } 305 306 return channel; 307 } 308 309 private final class Binder extends SimpleChannelUpstreamHandler { 310 311 private final SocketAddress localAddress; 312 private final BlockingQueue<ChannelFuture> futureQueue; 313 private final Map<String, Object> childOptions = 314 new HashMap<String, Object>(); 315 316 Binder(SocketAddress localAddress, BlockingQueue<ChannelFuture> futureQueue) { 317 this.localAddress = localAddress; 318 this.futureQueue = futureQueue; 319 } 320 321 @Override 322 public void channelOpen( 323 ChannelHandlerContext ctx, 324 ChannelStateEvent evt) { 325 326 try { 327 evt.getChannel().getConfig().setPipelineFactory(getPipelineFactory()); 328 329 // Split options into two categories: parent and child. 330 Map<String, Object> allOptions = getOptions(); 331 Map<String, Object> parentOptions = new HashMap<String, Object>(); 332 for (Entry<String, Object> e: allOptions.entrySet()) { 333 if (e.getKey().startsWith("child.")) { 334 childOptions.put( 335 e.getKey().substring(6), 336 e.getValue()); 337 } else if (!e.getKey().equals("pipelineFactory")) { 338 parentOptions.put(e.getKey(), e.getValue()); 339 } 340 } 341 342 // Apply parent options. 343 evt.getChannel().getConfig().setOptions(parentOptions); 344 } finally { 345 ctx.sendUpstream(evt); 346 } 347 348 boolean finished = futureQueue.offer(evt.getChannel().bind(localAddress)); 349 assert finished; 350 } 351 352 @Override 353 public void childChannelOpen( 354 ChannelHandlerContext ctx, 355 ChildChannelStateEvent e) throws Exception { 356 // Apply child options. 357 e.getChildChannel().getConfig().setOptions(childOptions); 358 ctx.sendUpstream(e); 359 } 360 361 @Override 362 public void exceptionCaught( 363 ChannelHandlerContext ctx, ExceptionEvent e) 364 throws Exception { 365 boolean finished = futureQueue.offer(failedFuture(e.getChannel(), e.getCause())); 366 assert finished; 367 ctx.sendUpstream(e); 368 } 369 } 370 }