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 org.jboss.netty.channel;
17  
18  import java.net.SocketAddress;
19  import java.util.Map;
20  
21  import org.jboss.netty.util.internal.ConversionUtil;
22  
23  
24  /**
25   * A helper class which provides various convenience methods related with
26   * {@link Channel}, {@link ChannelHandler}, and {@link ChannelPipeline}.
27   *
28   * <h3>Factory methods</h3>
29   * <p>
30   * It is always recommended to use the factory methods provided by
31   * {@link Channels} rather than calling the constructor of the implementation
32   * types.
33   * <ul>
34   * <li>{@link #pipeline()}</li>
35   * <li>{@link #pipeline(ChannelPipeline)}</li>
36   * <li>{@link #pipelineFactory(ChannelPipeline)}</li>
37   * <li>{@link #succeededFuture(Channel)}</li>
38   * <li>{@link #failedFuture(Channel, Throwable)}</li>
39   * </ul>
40   *
41   * <h3>Upstream and downstream event generation</h3>
42   * <p>
43   * Various event generation methods are provided to simplify the generation of
44   * upstream events and downstream events.  It is always recommended to use the
45   * event generation methods provided by {@link Channels} rather than calling
46   * {@link ChannelHandlerContext#sendUpstream(ChannelEvent)} or
47   * {@link ChannelHandlerContext#sendDownstream(ChannelEvent)} by yourself.
48   * @apiviz.landmark
49   */
50  public final class Channels {
51  
52      // pipeline factory methods
53  
54      /**
55       * Creates a new {@link ChannelPipeline}.
56       */
57      public static ChannelPipeline pipeline() {
58          return new DefaultChannelPipeline();
59      }
60  
61      /**
62       * Creates a new {@link ChannelPipeline} which contains the specified
63       * {@link ChannelHandler}s.  The names of the specified handlers are
64       * generated automatically; the first handler's name is {@code "0"},
65       * the second handler's name is {@code "1"}, the third handler's name is
66       * {@code "2"}, and so on.
67       */
68      public static ChannelPipeline pipeline(ChannelHandler... handlers) {
69          if (handlers == null) {
70              throw new NullPointerException("handlers");
71          }
72  
73          ChannelPipeline newPipeline = pipeline();
74          for (int i = 0; i < handlers.length; i ++) {
75              ChannelHandler h = handlers[i];
76              if (h == null) {
77                  break;
78              }
79              newPipeline.addLast(ConversionUtil.toString(i), h);
80          }
81          return newPipeline;
82      }
83  
84      /**
85       * Creates a new {@link ChannelPipeline} which contains the same entries
86       * with the specified {@code pipeline}.  Please note that only the names
87       * and the references of the {@link ChannelHandler}s will be copied; a new
88       * {@link ChannelHandler} instance will never be created.
89       */
90      public static ChannelPipeline pipeline(ChannelPipeline pipeline) {
91          ChannelPipeline newPipeline = pipeline();
92          for (Map.Entry<String, ChannelHandler> e: pipeline.toMap().entrySet()) {
93              newPipeline.addLast(e.getKey(), e.getValue());
94          }
95          return newPipeline;
96      }
97  
98      /**
99       * Creates a new {@link ChannelPipelineFactory} which creates a new
100      * {@link ChannelPipeline} which contains the same entries with the
101      * specified {@code pipeline}.  Please note that only the names and the
102      * references of the {@link ChannelHandler}s will be copied; a new
103      * {@link ChannelHandler} instance will never be created.
104      */
105     public static ChannelPipelineFactory pipelineFactory(
106             final ChannelPipeline pipeline) {
107         return new ChannelPipelineFactory() {
108             public ChannelPipeline getPipeline() {
109                 return pipeline(pipeline);
110             }
111         };
112     }
113 
114     // future factory methods
115 
116     /**
117      * Creates a new non-cancellable {@link ChannelFuture} for the specified
118      * {@link Channel}.
119      */
120     public static ChannelFuture future(Channel channel) {
121         return future(channel, false);
122     }
123 
124     /**
125      * Creates a new {@link ChannelFuture} for the specified {@link Channel}.
126      *
127      * @param cancellable {@code true} if and only if the returned future
128      *                    can be canceled by {@link ChannelFuture#cancel()}
129      */
130     public static ChannelFuture future(Channel channel, boolean cancellable) {
131         return new DefaultChannelFuture(channel, cancellable);
132     }
133 
134     /**
135      * Creates a new {@link ChannelFuture} which is already succeeded for the
136      * specified {@link Channel}.
137      */
138     public static ChannelFuture succeededFuture(Channel channel) {
139         if (channel instanceof AbstractChannel) {
140             return ((AbstractChannel) channel).getSucceededFuture();
141         } else {
142             return new SucceededChannelFuture(channel);
143         }
144     }
145 
146     /**
147      * Creates a new {@link ChannelFuture} which has failed already for the
148      * specified {@link Channel}.
149      *
150      * @param cause the cause of the failure
151      */
152     public static ChannelFuture failedFuture(Channel channel, Throwable cause) {
153         return new FailedChannelFuture(channel, cause);
154     }
155 
156     // event emission methods
157 
158     /**
159      * Sends a {@code "channelOpen"} event to the first
160      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
161      * the specified {@link Channel}.  If the specified channel has a parent,
162      * a {@code "childChannelOpen"} event will be sent, too.
163      */
164     public static void fireChannelOpen(Channel channel) {
165         // Notify the parent handler.
166         if (channel.getParent() != null) {
167             fireChildChannelStateChanged(channel.getParent(), channel);
168         }
169 
170         channel.getPipeline().sendUpstream(
171                 new UpstreamChannelStateEvent(
172                         channel, ChannelState.OPEN, Boolean.TRUE));
173     }
174 
175     /**
176      * Sends a {@code "channelOpen"} event to the
177      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
178      * from the handler associated with the specified
179      * {@link ChannelHandlerContext}.
180      * <p>
181      * Please note that this method does not trigger a
182      * {@code "childChannelOpen"} event unlike {@link #fireChannelOpen(Channel)}
183      * method.
184      */
185     public static void fireChannelOpen(ChannelHandlerContext ctx) {
186         ctx.sendUpstream(new UpstreamChannelStateEvent(
187                 ctx.getChannel(), ChannelState.OPEN, Boolean.TRUE));
188     }
189 
190     /**
191      * Sends a {@code "channelBound"} event to the first
192      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
193      * the specified {@link Channel}.
194      *
195      * @param localAddress
196      *        the local address where the specified channel is bound
197      */
198     public static void fireChannelBound(Channel channel, SocketAddress localAddress) {
199         channel.getPipeline().sendUpstream(
200                 new UpstreamChannelStateEvent(
201                         channel, ChannelState.BOUND, localAddress));
202     }
203 
204     /**
205      * Sends a {@code "channelBound"} event to the
206      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
207      * from the handler associated with the specified
208      * {@link ChannelHandlerContext}.
209      *
210      * @param localAddress
211      *        the local address where the specified channel is bound
212      */
213     public static void fireChannelBound(ChannelHandlerContext ctx, SocketAddress localAddress) {
214         ctx.sendUpstream(new UpstreamChannelStateEvent(
215                 ctx.getChannel(), ChannelState.BOUND, localAddress));
216     }
217 
218     /**
219      * Sends a {@code "channelConnected"} event to the first
220      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
221      * the specified {@link Channel}.
222      *
223      * @param remoteAddress
224      *        the remote address where the specified channel is connected
225      */
226     public static void fireChannelConnected(Channel channel, SocketAddress remoteAddress) {
227         channel.getPipeline().sendUpstream(
228                 new UpstreamChannelStateEvent(
229                         channel, ChannelState.CONNECTED, remoteAddress));
230     }
231 
232     /**
233      * Sends a {@code "channelConnected"} event to the
234      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
235      * from the handler associated with the specified
236      * {@link ChannelHandlerContext}.
237      *
238      * @param remoteAddress
239      *        the remote address where the specified channel is connected
240      */
241     public static void fireChannelConnected(ChannelHandlerContext ctx, SocketAddress remoteAddress) {
242 
243         ctx.sendUpstream(new UpstreamChannelStateEvent(
244                 ctx.getChannel(), ChannelState.CONNECTED, remoteAddress));
245     }
246 
247     /**
248      * Sends a {@code "messageReceived"} event to the first
249      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
250      * the specified {@link Channel}.
251      *
252      * @param message  the received message
253      */
254     public static void fireMessageReceived(Channel channel, Object message) {
255         fireMessageReceived(channel, message, null);
256     }
257 
258     /**
259      * Sends a {@code "messageReceived"} event to the first
260      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
261      * the specified {@link Channel} belongs.
262      *
263      * @param message        the received message
264      * @param remoteAddress  the remote address where the received message
265      *                       came from
266      */
267     public static void fireMessageReceived(Channel channel, Object message, SocketAddress remoteAddress) {
268         channel.getPipeline().sendUpstream(
269                 new UpstreamMessageEvent(channel, message, remoteAddress));
270     }
271 
272     /**
273      * Sends a {@code "messageReceived"} event to the
274      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
275      * from the handler associated with the specified
276      * {@link ChannelHandlerContext}.
277      *
278      * @param message  the received message
279      */
280     public static void fireMessageReceived(ChannelHandlerContext ctx, Object message) {
281         ctx.sendUpstream(new UpstreamMessageEvent(ctx.getChannel(), message, null));
282     }
283 
284     /**
285      * Sends a {@code "messageReceived"} event to the
286      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
287      * from the handler associated with the specified
288      * {@link ChannelHandlerContext}.
289      *
290      * @param message        the received message
291      * @param remoteAddress  the remote address where the received message
292      *                       came from
293      */
294     public static void fireMessageReceived(
295             ChannelHandlerContext ctx, Object message, SocketAddress remoteAddress) {
296         ctx.sendUpstream(new UpstreamMessageEvent(
297                 ctx.getChannel(), message, remoteAddress));
298     }
299 
300     /**
301      * Sends a {@code "writeComplete"} event to the first
302      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
303      * the specified {@link Channel} in the next io-thread.
304      */
305     public static ChannelFuture fireWriteCompleteLater(final Channel channel, final long amount) {
306         return channel.getPipeline().execute(new Runnable() {
307 
308             public void run() {
309                 fireWriteComplete(channel, amount);
310             }
311         });
312     }
313 
314     /**
315      * Sends a {@code "writeComplete"} event to the first
316      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
317      * the specified {@link Channel}.
318      */
319     public static void fireWriteComplete(Channel channel, long amount) {
320         if (amount == 0) {
321             return;
322         }
323 
324         channel.getPipeline().sendUpstream(
325                 new DefaultWriteCompletionEvent(channel, amount));
326     }
327 
328     /**
329      * Sends a {@code "writeComplete"} event to the
330      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
331      * from the handler associated with the specified
332      * {@link ChannelHandlerContext}.
333      */
334     public static void fireWriteComplete(ChannelHandlerContext ctx, long amount) {
335         ctx.sendUpstream(new DefaultWriteCompletionEvent(ctx.getChannel(), amount));
336     }
337 
338     /**
339      * Sends a {@code "channelInterestChanged"} event to the first
340      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
341      * the specified {@link Channel} once the io-thread runs again.
342      */
343     public static ChannelFuture fireChannelInterestChangedLater(final Channel channel) {
344         return channel.getPipeline().execute(new Runnable() {
345 
346             public void run() {
347                 fireChannelInterestChanged(channel);
348             }
349         });
350     }
351 
352     /**
353      * Sends a {@code "channelInterestChanged"} event to the first
354      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
355      * the specified {@link Channel}.
356      */
357     public static void fireChannelInterestChanged(Channel channel) {
358         channel.getPipeline().sendUpstream(
359                 new UpstreamChannelStateEvent(
360                         channel, ChannelState.INTEREST_OPS, Channel.OP_READ));
361     }
362 
363     /**
364      * Sends a {@code "channelInterestChanged"} event to the
365      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
366      * from the handler associated with the specified
367      * {@link ChannelHandlerContext}.
368      */
369     public static void fireChannelInterestChanged(
370             ChannelHandlerContext ctx) {
371 
372         ctx.sendUpstream(
373                 new UpstreamChannelStateEvent(
374                         ctx.getChannel(), ChannelState.INTEREST_OPS, Channel.OP_READ));
375     }
376 
377     /**
378      * Sends a {@code "channelDisconnected"} event to the first
379      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
380      * the specified {@link Channel} once the io-thread runs again.
381      */
382     public static ChannelFuture fireChannelDisconnectedLater(final Channel channel) {
383         return channel.getPipeline().execute(new Runnable() {
384 
385             public void run() {
386                 fireChannelDisconnected(channel);
387             }
388         });
389     }
390     /**
391      * Sends a {@code "channelDisconnected"} event to the first
392      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
393      * the specified {@link Channel}.
394      */
395     public static void fireChannelDisconnected(Channel channel) {
396         channel.getPipeline().sendUpstream(
397                 new UpstreamChannelStateEvent(
398                         channel, ChannelState.CONNECTED, null));
399     }
400 
401     /**
402      * Sends a {@code "channelDisconnected"} event to the
403      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
404      * from the handler associated with the specified
405      * {@link ChannelHandlerContext}.
406      */
407     public static void fireChannelDisconnected(ChannelHandlerContext ctx) {
408         ctx.sendUpstream(new UpstreamChannelStateEvent(
409                 ctx.getChannel(), ChannelState.CONNECTED, null));
410     }
411 
412     /**
413      * Sends a {@code "channelUnbound"} event to the first
414      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
415      * the specified {@link Channel} once the io-thread runs again.
416      */
417     public static ChannelFuture fireChannelUnboundLater(final Channel channel) {
418         return channel.getPipeline().execute(new Runnable() {
419 
420             public void run() {
421                 fireChannelUnbound(channel);
422             }
423         });
424     }
425 
426     /**
427      * Sends a {@code "channelUnbound"} event to the first
428      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
429      * the specified {@link Channel}.
430      */
431     public static void fireChannelUnbound(Channel channel) {
432         channel.getPipeline().sendUpstream(new UpstreamChannelStateEvent(
433                 channel, ChannelState.BOUND, null));
434     }
435 
436     /**
437      * Sends a {@code "channelUnbound"} event to the
438      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
439      * from the handler associated with the specified
440      * {@link ChannelHandlerContext}.
441      */
442     public static void fireChannelUnbound(ChannelHandlerContext ctx) {
443 
444         ctx.sendUpstream(new UpstreamChannelStateEvent(
445                 ctx.getChannel(), ChannelState.BOUND, null));
446     }
447 
448     /**
449      * Sends a {@code "channelClosed"} event to the first
450      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
451      * the specified {@link Channel} once the io-thread runs again.
452      */
453     public static ChannelFuture fireChannelClosedLater(final Channel channel) {
454         return channel.getPipeline().execute(new Runnable() {
455 
456             public void run() {
457                 fireChannelClosed(channel);
458             }
459         });
460     }
461 
462     /**
463      * Sends a {@code "channelClosed"} event to the first
464      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
465      * the specified {@link Channel}.
466      */
467     public static void fireChannelClosed(Channel channel) {
468         channel.getPipeline().sendUpstream(
469                 new UpstreamChannelStateEvent(
470                         channel, ChannelState.OPEN, Boolean.FALSE));
471 
472         // Notify the parent handler.
473         if (channel.getParent() != null) {
474             fireChildChannelStateChanged(channel.getParent(), channel);
475         }
476     }
477 
478     /**
479      * Sends a {@code "channelClosed"} event to the
480      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
481      * from the handler associated with the specified
482      * {@link ChannelHandlerContext}.
483      */
484     public static void fireChannelClosed(ChannelHandlerContext ctx) {
485         ctx.sendUpstream(
486                 new UpstreamChannelStateEvent(
487                         ctx.getChannel(), ChannelState.OPEN, Boolean.FALSE));
488     }
489 
490     /**
491      * Sends a {@code "exceptionCaught"} event to the first
492      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
493      * the specified {@link Channel} once the io-thread runs again.
494      */
495     public static ChannelFuture fireExceptionCaughtLater(final Channel channel, final Throwable cause) {
496         return channel.getPipeline().execute(new Runnable() {
497 
498             public void run() {
499                 fireExceptionCaught(channel, cause);
500             }
501         });
502     }
503 
504     /**
505      * Sends a {@code "exceptionCaught"} event to the
506      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
507      * from the handler associated with the specified
508      * {@link ChannelHandlerContext} once the io-thread runs again.
509      */
510     public static ChannelFuture fireExceptionCaughtLater(final ChannelHandlerContext ctx, final Throwable cause) {
511         return ctx.getPipeline().execute(new Runnable() {
512 
513             public void run() {
514                 fireExceptionCaught(ctx, cause);
515             }
516         });
517     }
518 
519     /**
520      * Sends a {@code "exceptionCaught"} event to the first
521      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
522      * the specified {@link Channel}.
523      */
524     public static void fireExceptionCaught(Channel channel, Throwable cause) {
525         channel.getPipeline().sendUpstream(
526                 new DefaultExceptionEvent(channel, cause));
527     }
528 
529     /**
530      * Sends a {@code "exceptionCaught"} event to the
531      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
532      * from the handler associated with the specified
533      * {@link ChannelHandlerContext}.
534      */
535     public static void fireExceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
536         ctx.sendUpstream(new DefaultExceptionEvent(ctx.getChannel(), cause));
537     }
538 
539     private static void fireChildChannelStateChanged(
540             Channel channel, Channel childChannel) {
541         channel.getPipeline().sendUpstream(
542                 new DefaultChildChannelStateEvent(channel, childChannel));
543     }
544 
545     /**
546      * Sends a {@code "bind"} request to the last
547      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
548      * the specified {@link Channel}.
549      *
550      * @param channel  the channel to bind
551      * @param localAddress  the local address to bind to
552      *
553      * @return the {@link ChannelFuture} which will be notified when the
554      *         bind operation is done
555      */
556     public static ChannelFuture bind(Channel channel, SocketAddress localAddress) {
557         if (localAddress == null) {
558             throw new NullPointerException("localAddress");
559         }
560         ChannelFuture future = future(channel);
561         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
562                 channel, future, ChannelState.BOUND, localAddress));
563         return future;
564     }
565 
566     /**
567      * Sends a {@code "bind"} request to the
568      * {@link ChannelDownstreamHandler} which is placed in the closest
569      * downstream from the handler associated with the specified
570      * {@link ChannelHandlerContext}.
571      *
572      * @param ctx     the context
573      * @param future  the future which will be notified when the bind
574      *                operation is done
575      * @param localAddress the local address to bind to
576      */
577     public static void bind(
578             ChannelHandlerContext ctx, ChannelFuture future, SocketAddress localAddress) {
579         if (localAddress == null) {
580             throw new NullPointerException("localAddress");
581         }
582         ctx.sendDownstream(new DownstreamChannelStateEvent(
583                 ctx.getChannel(), future, ChannelState.BOUND, localAddress));
584     }
585 
586     /**
587      * Sends a {@code "unbind"} request to the
588      * {@link ChannelDownstreamHandler} which is placed in the closest
589      * downstream from the handler associated with the specified
590      * {@link ChannelHandlerContext}.
591      *
592      * @param ctx     the context
593      * @param future  the future which will be notified when the unbind
594      *                operation is done
595      */
596     public static void unbind(ChannelHandlerContext ctx, ChannelFuture future) {
597         ctx.sendDownstream(new DownstreamChannelStateEvent(
598                 ctx.getChannel(), future, ChannelState.BOUND, null));
599     }
600 
601     /**
602      * Sends a {@code "unbind"} request to the last
603      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
604      * the specified {@link Channel}.
605      *
606      * @param channel  the channel to unbind
607      *
608      * @return the {@link ChannelFuture} which will be notified when the
609      *         unbind operation is done
610      */
611     public static ChannelFuture unbind(Channel channel) {
612         ChannelFuture future = future(channel);
613         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
614                 channel, future, ChannelState.BOUND, null));
615         return future;
616     }
617 
618     /**
619      * Sends a {@code "connect"} request to the last
620      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
621      * the specified {@link Channel}.
622      *
623      * @param channel  the channel to attempt a connection
624      * @param remoteAddress  the remote address to connect to
625      *
626      * @return the {@link ChannelFuture} which will be notified when the
627      *         connection attempt is done
628      */
629     public static ChannelFuture connect(Channel channel, SocketAddress remoteAddress) {
630         if (remoteAddress == null) {
631             throw new NullPointerException("remoteAddress");
632         }
633         ChannelFuture future = future(channel, true);
634         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
635                 channel, future, ChannelState.CONNECTED, remoteAddress));
636         return future;
637     }
638 
639     /**
640      * Sends a {@code "connect"} request to the
641      * {@link ChannelDownstreamHandler} which is placed in the closest
642      * downstream from the handler associated with the specified
643      * {@link ChannelHandlerContext}.
644      *
645      * @param ctx     the context
646      * @param future  the future which will be notified when the connection
647      *                attempt is done
648      * @param remoteAddress the remote address to connect to
649      */
650     public static void connect(
651             ChannelHandlerContext ctx, ChannelFuture future, SocketAddress remoteAddress) {
652         if (remoteAddress == null) {
653             throw new NullPointerException("remoteAddress");
654         }
655         ctx.sendDownstream(new DownstreamChannelStateEvent(
656                 ctx.getChannel(), future, ChannelState.CONNECTED, remoteAddress));
657     }
658 
659     /**
660      * Sends a {@code "write"} request to the last
661      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
662      * the specified {@link Channel}.
663      *
664      * @param channel  the channel to write a message
665      * @param message  the message to write to the channel
666      *
667      * @return the {@link ChannelFuture} which will be notified when the
668      *         write operation is done
669      */
670     public static ChannelFuture write(Channel channel, Object message) {
671         return write(channel, message, null);
672     }
673 
674     /**
675      * Sends a {@code "write"} request to the
676      * {@link ChannelDownstreamHandler} which is placed in the closest
677      * downstream from the handler associated with the specified
678      * {@link ChannelHandlerContext}.
679      *
680      * @param ctx     the context
681      * @param future  the future which will be notified when the write
682      *                operation is done
683      */
684     public static void write(
685             ChannelHandlerContext ctx, ChannelFuture future, Object message) {
686         write(ctx, future, message, null);
687     }
688 
689     /**
690      * Sends a {@code "write"} request to the last
691      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
692      * the specified {@link Channel}.
693      *
694      * @param channel  the channel to write a message
695      * @param message  the message to write to the channel
696      * @param remoteAddress  the destination of the message.
697      *                       {@code null} to use the default remote address
698      *
699      * @return the {@link ChannelFuture} which will be notified when the
700      *         write operation is done
701      */
702     public static ChannelFuture write(Channel channel, Object message, SocketAddress remoteAddress) {
703         ChannelFuture future = future(channel);
704         channel.getPipeline().sendDownstream(
705                 new DownstreamMessageEvent(channel, future, message, remoteAddress));
706         return future;
707     }
708 
709     /**
710      * Sends a {@code "write"} request to the
711      * {@link ChannelDownstreamHandler} which is placed in the closest
712      * downstream from the handler associated with the specified
713      * {@link ChannelHandlerContext}.
714      *
715      * @param ctx     the context
716      * @param future  the future which will be notified when the write
717      *                operation is done
718      * @param message the message to write to the channel
719      * @param remoteAddress  the destination of the message.
720      *                       {@code null} to use the default remote address.
721      */
722     public static void write(
723             ChannelHandlerContext ctx, ChannelFuture future,
724             Object message, SocketAddress remoteAddress) {
725         ctx.sendDownstream(
726                 new DownstreamMessageEvent(ctx.getChannel(), future, message, remoteAddress));
727     }
728 
729     /**
730      * Sends a {@code "setInterestOps"} request to the last
731      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
732      * the specified {@link Channel}.
733      *
734      * @param channel     the channel to change its interestOps
735      * @param interestOps the new interestOps
736      *
737      * @return the {@link ChannelFuture} which will be notified when the
738      *         interestOps is changed
739      */
740     public static ChannelFuture setInterestOps(Channel channel, int interestOps) {
741         validateInterestOps(interestOps);
742         interestOps = filterDownstreamInterestOps(interestOps);
743 
744         ChannelFuture future = future(channel);
745         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
746                 channel, future, ChannelState.INTEREST_OPS, interestOps));
747         return future;
748     }
749 
750     /**
751      * Sends a {@code "setInterestOps"} request to the
752      * {@link ChannelDownstreamHandler} which is placed in the closest
753      * downstream from the handler associated with the specified
754      * {@link ChannelHandlerContext}.
755      *
756      * @param ctx     the context
757      * @param future  the future which will be notified when the interestOps is
758      *                changed.
759      */
760     public static void setInterestOps(
761             ChannelHandlerContext ctx, ChannelFuture future, int interestOps) {
762         validateInterestOps(interestOps);
763         interestOps = filterDownstreamInterestOps(interestOps);
764 
765         ctx.sendDownstream(
766                 new DownstreamChannelStateEvent(
767                         ctx.getChannel(), future, ChannelState.INTEREST_OPS, interestOps));
768     }
769 
770     /**
771      * Sends a {@code "disconnect"} request to the last
772      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
773      * the specified {@link Channel}.
774      *
775      * @param channel  the channel to disconnect
776      *
777      * @return the {@link ChannelFuture} which will be notified on disconnection
778      */
779     public static ChannelFuture disconnect(Channel channel) {
780         ChannelFuture future = future(channel);
781         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
782                 channel, future, ChannelState.CONNECTED, null));
783         return future;
784     }
785 
786     /**
787      * Sends a {@code "disconnect"} request to the
788      * {@link ChannelDownstreamHandler} which is placed in the closest
789      * downstream from the handler associated with the specified
790      * {@link ChannelHandlerContext}.
791      *
792      * @param ctx     the context
793      * @param future  the future which will be notified on disconnection
794      */
795     public static void disconnect(
796             ChannelHandlerContext ctx, ChannelFuture future) {
797         ctx.sendDownstream(new DownstreamChannelStateEvent(
798                 ctx.getChannel(), future, ChannelState.CONNECTED, null));
799     }
800 
801     /**
802      * Sends a {@code "close"} request to the last
803      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
804      * the specified {@link Channel}.
805      *
806      * @param channel  the channel to close
807      *
808      * @return the {@link ChannelFuture} which will be notified on closure
809      */
810     public static ChannelFuture close(Channel channel) {
811         ChannelFuture future = channel.getCloseFuture();
812         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
813                 channel, future, ChannelState.OPEN, Boolean.FALSE));
814         return future;
815     }
816 
817     /**
818      * Sends a {@code "close"} request to the
819      * {@link ChannelDownstreamHandler} which is placed in the closest
820      * downstream from the handler associated with the specified
821      * {@link ChannelHandlerContext}.
822      *
823      * @param ctx     the context
824      * @param future  the future which will be notified on closure
825      */
826     public static void close(
827             ChannelHandlerContext ctx, ChannelFuture future) {
828         ctx.sendDownstream(new DownstreamChannelStateEvent(
829                 ctx.getChannel(), future, ChannelState.OPEN, Boolean.FALSE));
830     }
831 
832     private static void validateInterestOps(int interestOps) {
833         switch (interestOps) {
834         case Channel.OP_NONE:
835         case Channel.OP_READ:
836         case Channel.OP_WRITE:
837         case Channel.OP_READ_WRITE:
838             break;
839         default:
840             throw new IllegalArgumentException(
841                     "Invalid interestOps: " + interestOps);
842         }
843     }
844 
845     private static int filterDownstreamInterestOps(int interestOps) {
846         return interestOps & ~Channel.OP_WRITE;
847     }
848 
849     private Channels() {
850         // Unused
851     }
852 }