View Javadoc
1   /*
2    * Copyright 2013 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.channel;
18  
19  import io.netty.util.internal.StringUtil;
20  
21  import java.net.SocketAddress;
22  
23  import static io.netty.channel.DefaultChannelPipeline.*;
24  
25  /**
26   * A set of helper methods for easier implementation of custom {@link ChannelHandlerInvoker} implementation.
27   */
28  public final class ChannelHandlerInvokerUtil {
29  
30      public static void invokeChannelRegisteredNow(ChannelHandlerContext ctx) {
31          try {
32              ctx.handler().channelRegistered(ctx);
33          } catch (Throwable t) {
34              notifyHandlerException(ctx, t);
35          }
36      }
37  
38      public static void invokeChannelUnregisteredNow(ChannelHandlerContext ctx) {
39          try {
40              ctx.handler().channelUnregistered(ctx);
41          } catch (Throwable t) {
42              notifyHandlerException(ctx, t);
43          }
44      }
45  
46      public static void invokeChannelActiveNow(final ChannelHandlerContext ctx) {
47          try {
48              ctx.handler().channelActive(ctx);
49          } catch (Throwable t) {
50              notifyHandlerException(ctx, t);
51          }
52      }
53  
54      public static void invokeChannelInactiveNow(final ChannelHandlerContext ctx) {
55          try {
56              ctx.handler().channelInactive(ctx);
57          } catch (Throwable t) {
58              notifyHandlerException(ctx, t);
59          }
60      }
61  
62      public static void invokeExceptionCaughtNow(final ChannelHandlerContext ctx, final Throwable cause) {
63          try {
64              ctx.handler().exceptionCaught(ctx, cause);
65          } catch (Throwable t) {
66              if (logger.isWarnEnabled()) {
67                  logger.warn("An exception was thrown by a user handler's exceptionCaught() method:", t);
68                  logger.warn(".. and the cause of the exceptionCaught() was:", cause);
69              }
70          }
71      }
72  
73      public static void invokeUserEventTriggeredNow(final ChannelHandlerContext ctx, final Object event) {
74          try {
75              ctx.handler().userEventTriggered(ctx, event);
76          } catch (Throwable t) {
77              notifyHandlerException(ctx, t);
78          }
79      }
80  
81      public static void invokeChannelReadNow(final ChannelHandlerContext ctx, final Object msg) {
82          try {
83              ((AbstractChannelHandlerContext) ctx).invokedThisChannelRead = true;
84              ctx.handler().channelRead(ctx, msg);
85          } catch (Throwable t) {
86              notifyHandlerException(ctx, t);
87          }
88      }
89  
90      public static void invokeChannelReadCompleteNow(final ChannelHandlerContext ctx) {
91          try {
92              ctx.handler().channelReadComplete(ctx);
93          } catch (Throwable t) {
94              notifyHandlerException(ctx, t);
95          }
96      }
97  
98      public static void invokeChannelWritabilityChangedNow(final ChannelHandlerContext ctx) {
99          try {
100             ctx.handler().channelWritabilityChanged(ctx);
101         } catch (Throwable t) {
102             notifyHandlerException(ctx, t);
103         }
104     }
105 
106     public static void invokeBindNow(
107             final ChannelHandlerContext ctx, final SocketAddress localAddress, final ChannelPromise promise) {
108         try {
109             ctx.handler().bind(ctx, localAddress, promise);
110         } catch (Throwable t) {
111             notifyOutboundHandlerException(t, promise);
112         }
113     }
114     public static void invokeConnectNow(
115             final ChannelHandlerContext ctx,
116             final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
117         try {
118             ctx.handler().connect(ctx, remoteAddress, localAddress, promise);
119         } catch (Throwable t) {
120             notifyOutboundHandlerException(t, promise);
121         }
122     }
123 
124     public static void invokeDisconnectNow(final ChannelHandlerContext ctx, final ChannelPromise promise) {
125         try {
126             ctx.handler().disconnect(ctx, promise);
127         } catch (Throwable t) {
128             notifyOutboundHandlerException(t, promise);
129         }
130     }
131 
132     public static void invokeCloseNow(final ChannelHandlerContext ctx, final ChannelPromise promise) {
133         try {
134             ctx.handler().close(ctx, promise);
135         } catch (Throwable t) {
136             notifyOutboundHandlerException(t, promise);
137         }
138     }
139 
140     public static void invokeDeregisterNow(final ChannelHandlerContext ctx, final ChannelPromise promise) {
141         try {
142             ctx.handler().deregister(ctx, promise);
143         } catch (Throwable t) {
144             notifyOutboundHandlerException(t, promise);
145         }
146     }
147 
148     public static void invokeReadNow(final ChannelHandlerContext ctx) {
149         try {
150             ctx.handler().read(ctx);
151         } catch (Throwable t) {
152             notifyHandlerException(ctx, t);
153         }
154     }
155 
156     public static void invokeWriteNow(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
157         try {
158             ctx.handler().write(ctx, msg, promise);
159         } catch (Throwable t) {
160             notifyOutboundHandlerException(t, promise);
161         }
162     }
163 
164     public static void invokeFlushNow(final ChannelHandlerContext ctx) {
165         try {
166             ctx.handler().flush(ctx);
167         } catch (Throwable t) {
168             notifyHandlerException(ctx, t);
169         }
170     }
171 
172     public static boolean validatePromise(
173             ChannelHandlerContext ctx, ChannelPromise promise, boolean allowVoidPromise) {
174         if (ctx == null) {
175             throw new NullPointerException("ctx");
176         }
177 
178         if (promise == null) {
179             throw new NullPointerException("promise");
180         }
181 
182         if (promise.isDone()) {
183             if (promise.isCancelled()) {
184                 return false;
185             }
186             throw new IllegalArgumentException("promise already done: " + promise);
187         }
188 
189         if (promise.channel() != ctx.channel()) {
190             throw new IllegalArgumentException(String.format(
191                     "promise.channel does not match: %s (expected: %s)", promise.channel(), ctx.channel()));
192         }
193 
194         if (promise.getClass() == DefaultChannelPromise.class) {
195             return true;
196         }
197 
198         if (!allowVoidPromise && promise instanceof VoidChannelPromise) {
199             throw new IllegalArgumentException(
200                     StringUtil.simpleClassName(VoidChannelPromise.class) + " not allowed for this operation");
201         }
202 
203         if (promise instanceof AbstractChannel.CloseFuture) {
204             throw new IllegalArgumentException(
205                     StringUtil.simpleClassName(AbstractChannel.CloseFuture.class) + " not allowed in a pipeline");
206         }
207         return true;
208     }
209 
210     private static void notifyHandlerException(ChannelHandlerContext ctx, Throwable cause) {
211         if (inExceptionCaught(cause)) {
212             if (logger.isWarnEnabled()) {
213                 logger.warn(
214                         "An exception was thrown by a user handler " +
215                                 "while handling an exceptionCaught event", cause);
216             }
217             return;
218         }
219 
220         invokeExceptionCaughtNow(ctx, cause);
221     }
222 
223     private static void notifyOutboundHandlerException(Throwable cause, ChannelPromise promise) {
224         if (!promise.tryFailure(cause) && !(promise instanceof VoidChannelPromise)) {
225             if (logger.isWarnEnabled()) {
226                 logger.warn("Failed to fail the promise because it's done already: {}", promise, cause);
227             }
228         }
229     }
230 
231     private static boolean inExceptionCaught(Throwable cause) {
232         do {
233             StackTraceElement[] trace = cause.getStackTrace();
234             if (trace != null) {
235                 for (StackTraceElement t : trace) {
236                     if (t == null) {
237                         break;
238                     }
239                     if ("exceptionCaught".equals(t.getMethodName())) {
240                         return true;
241                     }
242                 }
243             }
244 
245             cause = cause.getCause();
246         } while (cause != null);
247 
248         return false;
249     }
250 
251     private ChannelHandlerInvokerUtil() { }
252 }