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.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     @SuppressWarnings("unchecked")
261     void init(Channel channel) {
262         ChannelPipeline p = channel.pipeline();
263         p.addLast(config.handler());
264
265         setChannelOptions(channel, newOptionsArray(), logger);
266         setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
267     }
268
269     @Override
270     public Bootstrap validate() {
271         super.validate();
272         if (config.handler() == null) {
273             throw new IllegalStateException("handler not set");
274         }
275         return this;
276     }
277
278     @Override
279     @SuppressWarnings("CloneDoesntCallSuperClone")
280     public Bootstrap clone() {
281         return new Bootstrap(this);
282     }
283
284     /**
285      * Returns a deep clone of this bootstrap which has the identical configuration except that it uses
286      * the given {@link EventLoopGroup}. This method is useful when making multiple {@link Channel}s with similar
287      * settings.
288      */

289     public Bootstrap clone(EventLoopGroup group) {
290         Bootstrap bs = new Bootstrap(this);
291         bs.group = group;
292         return bs;
293     }
294
295     @Override
296     public final BootstrapConfig config() {
297         return config;
298     }
299
300     final SocketAddress remoteAddress() {
301         return remoteAddress;
302     }
303
304     final AddressResolverGroup<?> resolver() {
305         return resolver;
306     }
307 }
308