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