View Javadoc
1   /*
2    * Copyright 2019 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    *   https://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.netty5.channel;
17  
18  import io.netty5.util.concurrent.FastThreadLocal;
19  import io.netty5.util.internal.PlatformDependent;
20  import io.netty5.util.internal.logging.InternalLogger;
21  import io.netty5.util.internal.logging.InternalLoggerFactory;
22  
23  import java.lang.annotation.ElementType;
24  import java.lang.annotation.Inherited;
25  import java.lang.annotation.Retention;
26  import java.lang.annotation.RetentionPolicy;
27  import java.lang.annotation.Target;
28  import java.lang.reflect.Method;
29  import java.net.SocketAddress;
30  import java.security.AccessController;
31  import java.security.PrivilegedExceptionAction;
32  import java.util.Map;
33  import java.util.WeakHashMap;
34  
35  final class ChannelHandlerMask {
36      private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelHandlerMask.class);
37  
38      // Using to mask which methods must be called for a ChannelHandler.
39      static final int MASK_CHANNEL_EXCEPTION_CAUGHT = 1;
40      static final int MASK_CHANNEL_REGISTERED = 1 << 1;
41      static final int MASK_CHANNEL_UNREGISTERED = 1 << 2;
42      static final int MASK_CHANNEL_ACTIVE = 1 << 3;
43      static final int MASK_CHANNEL_INACTIVE = 1 << 4;
44      static final int MASK_CHANNEL_SHUTDOWN = 1 << 5;
45      static final int MASK_CHANNEL_READ = 1 << 6;
46      static final int MASK_CHANNEL_READ_COMPLETE = 1 << 7;
47      static final int MASK_CHANNEL_INBOUND_EVENT = 1 << 8;
48      static final int MASK_CHANNEL_WRITABILITY_CHANGED = 1 << 9;
49      static final int MASK_BIND = 1 << 10;
50      static final int MASK_CONNECT = 1 << 11;
51      static final int MASK_DISCONNECT = 1 << 12;
52      static final int MASK_CLOSE = 1 << 13;
53      static final int MASK_SHUTDOWN = 1 << 14;
54      static final int MASK_REGISTER = 1 << 15;
55      static final int MASK_DEREGISTER = 1 << 16;
56      static final int MASK_READ = 1 << 17;
57      static final int MASK_WRITE = 1 << 18;
58      static final int MASK_FLUSH = 1 << 19;
59      static final int MASK_SEND_OUTBOUND_EVENT = 1 << 20;
60  
61      static final int MASK_PENDING_OUTBOUND_BYTES = 1 << 21;
62  
63      private static final int MASK_ALL_INBOUND = MASK_CHANNEL_EXCEPTION_CAUGHT | MASK_CHANNEL_REGISTERED |
64              MASK_CHANNEL_UNREGISTERED | MASK_CHANNEL_ACTIVE | MASK_CHANNEL_INACTIVE | MASK_CHANNEL_SHUTDOWN |
65              MASK_CHANNEL_READ | MASK_CHANNEL_READ_COMPLETE  | MASK_CHANNEL_WRITABILITY_CHANGED |
66              MASK_CHANNEL_INBOUND_EVENT;
67      private static final int MASK_ALL_OUTBOUND = MASK_BIND | MASK_CONNECT | MASK_DISCONNECT |
68              MASK_CLOSE | MASK_SHUTDOWN | MASK_REGISTER | MASK_DEREGISTER | MASK_READ | MASK_WRITE | MASK_FLUSH |
69              MASK_SEND_OUTBOUND_EVENT | MASK_PENDING_OUTBOUND_BYTES;
70  
71      private static final FastThreadLocal<Map<Class<? extends ChannelHandler>, Integer>> MASKS =
72              new FastThreadLocal<>() {
73                  @Override
74                  protected Map<Class<? extends ChannelHandler>, Integer> initialValue() {
75                      return new WeakHashMap<>(32);
76                  }
77              };
78  
79      /**
80       * Return the {@code executionMask}.
81       */
82      static int mask(Class<? extends ChannelHandler> clazz) {
83          // Try to obtain the mask from the cache first. If this fails calculate it and put it in the cache for fast
84          // lookup in the future.
85          Map<Class<? extends ChannelHandler>, Integer> cache = MASKS.get();
86          Integer mask = cache.get(clazz);
87          if (mask == null) {
88              mask = mask0(clazz);
89              cache.put(clazz, mask);
90          }
91          return mask;
92      }
93  
94      static boolean isInbound(Class<? extends ChannelHandler> clazz) {
95          return (mask(clazz) & MASK_ALL_INBOUND) != 0;
96      }
97  
98      static boolean isOutbound(Class<? extends ChannelHandler> clazz) {
99          return (mask(clazz) & MASK_ALL_OUTBOUND) != 0;
100     }
101 
102     /**
103      * Calculate the {@code executionMask}.
104      */
105     private static int mask0(Class<? extends ChannelHandler> handlerType) {
106         int mask = 0;
107         mask |= MASK_ALL_INBOUND;
108         mask |= MASK_ALL_OUTBOUND;
109 
110         try {
111             if (isSkippable(handlerType, "channelExceptionCaught", ChannelHandlerContext.class, Throwable.class)) {
112                 mask &= ~MASK_CHANNEL_EXCEPTION_CAUGHT;
113             }
114 
115             if (isSkippable(handlerType, "channelRegistered", ChannelHandlerContext.class)) {
116                 mask &= ~MASK_CHANNEL_REGISTERED;
117             }
118             if (isSkippable(handlerType, "channelUnregistered", ChannelHandlerContext.class)) {
119                 mask &= ~MASK_CHANNEL_UNREGISTERED;
120             }
121             if (isSkippable(handlerType, "channelActive", ChannelHandlerContext.class)) {
122                 mask &= ~MASK_CHANNEL_ACTIVE;
123             }
124             if (isSkippable(handlerType, "channelInactive", ChannelHandlerContext.class)) {
125                 mask &= ~MASK_CHANNEL_INACTIVE;
126             }
127             if (isSkippable(handlerType, "channelShutdown", ChannelHandlerContext.class,
128                     ChannelShutdownDirection.class)) {
129                 mask &= ~MASK_CHANNEL_SHUTDOWN;
130             }
131             if (isSkippable(handlerType, "channelRead", ChannelHandlerContext.class, Object.class)) {
132                 mask &= ~MASK_CHANNEL_READ;
133             }
134             if (isSkippable(handlerType, "channelReadComplete", ChannelHandlerContext.class)) {
135                 mask &= ~MASK_CHANNEL_READ_COMPLETE;
136             }
137             if (isSkippable(handlerType, "channelWritabilityChanged", ChannelHandlerContext.class)) {
138                 mask &= ~MASK_CHANNEL_WRITABILITY_CHANGED;
139             }
140             if (isSkippable(handlerType, "channelInboundEvent", ChannelHandlerContext.class, Object.class)) {
141                 mask &= ~MASK_CHANNEL_INBOUND_EVENT;
142             }
143             if (isSkippable(handlerType, "bind", ChannelHandlerContext.class, SocketAddress.class)) {
144                 mask &= ~MASK_BIND;
145             }
146             if (isSkippable(handlerType, "connect", ChannelHandlerContext.class, SocketAddress.class,
147                     SocketAddress.class)) {
148                 mask &= ~MASK_CONNECT;
149             }
150             if (isSkippable(handlerType, "disconnect", ChannelHandlerContext.class)) {
151                 mask &= ~MASK_DISCONNECT;
152             }
153             if (isSkippable(handlerType, "close", ChannelHandlerContext.class)) {
154                 mask &= ~MASK_CLOSE;
155             }
156             if (isSkippable(handlerType, "shutdown", ChannelHandlerContext.class,
157                     ChannelShutdownDirection.class)) {
158                 mask &= ~MASK_SHUTDOWN;
159             }
160             if (isSkippable(handlerType, "register", ChannelHandlerContext.class)) {
161                 mask &= ~MASK_REGISTER;
162             }
163             if (isSkippable(handlerType, "deregister", ChannelHandlerContext.class)) {
164                 mask &= ~MASK_DEREGISTER;
165             }
166             if (isSkippable(handlerType, "read", ChannelHandlerContext.class)) {
167                 mask &= ~MASK_READ;
168             }
169             if (isSkippable(handlerType, "write", ChannelHandlerContext.class, Object.class)) {
170                 mask &= ~MASK_WRITE;
171             }
172             if (isSkippable(handlerType, "flush", ChannelHandlerContext.class)) {
173                 mask &= ~MASK_FLUSH;
174             }
175             if (isSkippable(handlerType, "sendOutboundEvent", ChannelHandlerContext.class, Object.class)) {
176                 mask &= ~MASK_SEND_OUTBOUND_EVENT;
177             }
178             if (isSkippable(handlerType, "pendingOutboundBytes", ChannelHandlerContext.class)) {
179                 mask &= ~MASK_PENDING_OUTBOUND_BYTES;
180             }
181         } catch (Exception e) {
182             // Should never reach here.
183             PlatformDependent.throwException(e);
184         }
185 
186         return mask;
187     }
188 
189     private static boolean isSkippable(
190             final Class<?> handlerType, final String methodName, final Class<?>... paramTypes) throws Exception {
191         return AccessController.doPrivileged((PrivilegedExceptionAction<Boolean>) () -> {
192             Method m;
193             try {
194                 m = handlerType.getMethod(methodName, paramTypes);
195             } catch (NoSuchMethodException e) {
196                 if (logger.isDebugEnabled()) {
197                     logger.debug(
198                             "Class {} missing method {}, assume we can not skip execution", handlerType, methodName, e);
199                 }
200                 return false;
201             }
202             return m.isAnnotationPresent(Skip.class);
203         });
204     }
205 
206     private ChannelHandlerMask() { }
207 
208     /**
209      * Indicates that the annotated event handler method in {@link ChannelHandler} will not be invoked by
210      * {@link ChannelPipeline} and so <strong>MUST</strong> only be used when the {@link ChannelHandler}
211      * method does nothing except forward to the next {@link ChannelHandler} in the pipeline.
212      * <p>
213      * Note that this annotation is not {@linkplain Inherited inherited}. If a user overrides a method annotated with
214      * {@link Skip}, it will not be skipped anymore. Similarly, the user can override a method not annotated with
215      * {@link Skip} and simply pass the event through to the next handler, which reverses the behavior of the
216      * supertype.
217      * </p>
218      */
219     @Target(ElementType.METHOD)
220     @Retention(RetentionPolicy.RUNTIME)
221     @interface Skip {
222         // no value
223     }
224 }