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 java.net.InetSocketAddress; 19 import java.net.SocketAddress; 20 21 import org.jboss.netty.channel.Channel; 22 import org.jboss.netty.channel.ChannelConfig; 23 import org.jboss.netty.channel.ChannelException; 24 import org.jboss.netty.channel.ChannelFactory; 25 import org.jboss.netty.channel.ChannelFuture; 26 import org.jboss.netty.channel.ChannelHandler; 27 import org.jboss.netty.channel.ChannelPipeline; 28 import org.jboss.netty.channel.ChannelPipelineException; 29 import org.jboss.netty.channel.ChannelPipelineFactory; 30 import org.jboss.netty.channel.Channels; 31 32 /** 33 * A helper class which creates a new server-side {@link Channel} for a 34 * connectionless transport. 35 * 36 * <h3>Only for connectionless transports</h3> 37 * 38 * This bootstrap is for connectionless transports only such as UDP/IP. 39 * Use {@link ServerBootstrap} instead for connection oriented transports. 40 * Do not use this helper if you are using a connection oriented transport such 41 * as TCP/IP and local transport which accepts an incoming connection and lets 42 * the accepted child channels handle received messages. 43 * 44 * <h3>Configuring channels</h3> 45 * 46 * {@link #setOption(String, Object) Options} are used to configure a channel: 47 * 48 * <pre> 49 * {@link ConnectionlessBootstrap} b = ...; 50 * 51 * // Options for a new channel 52 * b.setOption("localAddress", new {@link InetSocketAddress}(8080)); 53 * b.setOption("tcpNoDelay", true); 54 * b.setOption("receiveBufferSize", 1048576); 55 * </pre> 56 * 57 * For the detailed list of available options, please refer to 58 * {@link ChannelConfig} and its sub-types. 59 * 60 * <h3>Configuring a channel pipeline</h3> 61 * 62 * Every channel has its own {@link ChannelPipeline} and you can configure it 63 * in two ways. 64 * 65 * The recommended approach is to specify a {@link ChannelPipelineFactory} by 66 * calling {@link #setPipelineFactory(ChannelPipelineFactory)}. 67 * 68 * <pre> 69 * {@link ConnectionlessBootstrap} b = ...; 70 * b.setPipelineFactory(new MyPipelineFactory()); 71 * 72 * public class MyPipelineFactory implements {@link ChannelPipelineFactory} { 73 * public {@link ChannelPipeline} getPipeline() throws Exception { 74 * // Create and configure a new pipeline for a new channel. 75 * {@link ChannelPipeline} p = {@link Channels}.pipeline(); 76 * p.addLast("encoder", new EncodingHandler()); 77 * p.addLast("decoder", new DecodingHandler()); 78 * p.addLast("logic", new LogicHandler()); 79 * return p; 80 * } 81 * } 82 * </pre> 83 84 * <p> 85 * The alternative approach, which works only in a certain situation, is to use 86 * the default pipeline and let the bootstrap to shallow-copy the default 87 * pipeline for each new channel: 88 * 89 * <pre> 90 * {@link ConnectionlessBootstrap} b = ...; 91 * {@link ChannelPipeline} p = b.getPipeline(); 92 * 93 * // Add handlers to the default pipeline. 94 * p.addLast("encoder", new EncodingHandler()); 95 * p.addLast("decoder", new DecodingHandler()); 96 * p.addLast("logic", new LogicHandler()); 97 * </pre> 98 * 99 * Please note 'shallow-copy' here means that the added {@link ChannelHandler}s 100 * are not cloned but only their references are added to the new pipeline. 101 * Therefore, you cannot use this approach if you are going to open more than 102 * one {@link Channel}s or run a server that accepts incoming connections to 103 * create its child channels. 104 * 105 * <h3>Applying different settings for different {@link Channel}s</h3> 106 * 107 * {@link ConnectionlessBootstrap} is just a helper class. It neither 108 * allocates nor manages any resources. What manages the resources is the 109 * {@link ChannelFactory} implementation you specified in the constructor of 110 * {@link ConnectionlessBootstrap}. Therefore, it is OK to create as 111 * many {@link ConnectionlessBootstrap} instances as you want with the same 112 * {@link ChannelFactory} to apply different settings for different 113 * {@link Channel}s. 114 * 115 * @author <a href="http://www.jboss.org/netty/">The Netty Project</a> 116 * @author <a href="http://gleamynode.net/">Trustin Lee</a> 117 * 118 * @version $Rev: 2338 $, $Date: 2010-07-07 13:33:47 +0900 (Wed, 07 Jul 2010) $ 119 * 120 * @apiviz.landmark 121 */ 122 public class ConnectionlessBootstrap extends Bootstrap { 123 124 /** 125 * Creates a new instance with no {@link ChannelFactory} set. 126 * {@link #setFactory(ChannelFactory)} must be called before any I/O 127 * operation is requested. 128 */ 129 public ConnectionlessBootstrap() { 130 super(); 131 } 132 133 /** 134 * Creates a new instance with the specified initial {@link ChannelFactory}. 135 */ 136 public ConnectionlessBootstrap(ChannelFactory channelFactory) { 137 super(channelFactory); 138 } 139 140 /** 141 * Creates a new channel which is bound to the local address which was 142 * specified in the current {@code "localAddress"} option. This method is 143 * similar to the following code: 144 * 145 * <pre> 146 * {@link ConnectionlessBootstrap} b = ...; 147 * b.bind(b.getOption("localAddress")); 148 * </pre> 149 * 150 * @return a new bound channel which accepts incoming connections 151 * 152 * @throws IllegalStateException 153 * if {@code "localAddress"} option was not set 154 * @throws ClassCastException 155 * if {@code "localAddress"} option's value is 156 * neither a {@link SocketAddress} nor {@code null} 157 * @throws ChannelException 158 * if failed to create a new channel and 159 * bind it to the local address 160 */ 161 public Channel bind() { 162 SocketAddress localAddress = (SocketAddress) getOption("localAddress"); 163 if (localAddress == null) { 164 throw new IllegalStateException("localAddress option is not set."); 165 } 166 return bind(localAddress); 167 } 168 169 /** 170 * Creates a new channel which is bound to the specified local address. 171 * 172 * @return a new bound channel which accepts incoming connections 173 * 174 * @throws ChannelException 175 * if failed to create a new channel and 176 * bind it to the local address 177 */ 178 public Channel bind(final SocketAddress localAddress) { 179 if (localAddress == null) { 180 throw new NullPointerException("localAddress"); 181 } 182 183 ChannelPipeline pipeline; 184 try { 185 pipeline = getPipelineFactory().getPipeline(); 186 } catch (Exception e) { 187 throw new ChannelPipelineException("Failed to initialize a pipeline.", e); 188 } 189 190 Channel ch = getFactory().newChannel(pipeline); 191 192 // Apply options. 193 ch.getConfig().setPipelineFactory(getPipelineFactory()); 194 ch.getConfig().setOptions(getOptions()); 195 196 // Bind 197 ChannelFuture future = ch.bind(localAddress); 198 199 // Wait for the future. 200 future.awaitUninterruptibly(); 201 if (!future.isSuccess()) { 202 future.getChannel().close().awaitUninterruptibly(); 203 throw new ChannelException("Failed to bind to: " + localAddress, future.getCause()); 204 } 205 206 return ch; 207 } 208 209 /** 210 * Creates a new connected channel with the current {@code "remoteAddress"} 211 * and {@code "localAddress"} option. If the {@code "localAddress"} option 212 * is not set, the local address of a new channel is determined 213 * automatically. This method is similar to the following code: 214 * 215 * <pre> 216 * {@link ConnectionlessBootstrap} b = ...; 217 * b.connect(b.getOption("remoteAddress"), b.getOption("localAddress")); 218 * </pre> 219 * 220 * @return a future object which notifies when the creation of the connected 221 * channel succeeds or fails 222 * 223 * @throws IllegalStateException 224 * if {@code "remoteAddress"} option was not set 225 * @throws ClassCastException 226 * if {@code "remoteAddress"} or {@code "localAddress"} option's 227 * value is neither a {@link SocketAddress} nor {@code null} 228 * @throws ChannelPipelineException 229 * if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory} 230 * failed to create a new {@link ChannelPipeline} 231 */ 232 public ChannelFuture connect() { 233 SocketAddress remoteAddress = (SocketAddress) getOption("remoteAddress"); 234 if (remoteAddress == null) { 235 throw new IllegalStateException("remoteAddress option is not set."); 236 } 237 return connect(remoteAddress); 238 } 239 240 /** 241 * Creates a new connected channel with the specified 242 * {@code "remoteAddress"} and the current {@code "localAddress"} option. 243 * If the {@code "localAddress"} option is not set, the local address of 244 * a new channel is determined automatically. This method is identical 245 * with the following code: 246 * 247 * <pre> 248 * {@link ConnectionlessBootstrap} b = ...; 249 * b.connect(remoteAddress, b.getOption("localAddress")); 250 * </pre> 251 * 252 * @return a future object which notifies when the creation of the connected 253 * channel succeeds or fails 254 * 255 * @throws ClassCastException 256 * if {@code "localAddress"} option's value is 257 * neither a {@link SocketAddress} nor {@code null} 258 * @throws ChannelPipelineException 259 * if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory} 260 * failed to create a new {@link ChannelPipeline} 261 */ 262 public ChannelFuture connect(SocketAddress remoteAddress) { 263 if (remoteAddress == null) { 264 throw new NullPointerException("remotedAddress"); 265 } 266 SocketAddress localAddress = (SocketAddress) getOption("localAddress"); 267 return connect(remoteAddress, localAddress); 268 } 269 270 /** 271 * Creates a new connected channel with the specified 272 * {@code "remoteAddress"} and the specified {@code "localAddress"}. 273 * If the specified local address is {@code null}, the local address of a 274 * new channel is determined automatically. 275 * 276 * @return a future object which notifies when the creation of the connected 277 * channel succeeds or fails 278 * 279 * @throws ChannelPipelineException 280 * if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory} 281 * failed to create a new {@link ChannelPipeline} 282 */ 283 public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress) { 284 285 if (remoteAddress == null) { 286 throw new NullPointerException("remoteAddress"); 287 } 288 289 ChannelPipeline pipeline; 290 try { 291 pipeline = getPipelineFactory().getPipeline(); 292 } catch (Exception e) { 293 throw new ChannelPipelineException("Failed to initialize a pipeline.", e); 294 } 295 296 // Set the options. 297 Channel ch = getFactory().newChannel(pipeline); 298 ch.getConfig().setOptions(getOptions()); 299 300 // Bind. 301 if (localAddress != null) { 302 ch.bind(localAddress); 303 } 304 305 // Connect. 306 return ch.connect(remoteAddress); 307 } 308 }