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 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 * @apiviz.landmark 116 */ 117 public class ConnectionlessBootstrap extends Bootstrap { 118 119 /** 120 * Creates a new instance with no {@link ChannelFactory} set. 121 * {@link #setFactory(ChannelFactory)} must be called before any I/O 122 * operation is requested. 123 */ 124 public ConnectionlessBootstrap() { 125 super(); 126 } 127 128 /** 129 * Creates a new instance with the specified initial {@link ChannelFactory}. 130 */ 131 public ConnectionlessBootstrap(ChannelFactory channelFactory) { 132 super(channelFactory); 133 } 134 135 /** 136 * Creates a new channel which is bound to the local address which was 137 * specified in the current {@code "localAddress"} option. This method is 138 * similar to the following code: 139 * 140 * <pre> 141 * {@link ConnectionlessBootstrap} b = ...; 142 * b.bind(b.getOption("localAddress")); 143 * </pre> 144 * 145 * @return a new bound channel which accepts incoming connections 146 * 147 * @throws IllegalStateException 148 * if {@code "localAddress"} option was not set 149 * @throws ClassCastException 150 * if {@code "localAddress"} option's value is 151 * neither a {@link SocketAddress} nor {@code null} 152 * @throws ChannelException 153 * if failed to create a new channel and 154 * bind it to the local address 155 */ 156 public Channel bind() { 157 SocketAddress localAddress = (SocketAddress) getOption("localAddress"); 158 if (localAddress == null) { 159 throw new IllegalStateException("localAddress option is not set."); 160 } 161 return bind(localAddress); 162 } 163 164 /** 165 * Creates a new channel which is bound to the specified local address. 166 * 167 * @return a new bound channel which accepts incoming connections 168 * 169 * @throws ChannelException 170 * if failed to create a new channel and 171 * bind it to the local address 172 */ 173 public Channel bind(final SocketAddress localAddress) { 174 if (localAddress == null) { 175 throw new NullPointerException("localAddress"); 176 } 177 178 ChannelPipeline pipeline; 179 try { 180 pipeline = getPipelineFactory().getPipeline(); 181 } catch (Exception e) { 182 throw new ChannelPipelineException("Failed to initialize a pipeline.", e); 183 } 184 185 Channel ch = getFactory().newChannel(pipeline); 186 187 // Apply options. 188 boolean success = false; 189 try { 190 ch.getConfig().setOptions(getOptions()); 191 success = true; 192 } finally { 193 if (!success) { 194 ch.close(); 195 } 196 } 197 198 // Bind 199 ChannelFuture future = ch.bind(localAddress); 200 201 // Wait for the future. 202 future.awaitUninterruptibly(); 203 if (!future.isSuccess()) { 204 future.getChannel().close().awaitUninterruptibly(); 205 throw new ChannelException("Failed to bind to: " + localAddress, future.getCause()); 206 } 207 208 return ch; 209 } 210 211 /** 212 * Creates a new connected channel with the current {@code "remoteAddress"} 213 * and {@code "localAddress"} option. If the {@code "localAddress"} option 214 * is not set, the local address of a new channel is determined 215 * automatically. This method is similar to the following code: 216 * 217 * <pre> 218 * {@link ConnectionlessBootstrap} b = ...; 219 * b.connect(b.getOption("remoteAddress"), b.getOption("localAddress")); 220 * </pre> 221 * 222 * @return a future object which notifies when the creation of the connected 223 * channel succeeds or fails 224 * 225 * @throws IllegalStateException 226 * if {@code "remoteAddress"} option was not set 227 * @throws ClassCastException 228 * if {@code "remoteAddress"} or {@code "localAddress"} option's 229 * value is neither a {@link SocketAddress} nor {@code null} 230 * @throws ChannelPipelineException 231 * if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory} 232 * failed to create a new {@link ChannelPipeline} 233 */ 234 public ChannelFuture connect() { 235 SocketAddress remoteAddress = (SocketAddress) getOption("remoteAddress"); 236 if (remoteAddress == null) { 237 throw new IllegalStateException("remoteAddress option is not set."); 238 } 239 return connect(remoteAddress); 240 } 241 242 /** 243 * Creates a new connected channel with the specified 244 * {@code "remoteAddress"} and the current {@code "localAddress"} option. 245 * If the {@code "localAddress"} option is not set, the local address of 246 * a new channel is determined automatically. This method is identical 247 * with the following code: 248 * 249 * <pre> 250 * {@link ConnectionlessBootstrap} b = ...; 251 * b.connect(remoteAddress, b.getOption("localAddress")); 252 * </pre> 253 * 254 * @return a future object which notifies when the creation of the connected 255 * channel succeeds or fails 256 * 257 * @throws ClassCastException 258 * if {@code "localAddress"} option's value is 259 * neither a {@link SocketAddress} nor {@code null} 260 * @throws ChannelPipelineException 261 * if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory} 262 * failed to create a new {@link ChannelPipeline} 263 */ 264 public ChannelFuture connect(SocketAddress remoteAddress) { 265 if (remoteAddress == null) { 266 throw new NullPointerException("remotedAddress"); 267 } 268 SocketAddress localAddress = (SocketAddress) getOption("localAddress"); 269 return connect(remoteAddress, localAddress); 270 } 271 272 /** 273 * Creates a new connected channel with the specified 274 * {@code "remoteAddress"} and the specified {@code "localAddress"}. 275 * If the specified local address is {@code null}, the local address of a 276 * new channel is determined automatically. 277 * 278 * @return a future object which notifies when the creation of the connected 279 * channel succeeds or fails 280 * 281 * @throws ChannelPipelineException 282 * if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory} 283 * failed to create a new {@link ChannelPipeline} 284 */ 285 public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress) { 286 287 if (remoteAddress == null) { 288 throw new NullPointerException("remoteAddress"); 289 } 290 291 ChannelPipeline pipeline; 292 try { 293 pipeline = getPipelineFactory().getPipeline(); 294 } catch (Exception e) { 295 throw new ChannelPipelineException("Failed to initialize a pipeline.", e); 296 } 297 298 // Set the options. 299 Channel ch = getFactory().newChannel(pipeline); 300 boolean success = false; 301 try { 302 ch.getConfig().setOptions(getOptions()); 303 success = true; 304 } finally { 305 if (!success) { 306 ch.close(); 307 } 308 } 309 310 // Bind. 311 if (localAddress != null) { 312 ch.bind(localAddress); 313 } 314 315 // Connect. 316 return ch.connect(remoteAddress); 317 } 318 }