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             }
317             // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
318             return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
319         }
320 
321         ChannelFuture regFuture = group().register(channel);
322         if (regFuture.cause() != null) {
323             if (channel.isRegistered()) {
324                 channel.close();
325             } else {
326                 channel.unsafe().closeForcibly();
327             }
328         }
329 
330         // If we are here and the promise is not failed, it's one of the following cases:
331         // 1) If we attempted registration from the event loop, the registration has been completed at this point.
332         //    i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
333         // 2) If we attempted registration from the other thread, the registration request has been successfully
334         //    added to the event loop's task queue for later execution.
335         //    i.e. It's safe to attempt bind() or connect() now:
336         //         because bind() or connect() will be executed *after* the scheduled registration task is executed
337         //         because register(), bind(), and connect() are all bound to the same thread.
338 
339         return regFuture;
340     }
341 
342     abstract void init(Channel channel) throws Exception;
343 
344     private static void doBind0(
345             final ChannelFuture regFuture, final Channel channel,
346             final SocketAddress localAddress, final ChannelPromise promise) {
347 
348         // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
349         // the pipeline in its channelRegistered() implementation.
350         channel.eventLoop().execute(new Runnable() {
351             @Override
352             public void run() {
353                 if (regFuture.isSuccess()) {
354                     channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
355                 } else {
356                     promise.setFailure(regFuture.cause());
357                 }
358             }
359         });
360     }
361 
362     /**
363      * the {@link ChannelHandler} to use for serving the requests.
364      */
365     public B handler(ChannelHandler handler) {
366         if (handler == null) {
367             throw new NullPointerException("handler");
368         }
369         this.handler = handler;
370         return self();
371     }
372 
373     final SocketAddress localAddress() {
374         return localAddress;
375     }
376 
377     final ChannelFactory<? extends C> channelFactory() {
378         return channelFactory;
379     }
380 
381     final ChannelHandler handler() {
382         return handler;
383     }
384 
385     /**
386      * Return the configured {@link EventLoopGroup} or {@code null} if non is configured yet.
387      */
388     public EventLoopGroup group() {
389         return group;
390     }
391 
392     final Map<ChannelOption<?>, Object> options() {
393         return options;
394     }
395 
396     final Map<AttributeKey<?>, Object> attrs() {
397         return attrs;
398     }
399 
400     static void setChannelOptions(
401             Channel channel, Map<ChannelOption<?>, Object> options, InternalLogger logger) {
402         for (Map.Entry<ChannelOption<?>, Object> e: options.entrySet()) {
403             setChannelOption(channel, e.getKey(), e.getValue(), logger);
404         }
405     }
406 
407     static void setChannelOptions(
408             Channel channel, Map.Entry<ChannelOption<?>, Object>[] options, InternalLogger logger) {
409         for (Map.Entry<ChannelOption<?>, Object> e: options) {
410             setChannelOption(channel, e.getKey(), e.getValue(), logger);
411         }
412     }
413 
414     @SuppressWarnings("unchecked")
415     private static void setChannelOption(
416             Channel channel, ChannelOption<?> option, Object value, InternalLogger logger) {
417         try {
418             if (!channel.config().setOption((ChannelOption<Object>) option, value)) {
419                 logger.warn("Unknown channel option '{}' for channel '{}'", option, channel);
420             }
421         } catch (Throwable t) {
422             logger.warn(
423                     "Failed to set channel option '{}' with value '{}' for channel '{}'", option, value, channel, t);
424         }
425     }
426 
427     @Override
428     public String toString() {
429         StringBuilder buf = new StringBuilder()
430             .append(StringUtil.simpleClassName(this))
431             .append('(');
432         if (group != null) {
433             buf.append("group: ")
434                .append(StringUtil.simpleClassName(group))
435                .append(", ");
436         }
437         if (channelFactory != null) {
438             buf.append("channelFactory: ")
439                .append(channelFactory)
440                .append(", ");
441         }
442         if (localAddress != null) {
443             buf.append("localAddress: ")
444                .append(localAddress)
445                .append(", ");
446         }
447         synchronized (options) {
448             if (!options.isEmpty()) {
449                 buf.append("options: ")
450                    .append(options)
451                    .append(", ");
452             }
453         }
454         synchronized (attrs) {
455             if (!attrs.isEmpty()) {
456                 buf.append("attrs: ")
457                    .append(attrs)
458                    .append(", ");
459             }
460         }
461         if (handler != null) {
462             buf.append("handler: ")
463                .append(handler)
464                .append(", ");
465         }
466         if (buf.charAt(buf.length() - 1) == '(') {
467             buf.append(')');
468         } else {
469             buf.setCharAt(buf.length() - 2, ')');
470             buf.setLength(buf.length() - 1);
471         }
472         return buf.toString();
473     }
474 
475     private static final class BootstrapChannelFactory<T extends Channel> implements ChannelFactory<T> {
476         private final Class<? extends T> clazz;
477 
478         BootstrapChannelFactory(Class<? extends T> clazz) {
479             this.clazz = clazz;
480         }
481 
482         @Override
483         public T newChannel() {
484             try {
485                 return clazz.getConstructor().newInstance();
486             } catch (Throwable t) {
487                 throw new ChannelException("Unable to create Channel from class " + clazz, t);
488             }
489         }
490 
491         @Override
492         public String toString() {
493             return StringUtil.simpleClassName(clazz) + ".class";
494         }
495     }
496 
497     private static final class PendingRegistrationPromise extends DefaultChannelPromise {
498         // Is set to the correct EventExecutor once the registration was successful. Otherwise it will
499         // stay null and so the GlobalEventExecutor.INSTANCE will be used for notifications.
500         private volatile EventExecutor executor;
501 
502         private PendingRegistrationPromise(Channel channel) {
503             super(channel);
504         }
505 
506         @Override
507         protected EventExecutor executor() {
508             EventExecutor executor = this.executor;
509             if (executor != null) {
510                 // If the registration was a success executor is set.
511                 //
512                 // See https://github.com/netty/netty/issues/2586
513                 return executor;
514             }
515             // The registration failed so we can only use the GlobalEventExecutor as last resort to notify.
516             return GlobalEventExecutor.INSTANCE;
517         }
518     }
519 }