View Javadoc
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 io.netty.bootstrap;
17  
18  import io.netty.channel.Channel;
19  import io.netty.channel.ChannelFuture;
20  import io.netty.channel.ChannelFutureListener;
21  import io.netty.channel.ChannelOption;
22  import io.netty.channel.ChannelPipeline;
23  import io.netty.channel.ChannelPromise;
24  import io.netty.channel.EventLoop;
25  import io.netty.channel.EventLoopGroup;
26  import io.netty.resolver.AddressResolver;
27  import io.netty.resolver.DefaultAddressResolverGroup;
28  import io.netty.resolver.NameResolver;
29  import io.netty.resolver.AddressResolverGroup;
30  import io.netty.util.AttributeKey;
31  import io.netty.util.concurrent.Future;
32  import io.netty.util.concurrent.FutureListener;
33  import io.netty.util.internal.logging.InternalLogger;
34  import io.netty.util.internal.logging.InternalLoggerFactory;
35  
36  import java.net.InetAddress;
37  import java.net.InetSocketAddress;
38  import java.net.SocketAddress;
39  import java.util.Map;
40  import java.util.Map.Entry;
41  
42  /**
43   * A {@link Bootstrap} that makes it easy to bootstrap a {@link Channel} to use
44   * for clients.
45   *
46   * <p>The {@link #bind()} methods are useful in combination with connectionless transports such as datagram (UDP).
47   * For regular TCP connections, please use the provided {@link #connect()} methods.</p>
48   */
49  public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
50  
51      private static final InternalLogger logger = InternalLoggerFactory.getInstance(Bootstrap.class);
52  
53      private static final AddressResolverGroup<?> DEFAULT_RESOLVER = DefaultAddressResolverGroup.INSTANCE;
54  
55      private final BootstrapConfig config = new BootstrapConfig(this);
56  
57      @SuppressWarnings("unchecked")
58      private volatile AddressResolverGroup<SocketAddress> resolver =
59              (AddressResolverGroup<SocketAddress>) DEFAULT_RESOLVER;
60      private volatile SocketAddress remoteAddress;
61  
62      public Bootstrap() { }
63  
64      private Bootstrap(Bootstrap bootstrap) {
65          super(bootstrap);
66          resolver = bootstrap.resolver;
67          remoteAddress = bootstrap.remoteAddress;
68      }
69  
70      /**
71       * Sets the {@link NameResolver} which will resolve the address of the unresolved named address.
72       *
73       * @param resolver the {@link NameResolver} for this {@code Bootstrap}; may be {@code null}, in which case a default
74       *                 resolver will be used
75       *
76       * @see io.netty.resolver.DefaultAddressResolverGroup
77       */
78      @SuppressWarnings("unchecked")
79      public Bootstrap resolver(AddressResolverGroup<?> resolver) {
80          this.resolver = (AddressResolverGroup<SocketAddress>) (resolver == null ? DEFAULT_RESOLVER : resolver);
81          return this;
82      }
83  
84      /**
85       * The {@link SocketAddress} to connect to once the {@link #connect()} method
86       * is called.
87       */
88      public Bootstrap remoteAddress(SocketAddress remoteAddress) {
89          this.remoteAddress = remoteAddress;
90          return this;
91      }
92  
93      /**
94       * @see #remoteAddress(SocketAddress)
95       */
96      public Bootstrap remoteAddress(String inetHost, int inetPort) {
97          remoteAddress = InetSocketAddress.createUnresolved(inetHost, inetPort);
98          return this;
99      }
100 
101     /**
102      * @see #remoteAddress(SocketAddress)
103      */
104     public Bootstrap remoteAddress(InetAddress inetHost, int inetPort) {
105         remoteAddress = new InetSocketAddress(inetHost, inetPort);
106         return this;
107     }
108 
109     /**
110      * Connect a {@link Channel} to the remote peer.
111      */
112     public ChannelFuture connect() {
113         validate();
114         SocketAddress remoteAddress = this.remoteAddress;
115         if (remoteAddress == null) {
116             throw new IllegalStateException("remoteAddress not set");
117         }
118 
119         return doResolveAndConnect(remoteAddress, config.localAddress());
120     }
121 
122     /**
123      * Connect a {@link Channel} to the remote peer.
124      */
125     public ChannelFuture connect(String inetHost, int inetPort) {
126         return connect(InetSocketAddress.createUnresolved(inetHost, inetPort));
127     }
128 
129     /**
130      * Connect a {@link Channel} to the remote peer.
131      */
132     public ChannelFuture connect(InetAddress inetHost, int inetPort) {
133         return connect(new InetSocketAddress(inetHost, inetPort));
134     }
135 
136     /**
137      * Connect a {@link Channel} to the remote peer.
138      */
139     public ChannelFuture connect(SocketAddress remoteAddress) {
140         if (remoteAddress == null) {
141             throw new NullPointerException("remoteAddress");
142         }
143 
144         validate();
145         return doResolveAndConnect(remoteAddress, config.localAddress());
146     }
147 
148     /**
149      * Connect a {@link Channel} to the remote peer.
150      */
151     public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
152         if (remoteAddress == null) {
153             throw new NullPointerException("remoteAddress");
154         }
155         validate();
156         return doResolveAndConnect(remoteAddress, localAddress);
157     }
158 
159     /**
160      * @see #connect()
161      */
162     private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
163         final ChannelFuture regFuture = initAndRegister();
164         final Channel channel = regFuture.channel();
165 
166         if (regFuture.isDone()) {
167             if (!regFuture.isSuccess()) {
168                 return regFuture;
169             }
170             return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
171         } else {
172             // Registration future is almost always fulfilled already, but just in case it's not.
173             final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
174             regFuture.addListener(new ChannelFutureListener() {
175                 @Override
176                 public void operationComplete(ChannelFuture future) throws Exception {
177                     // Directly obtain the cause and do a null check so we only need one volatile read in case of a
178                     // failure.
179                     Throwable cause = future.cause();
180                     if (cause != null) {
181                         // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
182                         // IllegalStateException once we try to access the EventLoop of the Channel.
183                         promise.setFailure(cause);
184                     } else {
185                         // Registration was successful, so set the correct executor to use.
186                         // See https://github.com/netty/netty/issues/2586
187                         promise.registered();
188                         doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
189                     }
190                 }
191             });
192             return promise;
193         }
194     }
195 
196     private ChannelFuture doResolveAndConnect0(final Channel channel, SocketAddress remoteAddress,
197                                                final SocketAddress localAddress, final ChannelPromise promise) {
198         try {
199             final EventLoop eventLoop = channel.eventLoop();
200             final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);
201 
202             if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
203                 // Resolver has no idea about what to do with the specified remote address or it's resolved already.
204                 doConnect(remoteAddress, localAddress, promise);
205                 return promise;
206             }
207 
208             final Future<SocketAddress> resolveFuture = resolver.resolve(remoteAddress);
209 
210             if (resolveFuture.isDone()) {
211                 final Throwable resolveFailureCause = resolveFuture.cause();
212 
213                 if (resolveFailureCause != null) {
214                     // Failed to resolve immediately
215                     channel.close();
216                     promise.setFailure(resolveFailureCause);
217                 } else {
218                     // Succeeded to resolve immediately; cached? (or did a blocking lookup)
219                     doConnect(resolveFuture.getNow(), localAddress, promise);
220                 }
221                 return promise;
222             }
223 
224             // Wait until the name resolution is finished.
225             resolveFuture.addListener(new FutureListener<SocketAddress>() {
226                 @Override
227                 public void operationComplete(Future<SocketAddress> future) throws Exception {
228                     if (future.cause() != null) {
229                         channel.close();
230                         promise.setFailure(future.cause());
231                     } else {
232                         doConnect(future.getNow(), localAddress, promise);
233                     }
234                 }
235             });
236         } catch (Throwable cause) {
237             promise.tryFailure(cause);
238         }
239         return promise;
240     }
241 
242     private static void doConnect(
243             final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
244 
245         // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
246         // the pipeline in its channelRegistered() implementation.
247         final Channel channel = connectPromise.channel();
248         channel.eventLoop().execute(new Runnable() {
249             @Override
250             public void run() {
251                 if (localAddress == null) {
252                     channel.connect(remoteAddress, connectPromise);
253                 } else {
254                     channel.connect(remoteAddress, localAddress, connectPromise);
255                 }
256                 connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
257             }
258         });
259     }
260 
261     @Override
262     @SuppressWarnings("unchecked")
263     void init(Channel channel) throws Exception {
264         ChannelPipeline p = channel.pipeline();
265         p.addLast(config.handler());
266 
267         final Map<ChannelOption<?>, Object> options = options0();
268         synchronized (options) {
269             setChannelOptions(channel, options, logger);
270         }
271 
272         final Map<AttributeKey<?>, Object> attrs = attrs0();
273         synchronized (attrs) {
274             for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
275                 channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
276             }
277         }
278     }
279 
280     @Override
281     public Bootstrap validate() {
282         super.validate();
283         if (config.handler() == null) {
284             throw new IllegalStateException("handler not set");
285         }
286         return this;
287     }
288 
289     @Override
290     @SuppressWarnings("CloneDoesntCallSuperClone")
291     public Bootstrap clone() {
292         return new Bootstrap(this);
293     }
294 
295     /**
296      * Returns a deep clone of this bootstrap which has the identical configuration except that it uses
297      * the given {@link EventLoopGroup}. This method is useful when making multiple {@link Channel}s with similar
298      * settings.
299      */
300     public Bootstrap clone(EventLoopGroup group) {
301         Bootstrap bs = new Bootstrap(this);
302         bs.group = group;
303         return bs;
304     }
305 
306     @Override
307     public final BootstrapConfig config() {
308         return config;
309     }
310 
311     final SocketAddress remoteAddress() {
312         return remoteAddress;
313     }
314 
315     final AddressResolverGroup<?> resolver() {
316         return resolver;
317     }
318 }