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