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.ChannelFactory; 24 import org.jboss.netty.channel.ChannelFuture; 25 import org.jboss.netty.channel.ChannelHandler; 26 import org.jboss.netty.channel.ChannelPipeline; 27 import org.jboss.netty.channel.ChannelPipelineException; 28 import org.jboss.netty.channel.ChannelPipelineFactory; 29 import org.jboss.netty.channel.Channels; 30 31 /** 32 * A helper class which creates a new client-side {@link Channel} and makes a 33 * connection attempt. 34 * 35 * <h3>Configuring a channel</h3> 36 * 37 * {@link #setOption(String, Object) Options} are used to configure a channel: 38 * 39 * <pre> 40 * {@link ClientBootstrap} b = ...; 41 * 42 * // Options for a new channel 43 * b.setOption("remoteAddress", new {@link InetSocketAddress}("example.com", 8080)); 44 * b.setOption("tcpNoDelay", true); 45 * b.setOption("receiveBufferSize", 1048576); 46 * </pre> 47 * 48 * For the detailed list of available options, please refer to 49 * {@link ChannelConfig} and its sub-types. 50 * 51 * <h3>Configuring a channel pipeline</h3> 52 * 53 * Every channel has its own {@link ChannelPipeline} and you can configure it 54 * in two ways. 55 * 56 * The recommended approach is to specify a {@link ChannelPipelineFactory} by 57 * calling {@link #setPipelineFactory(ChannelPipelineFactory)}. 58 * 59 * <pre> 60 * {@link ClientBootstrap} b = ...; 61 * b.setPipelineFactory(new MyPipelineFactory()); 62 * 63 * public class MyPipelineFactory implements {@link ChannelPipelineFactory} { 64 * public {@link ChannelPipeline} getPipeline() throws Exception { 65 * // Create and configure a new pipeline for a new channel. 66 * {@link ChannelPipeline} p = {@link Channels}.pipeline(); 67 * p.addLast("encoder", new EncodingHandler()); 68 * p.addLast("decoder", new DecodingHandler()); 69 * p.addLast("logic", new LogicHandler()); 70 * return p; 71 * } 72 * } 73 * </pre> 74 75 * <p> 76 * The alternative approach, which works only in a certain situation, is to use 77 * the default pipeline and let the bootstrap to shallow-copy the default 78 * pipeline for each new channel: 79 * 80 * <pre> 81 * {@link ClientBootstrap} b = ...; 82 * {@link ChannelPipeline} p = b.getPipeline(); 83 * 84 * // Add handlers to the default pipeline. 85 * p.addLast("encoder", new EncodingHandler()); 86 * p.addLast("decoder", new DecodingHandler()); 87 * p.addLast("logic", new LogicHandler()); 88 * </pre> 89 * 90 * Please note 'shallow-copy' here means that the added {@link ChannelHandler}s 91 * are not cloned but only their references are added to the new pipeline. 92 * Therefore, you cannot use this approach if you are going to open more than 93 * one {@link Channel}s or run a server that accepts incoming connections to 94 * create its child channels. 95 * 96 * <h3>Applying different settings for different {@link Channel}s</h3> 97 * 98 * {@link ClientBootstrap} is just a helper class. It neither allocates nor 99 * manages any resources. What manages the resources is the 100 * {@link ChannelFactory} implementation you specified in the constructor of 101 * {@link ClientBootstrap}. Therefore, it is OK to create as many 102 * {@link ClientBootstrap} instances as you want with the same 103 * {@link ChannelFactory} to apply different settings for different 104 * {@link Channel}s. 105 * 106 * @author <a href="http://www.jboss.org/netty/">The Netty Project</a> 107 * @author <a href="http://gleamynode.net/">Trustin Lee</a> 108 * 109 * @version $Rev: 2337 $, $Date: 2010-07-07 13:32:10 +0900 (Wed, 07 Jul 2010) $ 110 * 111 * @apiviz.landmark 112 */ 113 public class ClientBootstrap extends Bootstrap { 114 115 /** 116 * Creates a new instance with no {@link ChannelFactory} set. 117 * {@link #setFactory(ChannelFactory)} must be called before any I/O 118 * operation is requested. 119 */ 120 public ClientBootstrap() { 121 super(); 122 } 123 124 /** 125 * Creates a new instance with the specified initial {@link ChannelFactory}. 126 */ 127 public ClientBootstrap(ChannelFactory channelFactory) { 128 super(channelFactory); 129 } 130 131 /** 132 * Attempts a new connection with the current {@code "remoteAddress"} and 133 * {@code "localAddress"} option. If the {@code "localAddress"} option is 134 * not set, the local address of a new channel is determined automatically. 135 * This method is similar to the following code: 136 * 137 * <pre> 138 * {@link ClientBootstrap} b = ...; 139 * b.connect(b.getOption("remoteAddress"), b.getOption("localAddress")); 140 * </pre> 141 * 142 * @return a future object which notifies when this connection attempt 143 * succeeds or fails 144 * 145 * @throws IllegalStateException 146 * if {@code "remoteAddress"} option was not set 147 * @throws ClassCastException 148 * if {@code "remoteAddress"} or {@code "localAddress"} option's 149 * value is neither a {@link SocketAddress} nor {@code null} 150 * @throws ChannelPipelineException 151 * if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory} 152 * failed to create a new {@link ChannelPipeline} 153 */ 154 public ChannelFuture connect() { 155 SocketAddress remoteAddress = (SocketAddress) getOption("remoteAddress"); 156 if (remoteAddress == null) { 157 throw new IllegalStateException("remoteAddress option is not set."); 158 } 159 return connect(remoteAddress); 160 } 161 162 /** 163 * Attempts a new connection with the specified {@code remoteAddress} and 164 * the current {@code "localAddress"} option. If the {@code "localAddress"} 165 * option is not set, the local address of a new channel is determined 166 * automatically. This method is identical with the following code: 167 * 168 * <pre> 169 * {@link ClientBootstrap} b = ...; 170 * b.connect(remoteAddress, b.getOption("localAddress")); 171 * </pre> 172 * 173 * @return a future object which notifies when this connection attempt 174 * succeeds or fails 175 * 176 * @throws ClassCastException 177 * if {@code "localAddress"} option's value is 178 * neither a {@link SocketAddress} nor {@code null} 179 * @throws ChannelPipelineException 180 * if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory} 181 * failed to create a new {@link ChannelPipeline} 182 */ 183 public ChannelFuture connect(SocketAddress remoteAddress) { 184 if (remoteAddress == null) { 185 throw new NullPointerException("remoteAddress"); 186 } 187 SocketAddress localAddress = (SocketAddress) getOption("localAddress"); 188 return connect(remoteAddress, localAddress); 189 } 190 191 /** 192 * Attempts a new connection with the specified {@code remoteAddress} and 193 * the specified {@code localAddress}. If the specified local address is 194 * {@code null}, the local address of a new channel is determined 195 * automatically. 196 * 197 * @return a future object which notifies when this connection attempt 198 * succeeds or fails 199 * 200 * @throws ChannelPipelineException 201 * if this bootstrap's {@link #setPipelineFactory(ChannelPipelineFactory) pipelineFactory} 202 * failed to create a new {@link ChannelPipeline} 203 */ 204 public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress) { 205 206 if (remoteAddress == null) { 207 throw new NullPointerException("remoteAddress"); 208 } 209 210 ChannelPipeline pipeline; 211 try { 212 pipeline = getPipelineFactory().getPipeline(); 213 } catch (Exception e) { 214 throw new ChannelPipelineException("Failed to initialize a pipeline.", e); 215 } 216 217 // Set the options. 218 Channel ch = getFactory().newChannel(pipeline); 219 ch.getConfig().setOptions(getOptions()); 220 221 // Bind. 222 if (localAddress != null) { 223 ch.bind(localAddress); 224 } 225 226 // Connect. 227 return ch.connect(remoteAddress); 228 } 229 }