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.channel;
17  
18  import io.netty.bootstrap.Bootstrap;
19  import io.netty.bootstrap.ServerBootstrap;
20  import io.netty.channel.ChannelHandler.Sharable;
21  import io.netty.util.internal.PlatformDependent;
22  import io.netty.util.internal.logging.InternalLogger;
23  import io.netty.util.internal.logging.InternalLoggerFactory;
24  
25  import java.util.concurrent.ConcurrentMap;
26  
27  /**
28   * A special {@link ChannelInboundHandler} which offers an easy way to initialize a {@link Channel} once it was
29   * registered to its {@link EventLoop}.
30   *
31   * Implementations are most often used in the context of {@link Bootstrap#handler(ChannelHandler)} ,
32   * {@link ServerBootstrap#handler(ChannelHandler)} and {@link ServerBootstrap#childHandler(ChannelHandler)} to
33   * setup the {@link ChannelPipeline} of a {@link Channel}.
34   *
35   * <pre>
36   *
37   * public class MyChannelInitializer extends {@link ChannelInitializer} {
38   *     public void initChannel({@link Channel} channel) {
39   *         channel.pipeline().addLast("myHandler", new MyHandler());
40   *     }
41   * }
42   *
43   * {@link ServerBootstrap} bootstrap = ...;
44   * ...
45   * bootstrap.childHandler(new MyChannelInitializer());
46   * ...
47   * </pre>
48   * Be aware that this class is marked as {@link Sharable} and so the implementation must be safe to be re-used.
49   *
50   * @param <C>   A sub-type of {@link Channel}
51   */
52  @Sharable
53  public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter {
54  
55      private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelInitializer.class);
56      // We use a ConcurrentMap as a ChannelInitializer is usually shared between all Channels in a Bootstrap /
57      // ServerBootstrap. This way we can reduce the memory usage compared to use Attributes.
58      private final ConcurrentMap<ChannelHandlerContext, Boolean> initMap = PlatformDependent.newConcurrentHashMap();
59  
60      /**
61       * This method will be called once the {@link Channel} was registered. After the method returns this instance
62       * will be removed from the {@link ChannelPipeline} of the {@link Channel}.
63       *
64       * @param ch            the {@link Channel} which was registered.
65       * @throws Exception    is thrown if an error occurs. In that case it will be handled by
66       *                      {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
67       *                      the {@link Channel}.
68       */
69      protected abstract void initChannel(C ch) throws Exception;
70  
71      @Override
72      @SuppressWarnings("unchecked")
73      public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
74          // Normally this method will never be called as handlerAdded(...) should call initChannel(...) and remove
75          // the handler.
76          if (initChannel(ctx)) {
77              // we called initChannel(...) so we need to call now pipeline.fireChannelRegistered() to ensure we not
78              // miss an event.
79              ctx.pipeline().fireChannelRegistered();
80          } else {
81              // Called initChannel(...) before which is the expected behavior, so just forward the event.
82              ctx.fireChannelRegistered();
83          }
84      }
85  
86      /**
87       * Handle the {@link Throwable} by logging and closing the {@link Channel}. Sub-classes may override this.
88       */
89      @Override
90      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
91          logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), cause);
92          ctx.close();
93      }
94  
95      /**
96       * {@inheritDoc} If override this method ensure you call super!
97       */
98      @Override
99      public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
100         if (ctx.channel().isRegistered()) {
101             // This should always be true with our current DefaultChannelPipeline implementation.
102             // The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
103             // surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
104             // will be added in the expected order.
105             initChannel(ctx);
106         }
107     }
108 
109     @SuppressWarnings("unchecked")
110     private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
111         if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
112             try {
113                 initChannel((C) ctx.channel());
114             } catch (Throwable cause) {
115                 // Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
116                 // We do so to prevent multiple calls to initChannel(...).
117                 exceptionCaught(ctx, cause);
118             } finally {
119                 remove(ctx);
120             }
121             return true;
122         }
123         return false;
124     }
125 
126     private void remove(ChannelHandlerContext ctx) {
127         try {
128             ChannelPipeline pipeline = ctx.pipeline();
129             if (pipeline.context(this) != null) {
130                 pipeline.remove(this);
131             }
132         } finally {
133             initMap.remove(ctx);
134         }
135     }
136 }