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