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.EventLoopGroup;
25  import io.netty.util.AttributeKey;
26  import io.netty.util.internal.logging.InternalLogger;
27  import io.netty.util.internal.logging.InternalLoggerFactory;
28  
29  import java.net.InetAddress;
30  import java.net.InetSocketAddress;
31  import java.net.SocketAddress;
32  import java.util.Map;
33  import java.util.Map.Entry;
34  
35  /**
36   * A {@link Bootstrap} that makes it easy to bootstrap a {@link Channel} to use
37   * for clients.
38   *
39   * <p>The {@link #bind()} methods are useful in combination with connectionless transports such as datagram (UDP).
40   * For regular TCP connections, please use the provided {@link #connect()} methods.</p>
41   */
42  public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
43  
44      private static final InternalLogger logger = InternalLoggerFactory.getInstance(Bootstrap.class);
45  
46      private volatile SocketAddress remoteAddress;
47  
48      public Bootstrap() { }
49  
50      private Bootstrap(Bootstrap bootstrap) {
51          super(bootstrap);
52          remoteAddress = bootstrap.remoteAddress;
53      }
54  
55      /**
56       * The {@link SocketAddress} to connect to once the {@link #connect()} method
57       * is called.
58       */
59      public Bootstrap remoteAddress(SocketAddress remoteAddress) {
60          this.remoteAddress = remoteAddress;
61          return this;
62      }
63  
64      /**
65       * @see #remoteAddress(SocketAddress)
66       */
67      public Bootstrap remoteAddress(String inetHost, int inetPort) {
68          remoteAddress = new InetSocketAddress(inetHost, inetPort);
69          return this;
70      }
71  
72      /**
73       * @see #remoteAddress(SocketAddress)
74       */
75      public Bootstrap remoteAddress(InetAddress inetHost, int inetPort) {
76          remoteAddress = new InetSocketAddress(inetHost, inetPort);
77          return this;
78      }
79  
80      /**
81       * Connect a {@link Channel} to the remote peer.
82       */
83      public ChannelFuture connect() {
84          validate();
85          SocketAddress remoteAddress = this.remoteAddress;
86          if (remoteAddress == null) {
87              throw new IllegalStateException("remoteAddress not set");
88          }
89  
90          return doConnect(remoteAddress, localAddress());
91      }
92  
93      /**
94       * Connect a {@link Channel} to the remote peer.
95       */
96      public ChannelFuture connect(String inetHost, int inetPort) {
97          return connect(new InetSocketAddress(inetHost, inetPort));
98      }
99  
100     /**
101      * Connect a {@link Channel} to the remote peer.
102      */
103     public ChannelFuture connect(InetAddress inetHost, int inetPort) {
104         return connect(new InetSocketAddress(inetHost, inetPort));
105     }
106 
107     /**
108      * Connect a {@link Channel} to the remote peer.
109      */
110     public ChannelFuture connect(SocketAddress remoteAddress) {
111         if (remoteAddress == null) {
112             throw new NullPointerException("remoteAddress");
113         }
114 
115         validate();
116         return doConnect(remoteAddress, localAddress());
117     }
118 
119     /**
120      * Connect a {@link Channel} to the remote peer.
121      */
122     public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
123         if (remoteAddress == null) {
124             throw new NullPointerException("remoteAddress");
125         }
126         validate();
127         return doConnect(remoteAddress, localAddress);
128     }
129 
130     /**
131      * @see #connect()
132      */
133     private ChannelFuture doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
134         final ChannelFuture regFuture = initAndRegister();
135         final Channel channel = regFuture.channel();
136         if (regFuture.cause() != null) {
137             return regFuture;
138         }
139 
140         final ChannelPromise promise = channel.newPromise();
141         if (regFuture.isDone()) {
142             doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
143         } else {
144             regFuture.addListener(new ChannelFutureListener() {
145                 @Override
146                 public void operationComplete(ChannelFuture future) throws Exception {
147                     doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
148                 }
149             });
150         }
151 
152         return promise;
153     }
154 
155     private static void doConnect0(
156             final ChannelFuture regFuture, final Channel channel,
157             final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
158 
159         // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
160         // the pipeline in its channelRegistered() implementation.
161         channel.eventLoop().execute(new Runnable() {
162             @Override
163             public void run() {
164                 if (regFuture.isSuccess()) {
165                     if (localAddress == null) {
166                         channel.connect(remoteAddress, promise);
167                     } else {
168                         channel.connect(remoteAddress, localAddress, promise);
169                     }
170                     promise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
171                 } else {
172                     promise.setFailure(regFuture.cause());
173                 }
174             }
175         });
176     }
177 
178     @Override
179     @SuppressWarnings("unchecked")
180     void init(Channel channel) throws Exception {
181         ChannelPipeline p = channel.pipeline();
182         p.addLast(handler());
183 
184         final Map<ChannelOption<?>, Object> options = options();
185         synchronized (options) {
186             setChannelOptions(channel, options, logger);
187         }
188 
189         final Map<AttributeKey<?>, Object> attrs = attrs();
190         synchronized (attrs) {
191             for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
192                 channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
193             }
194         }
195     }
196 
197     @Override
198     public Bootstrap validate() {
199         super.validate();
200         if (handler() == null) {
201             throw new IllegalStateException("handler not set");
202         }
203         return this;
204     }
205 
206     @Override
207     @SuppressWarnings("CloneDoesntCallSuperClone")
208     public Bootstrap clone() {
209         return new Bootstrap(this);
210     }
211 
212     /**
213      * Returns a deep clone of this bootstrap which has the identical configuration except that it uses
214      * the given {@link EventLoopGroup}. This method is useful when making multiple {@link Channel}s with similar
215      * settings.
216      */
217     public Bootstrap clone(EventLoopGroup group) {
218         Bootstrap bs = new Bootstrap(this);
219         bs.group = group;
220         return bs;
221     }
222 
223     @Override
224     public String toString() {
225         if (remoteAddress == null) {
226             return super.toString();
227         }
228 
229         StringBuilder buf = new StringBuilder(super.toString());
230         buf.setLength(buf.length() - 1);
231 
232         return buf.append(", remoteAddress: ")
233                   .append(remoteAddress)
234                   .append(')')
235                   .toString();
236     }
237 }