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  
17  package io.netty.bootstrap;
18  
19  import io.netty.channel.Channel;
20  import io.netty.channel.ChannelFuture;
21  import io.netty.channel.ChannelFutureListener;
22  import io.netty.channel.ChannelHandler;
23  import io.netty.channel.ChannelOption;
24  import io.netty.channel.ChannelPromise;
25  import io.netty.channel.DefaultChannelPromise;
26  import io.netty.channel.EventLoop;
27  import io.netty.channel.EventLoopGroup;
28  import io.netty.channel.ReflectiveChannelFactory;
29  import io.netty.util.AttributeKey;
30  import io.netty.util.concurrent.EventExecutor;
31  import io.netty.util.concurrent.GlobalEventExecutor;
32  import io.netty.util.internal.StringUtil;
33  
34  import java.net.InetAddress;
35  import java.net.InetSocketAddress;
36  import java.net.SocketAddress;
37  import java.util.LinkedHashMap;
38  import java.util.Map;
39  
40  /**
41   * {@link AbstractBootstrap} is a helper class that makes it easy to bootstrap a {@link Channel}. It support
42   * method-chaining to provide an easy way to configure the {@link AbstractBootstrap}.
43   *
44   * <p>When not used in a {@link ServerBootstrap} context, the {@link #bind()} methods are useful for connectionless
45   * transports such as datagram (UDP).</p>
46   */
47  public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
48  
49      private volatile EventLoopGroup group;
50      @SuppressWarnings("deprecation")
51      private volatile ChannelFactory<? extends C> channelFactory;
52      private volatile SocketAddress localAddress;
53      private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
54      private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
55      private volatile ChannelHandler handler;
56  
57      AbstractBootstrap() {
58          // Disallow extending from a different package.
59      }
60  
61      AbstractBootstrap(AbstractBootstrap<B, C> bootstrap) {
62          group = bootstrap.group;
63          channelFactory = bootstrap.channelFactory;
64          handler = bootstrap.handler;
65          localAddress = bootstrap.localAddress;
66          synchronized (bootstrap.options) {
67              options.putAll(bootstrap.options);
68          }
69          synchronized (bootstrap.attrs) {
70              attrs.putAll(bootstrap.attrs);
71          }
72      }
73  
74      /**
75       * The {@link EventLoopGroup} which is used to handle all the events for the to-be-creates
76       * {@link Channel}
77       */
78      @SuppressWarnings("unchecked")
79      public B group(EventLoopGroup group) {
80          if (group == null) {
81              throw new NullPointerException("group");
82          }
83          if (this.group != null) {
84              throw new IllegalStateException("group set already");
85          }
86          this.group = group;
87          return (B) this;
88      }
89  
90      /**
91       * The {@link Class} which is used to create {@link Channel} instances from.
92       * You either use this or {@link #channelFactory(io.netty.channel.ChannelFactory)} if your
93       * {@link Channel} implementation has no no-args constructor.
94       */
95      public B channel(Class<? extends C> channelClass) {
96          if (channelClass == null) {
97              throw new NullPointerException("channelClass");
98          }
99          return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
100     }
101 
102     /**
103      * @deprecated Use {@link #channelFactory(io.netty.channel.ChannelFactory)} instead.
104      */
105     @Deprecated
106     @SuppressWarnings("unchecked")
107     public B channelFactory(ChannelFactory<? extends C> channelFactory) {
108         if (channelFactory == null) {
109             throw new NullPointerException("channelFactory");
110         }
111         if (this.channelFactory != null) {
112             throw new IllegalStateException("channelFactory set already");
113         }
114 
115         this.channelFactory = channelFactory;
116         return (B) this;
117     }
118 
119     /**
120      * {@link io.netty.channel.ChannelFactory} which is used to create {@link Channel} instances from
121      * when calling {@link #bind()}. This method is usually only used if {@link #channel(Class)}
122      * is not working for you because of some more complex needs. If your {@link Channel} implementation
123      * has a no-args constructor, its highly recommend to just use {@link #channel(Class)} for
124      * simplify your code.
125      */
126     @SuppressWarnings({ "unchecked", "deprecation" })
127     public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
128         return channelFactory((ChannelFactory<C>) channelFactory);
129     }
130 
131     /**
132      * The {@link SocketAddress} which is used to bind the local "end" to.
133      */
134     @SuppressWarnings("unchecked")
135     public B localAddress(SocketAddress localAddress) {
136         this.localAddress = localAddress;
137         return (B) this;
138     }
139 
140     /**
141      * @see {@link #localAddress(SocketAddress)}
142      */
143     public B localAddress(int inetPort) {
144         return localAddress(new InetSocketAddress(inetPort));
145     }
146 
147     /**
148      * @see {@link #localAddress(SocketAddress)}
149      */
150     public B localAddress(String inetHost, int inetPort) {
151         return localAddress(new InetSocketAddress(inetHost, inetPort));
152     }
153 
154     /**
155      * @see {@link #localAddress(SocketAddress)}
156      */
157     public B localAddress(InetAddress inetHost, int inetPort) {
158         return localAddress(new InetSocketAddress(inetHost, inetPort));
159     }
160 
161     /**
162      * Allow to specify a {@link ChannelOption} which is used for the {@link Channel} instances once they got
163      * created. Use a value of {@code null} to remove a previous set {@link ChannelOption}.
164      */
165     @SuppressWarnings("unchecked")
166     public <T> B option(ChannelOption<T> option, T value) {
167         if (option == null) {
168             throw new NullPointerException("option");
169         }
170         if (value == null) {
171             synchronized (options) {
172                 options.remove(option);
173             }
174         } else {
175             synchronized (options) {
176                 options.put(option, value);
177             }
178         }
179         return (B) this;
180     }
181 
182     /**
183      * Allow to specify an initial attribute of the newly created {@link Channel}.  If the {@code value} is
184      * {@code null}, the attribute of the specified {@code key} is removed.
185      */
186     @SuppressWarnings("unchecked")
187     public <T> B attr(AttributeKey<T> key, T value) {
188         if (key == null) {
189             throw new NullPointerException("key");
190         }
191         if (value == null) {
192             synchronized (attrs) {
193                 attrs.remove(key);
194             }
195         } else {
196             synchronized (attrs) {
197                 attrs.put(key, value);
198             }
199         }
200         return (B) this;
201     }
202 
203     /**
204      * Validate all the parameters. Sub-classes may override this, but should
205      * call the super method in that case.
206      */
207     @SuppressWarnings("unchecked")
208     public B validate() {
209         if (group == null) {
210             throw new IllegalStateException("group not set");
211         }
212         if (channelFactory == null) {
213             throw new IllegalStateException("channel or channelFactory not set");
214         }
215         return (B) this;
216     }
217 
218     /**
219      * Returns a deep clone of this bootstrap which has the identical configuration.  This method is useful when making
220      * multiple {@link Channel}s with similar settings.  Please note that this method does not clone the
221      * {@link EventLoopGroup} deeply but shallowly, making the group a shared resource.
222      */
223     @Override
224     @SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException")
225     public abstract B clone();
226 
227     /**
228      * Create a new {@link Channel} and register it with an {@link EventLoop}.
229      */
230     public ChannelFuture register() {
231         validate();
232         return initAndRegister();
233     }
234 
235     /**
236      * Create a new {@link Channel} and bind it.
237      */
238     public ChannelFuture bind() {
239         validate();
240         SocketAddress localAddress = this.localAddress;
241         if (localAddress == null) {
242             throw new IllegalStateException("localAddress not set");
243         }
244         return doBind(localAddress);
245     }
246 
247     /**
248      * Create a new {@link Channel} and bind it.
249      */
250     public ChannelFuture bind(int inetPort) {
251         return bind(new InetSocketAddress(inetPort));
252     }
253 
254     /**
255      * Create a new {@link Channel} and bind it.
256      */
257     public ChannelFuture bind(String inetHost, int inetPort) {
258         return bind(new InetSocketAddress(inetHost, inetPort));
259     }
260 
261     /**
262      * Create a new {@link Channel} and bind it.
263      */
264     public ChannelFuture bind(InetAddress inetHost, int inetPort) {
265         return bind(new InetSocketAddress(inetHost, inetPort));
266     }
267 
268     /**
269      * Create a new {@link Channel} and bind it.
270      */
271     public ChannelFuture bind(SocketAddress localAddress) {
272         validate();
273         if (localAddress == null) {
274             throw new NullPointerException("localAddress");
275         }
276         return doBind(localAddress);
277     }
278 
279     private ChannelFuture doBind(final SocketAddress localAddress) {
280         final ChannelFuture regFuture = initAndRegister();
281         final Channel channel = regFuture.channel();
282         if (regFuture.cause() != null) {
283             return regFuture;
284         }
285 
286         if (regFuture.isDone()) {
287             // At this point we know that the registration was complete and successful.
288             ChannelPromise promise = channel.newPromise();
289             doBind0(regFuture, channel, localAddress, promise);
290             return promise;
291         } else {
292             // Registration future is almost always fulfilled already, but just in case it's not.
293             final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
294             regFuture.addListener(new ChannelFutureListener() {
295                 @Override
296                 public void operationComplete(ChannelFuture future) throws Exception {
297                     Throwable cause = future.cause();
298                     if (cause != null) {
299                         // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
300                         // IllegalStateException once we try to access the EventLoop of the Channel.
301                         promise.setFailure(cause);
302                     } else {
303                         // Registration was successful, so set the correct executor to use.
304                         // See https://github.com/netty/netty/issues/2586
305                         promise.executor = channel.eventLoop();
306                     }
307                     doBind0(regFuture, channel, localAddress, promise);
308                 }
309             });
310             return promise;
311         }
312     }
313 
314     final ChannelFuture initAndRegister() {
315         final Channel channel = channelFactory().newChannel();
316         try {
317             init(channel);
318         } catch (Throwable t) {
319             channel.unsafe().closeForcibly();
320             // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
321             return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
322         }
323 
324         ChannelFuture regFuture = group().register(channel);
325         if (regFuture.cause() != null) {
326             if (channel.isRegistered()) {
327                 channel.close();
328             } else {
329                 channel.unsafe().closeForcibly();
330             }
331         }
332 
333         // If we are here and the promise is not failed, it's one of the following cases:
334         // 1) If we attempted registration from the event loop, the registration has been completed at this point.
335         //    i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
336         // 2) If we attempted registration from the other thread, the registration request has been successfully
337         //    added to the event loop's task queue for later execution.
338         //    i.e. It's safe to attempt bind() or connect() now:
339         //         because bind() or connect() will be executed *after* the scheduled registration task is executed
340         //         because register(), bind(), and connect() are all bound to the same thread.
341 
342         return regFuture;
343     }
344 
345     abstract void init(Channel channel) throws Exception;
346 
347     private static void doBind0(
348             final ChannelFuture regFuture, final Channel channel,
349             final SocketAddress localAddress, final ChannelPromise promise) {
350 
351         // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
352         // the pipeline in its channelRegistered() implementation.
353         channel.eventLoop().execute(new Runnable() {
354             @Override
355             public void run() {
356                 if (regFuture.isSuccess()) {
357                     channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
358                 } else {
359                     promise.setFailure(regFuture.cause());
360                 }
361             }
362         });
363     }
364 
365     /**
366      * the {@link ChannelHandler} to use for serving the requests.
367      */
368     @SuppressWarnings("unchecked")
369     public B handler(ChannelHandler handler) {
370         if (handler == null) {
371             throw new NullPointerException("handler");
372         }
373         this.handler = handler;
374         return (B) this;
375     }
376 
377     final SocketAddress localAddress() {
378         return localAddress;
379     }
380 
381     @SuppressWarnings("deprecation")
382     final ChannelFactory<? extends C> channelFactory() {
383         return channelFactory;
384     }
385 
386     final ChannelHandler handler() {
387         return handler;
388     }
389 
390     /**
391      * Return the configured {@link EventLoopGroup} or {@code null} if non is configured yet.
392      */
393     public EventLoopGroup group() {
394         return group;
395     }
396 
397     final Map<ChannelOption<?>, Object> options() {
398         return options;
399     }
400 
401     final Map<AttributeKey<?>, Object> attrs() {
402         return attrs;
403     }
404 
405     @Override
406     public String toString() {
407         StringBuilder buf = new StringBuilder()
408             .append(StringUtil.simpleClassName(this))
409             .append('(');
410         if (group != null) {
411             buf.append("group: ")
412                .append(StringUtil.simpleClassName(group))
413                .append(", ");
414         }
415         if (channelFactory != null) {
416             buf.append("channelFactory: ")
417                .append(channelFactory)
418                .append(", ");
419         }
420         if (localAddress != null) {
421             buf.append("localAddress: ")
422                .append(localAddress)
423                .append(", ");
424         }
425         synchronized (options) {
426             if (!options.isEmpty()) {
427                 buf.append("options: ")
428                    .append(options)
429                    .append(", ");
430             }
431         }
432         synchronized (attrs) {
433             if (!attrs.isEmpty()) {
434                 buf.append("attrs: ")
435                    .append(attrs)
436                    .append(", ");
437             }
438         }
439         if (handler != null) {
440             buf.append("handler: ")
441                .append(handler)
442                .append(", ");
443         }
444         if (buf.charAt(buf.length() - 1) == '(') {
445             buf.append(')');
446         } else {
447             buf.setCharAt(buf.length() - 2, ')');
448             buf.setLength(buf.length() - 1);
449         }
450         return buf.toString();
451     }
452 
453     private static final class PendingRegistrationPromise extends DefaultChannelPromise {
454         // Is set to the correct EventExecutor once the registration was successful. Otherwise it will
455         // stay null and so the GlobalEventExecutor.INSTANCE will be used for notifications.
456         private volatile EventExecutor executor;
457 
458         private PendingRegistrationPromise(Channel channel) {
459             super(channel);
460         }
461 
462         @Override
463         protected EventExecutor executor() {
464             EventExecutor executor = this.executor;
465             if (executor != null) {
466                 // If the registration was a success executor is set.
467                 //
468                 // See https://github.com/netty/netty/issues/2586
469                 return executor;
470             }
471             // The registration failed so we can only use the GlobalEventExecutor as last resort to notify.
472             return GlobalEventExecutor.INSTANCE;
473         }
474     }
475 }