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 
316     /**
317      * Sends a {@code "writeComplete"} event to the first
318      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
319      * the specified {@link Channel}.
320      */
321     public static void fireWriteComplete(Channel channel, long amount) {
322         if (amount == 0) {
323             return;
324         }
325 
326         channel.getPipeline().sendUpstream(
327                 new DefaultWriteCompletionEvent(channel, amount));
328     }
329 
330     /**
331      * Sends a {@code "writeComplete"} event to the
332      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
333      * from the handler associated with the specified
334      * {@link ChannelHandlerContext}.
335      */
336     public static void fireWriteComplete(ChannelHandlerContext ctx, long amount) {
337         ctx.sendUpstream(new DefaultWriteCompletionEvent(ctx.getChannel(), amount));
338     }
339 
340     /**
341      * Sends a {@code "channelInterestChanged"} event to the first
342      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
343      * the specified {@link Channel} once the io-thread runs again.
344      */
345     public static ChannelFuture fireChannelInterestChangedLater(final Channel channel) {
346         return channel.getPipeline().execute(new Runnable() {
347 
348             public void run() {
349                 fireChannelInterestChanged(channel);
350 
351             }
352         });
353     }
354 
355     /**
356      * Sends a {@code "channelInterestChanged"} event to the first
357      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
358      * the specified {@link Channel}.
359      */
360     public static void fireChannelInterestChanged(Channel channel) {
361         channel.getPipeline().sendUpstream(
362                 new UpstreamChannelStateEvent(
363                         channel, ChannelState.INTEREST_OPS, Channel.OP_READ));
364     }
365 
366     /**
367      * Sends a {@code "channelInterestChanged"} event to the
368      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
369      * from the handler associated with the specified
370      * {@link ChannelHandlerContext}.
371      */
372     public static void fireChannelInterestChanged(
373             ChannelHandlerContext ctx) {
374 
375         ctx.sendUpstream(
376                 new UpstreamChannelStateEvent(
377                         ctx.getChannel(), ChannelState.INTEREST_OPS, Channel.OP_READ));
378     }
379 
380     /**
381      * Sends a {@code "channelDisconnected"} event to the first
382      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
383      * the specified {@link Channel} once the io-thread runs again.
384      */
385     public static ChannelFuture fireChannelDisconnectedLater(final Channel channel) {
386         return channel.getPipeline().execute(new Runnable() {
387 
388             public void run() {
389                 fireChannelDisconnected(channel);
390             }
391         });
392     }
393     /**
394      * Sends a {@code "channelDisconnected"} event to the first
395      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
396      * the specified {@link Channel}.
397      */
398     public static void fireChannelDisconnected(Channel channel) {
399         channel.getPipeline().sendUpstream(
400                 new UpstreamChannelStateEvent(
401                         channel, ChannelState.CONNECTED, null));
402     }
403 
404     /**
405      * Sends a {@code "channelDisconnected"} event to the
406      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
407      * from the handler associated with the specified
408      * {@link ChannelHandlerContext}.
409      */
410     public static void fireChannelDisconnected(ChannelHandlerContext ctx) {
411         ctx.sendUpstream(new UpstreamChannelStateEvent(
412                 ctx.getChannel(), ChannelState.CONNECTED, null));
413     }
414 
415     /**
416      * Sends a {@code "channelUnbound"} event to the first
417      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
418      * the specified {@link Channel} once the io-thread runs again.
419      */
420     public static ChannelFuture fireChannelUnboundLater(final Channel channel) {
421         return channel.getPipeline().execute(new Runnable() {
422 
423             public void run() {
424                 fireChannelUnbound(channel);
425             }
426         });
427     }
428 
429 
430     /**
431      * Sends a {@code "channelUnbound"} event to the first
432      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
433      * the specified {@link Channel}.
434      */
435     public static void fireChannelUnbound(Channel channel) {
436         channel.getPipeline().sendUpstream(new UpstreamChannelStateEvent(
437                 channel, ChannelState.BOUND, null));
438     }
439 
440     /**
441      * Sends a {@code "channelUnbound"} event to the
442      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
443      * from the handler associated with the specified
444      * {@link ChannelHandlerContext}.
445      */
446     public static void fireChannelUnbound(ChannelHandlerContext ctx) {
447 
448         ctx.sendUpstream(new UpstreamChannelStateEvent(
449                 ctx.getChannel(), ChannelState.BOUND, null));
450     }
451 
452 
453 
454     /**
455      * Sends a {@code "channelClosed"} event to the first
456      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
457      * the specified {@link Channel} once the io-thread runs again.
458      */
459     public static ChannelFuture fireChannelClosedLater(final Channel channel) {
460         return channel.getPipeline().execute(new Runnable() {
461 
462             public void run() {
463                 fireChannelClosed(channel);
464             }
465         });
466 
467     }
468 
469 
470     /**
471      * Sends a {@code "channelClosed"} event to the first
472      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
473      * the specified {@link Channel}.
474      */
475     public static void fireChannelClosed(Channel channel) {
476         channel.getPipeline().sendUpstream(
477                 new UpstreamChannelStateEvent(
478                         channel, ChannelState.OPEN, Boolean.FALSE));
479 
480         // Notify the parent handler.
481         if (channel.getParent() != null) {
482             fireChildChannelStateChanged(channel.getParent(), channel);
483         }
484     }
485 
486     /**
487      * Sends a {@code "channelClosed"} event to the
488      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
489      * from the handler associated with the specified
490      * {@link ChannelHandlerContext}.
491      */
492     public static void fireChannelClosed(ChannelHandlerContext ctx) {
493         ctx.sendUpstream(
494                 new UpstreamChannelStateEvent(
495                         ctx.getChannel(), ChannelState.OPEN, Boolean.FALSE));
496     }
497 
498     /**
499      * Sends a {@code "exceptionCaught"} event to the first
500      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
501      * the specified {@link Channel} once the io-thread runs again.
502      */
503     public static ChannelFuture fireExceptionCaughtLater(final Channel channel, final Throwable cause) {
504         return channel.getPipeline().execute(new Runnable() {
505 
506             public void run() {
507                 fireExceptionCaught(channel, cause);
508             }
509         });
510     }
511 
512     /**
513      * Sends a {@code "exceptionCaught"} event to the
514      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
515      * from the handler associated with the specified
516      * {@link ChannelHandlerContext} once the io-thread runs again.
517      */
518     public static ChannelFuture fireExceptionCaughtLater(final ChannelHandlerContext ctx, final Throwable cause) {
519         return ctx.getPipeline().execute(new Runnable() {
520 
521             public void run() {
522                 fireExceptionCaught(ctx, cause);
523             }
524         });
525     }
526 
527     /**
528      * Sends a {@code "exceptionCaught"} event to the first
529      * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of
530      * the specified {@link Channel}.
531      */
532     public static void fireExceptionCaught(Channel channel, Throwable cause) {
533         channel.getPipeline().sendUpstream(
534                 new DefaultExceptionEvent(channel, cause));
535     }
536 
537     /**
538      * Sends a {@code "exceptionCaught"} event to the
539      * {@link ChannelUpstreamHandler} which is placed in the closest upstream
540      * from the handler associated with the specified
541      * {@link ChannelHandlerContext}.
542      */
543     public static void fireExceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
544         ctx.sendUpstream(new DefaultExceptionEvent(ctx.getChannel(), cause));
545     }
546 
547     private static void fireChildChannelStateChanged(
548             Channel channel, Channel childChannel) {
549         channel.getPipeline().sendUpstream(
550                 new DefaultChildChannelStateEvent(channel, childChannel));
551     }
552 
553     /**
554      * Sends a {@code "bind"} request to the last
555      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
556      * the specified {@link Channel}.
557      *
558      * @param channel  the channel to bind
559      * @param localAddress  the local address to bind to
560      *
561      * @return the {@link ChannelFuture} which will be notified when the
562      *         bind operation is done
563      */
564     public static ChannelFuture bind(Channel channel, SocketAddress localAddress) {
565         if (localAddress == null) {
566             throw new NullPointerException("localAddress");
567         }
568         ChannelFuture future = future(channel);
569         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
570                 channel, future, ChannelState.BOUND, localAddress));
571         return future;
572     }
573 
574     /**
575      * Sends a {@code "bind"} request to the
576      * {@link ChannelDownstreamHandler} which is placed in the closest
577      * downstream from the handler associated with the specified
578      * {@link ChannelHandlerContext}.
579      *
580      * @param ctx     the context
581      * @param future  the future which will be notified when the bind
582      *                operation is done
583      * @param localAddress the local address to bind to
584      */
585     public static void bind(
586             ChannelHandlerContext ctx, ChannelFuture future, SocketAddress localAddress) {
587         if (localAddress == null) {
588             throw new NullPointerException("localAddress");
589         }
590         ctx.sendDownstream(new DownstreamChannelStateEvent(
591                 ctx.getChannel(), future, ChannelState.BOUND, localAddress));
592     }
593 
594     /**
595      * Sends a {@code "unbind"} request to the
596      * {@link ChannelDownstreamHandler} which is placed in the closest
597      * downstream from the handler associated with the specified
598      * {@link ChannelHandlerContext}.
599      *
600      * @param ctx     the context
601      * @param future  the future which will be notified when the unbind
602      *                operation is done
603      */
604     public static void unbind(ChannelHandlerContext ctx, ChannelFuture future) {
605         ctx.sendDownstream(new DownstreamChannelStateEvent(
606                 ctx.getChannel(), future, ChannelState.BOUND, null));
607     }
608 
609     /**
610      * Sends a {@code "unbind"} request to the last
611      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
612      * the specified {@link Channel}.
613      *
614      * @param channel  the channel to unbind
615      *
616      * @return the {@link ChannelFuture} which will be notified when the
617      *         unbind operation is done
618      */
619     public static ChannelFuture unbind(Channel channel) {
620         ChannelFuture future = future(channel);
621         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
622                 channel, future, ChannelState.BOUND, null));
623         return future;
624     }
625 
626     /**
627      * Sends a {@code "connect"} request to the last
628      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
629      * the specified {@link Channel}.
630      *
631      * @param channel  the channel to attempt a connection
632      * @param remoteAddress  the remote address to connect to
633      *
634      * @return the {@link ChannelFuture} which will be notified when the
635      *         connection attempt is done
636      */
637     public static ChannelFuture connect(Channel channel, SocketAddress remoteAddress) {
638         if (remoteAddress == null) {
639             throw new NullPointerException("remoteAddress");
640         }
641         ChannelFuture future = future(channel, true);
642         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
643                 channel, future, ChannelState.CONNECTED, remoteAddress));
644         return future;
645     }
646 
647     /**
648      * Sends a {@code "connect"} request to the
649      * {@link ChannelDownstreamHandler} which is placed in the closest
650      * downstream from the handler associated with the specified
651      * {@link ChannelHandlerContext}.
652      *
653      * @param ctx     the context
654      * @param future  the future which will be notified when the connection
655      *                attempt is done
656      * @param remoteAddress the remote address to connect to
657      */
658     public static void connect(
659             ChannelHandlerContext ctx, ChannelFuture future, SocketAddress remoteAddress) {
660         if (remoteAddress == null) {
661             throw new NullPointerException("remoteAddress");
662         }
663         ctx.sendDownstream(new DownstreamChannelStateEvent(
664                 ctx.getChannel(), future, ChannelState.CONNECTED, remoteAddress));
665     }
666 
667     /**
668      * Sends a {@code "write"} request to the last
669      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
670      * the specified {@link Channel}.
671      *
672      * @param channel  the channel to write a message
673      * @param message  the message to write to the channel
674      *
675      * @return the {@link ChannelFuture} which will be notified when the
676      *         write operation is done
677      */
678     public static ChannelFuture write(Channel channel, Object message) {
679         return write(channel, message, null);
680     }
681 
682     /**
683      * Sends a {@code "write"} request to the
684      * {@link ChannelDownstreamHandler} which is placed in the closest
685      * downstream from the handler associated with the specified
686      * {@link ChannelHandlerContext}.
687      *
688      * @param ctx     the context
689      * @param future  the future which will be notified when the write
690      *                operation is done
691      */
692     public static void write(
693             ChannelHandlerContext ctx, ChannelFuture future, Object message) {
694         write(ctx, future, message, null);
695     }
696 
697     /**
698      * Sends a {@code "write"} request to the last
699      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
700      * the specified {@link Channel}.
701      *
702      * @param channel  the channel to write a message
703      * @param message  the message to write to the channel
704      * @param remoteAddress  the destination of the message.
705      *                       {@code null} to use the default remote address
706      *
707      * @return the {@link ChannelFuture} which will be notified when the
708      *         write operation is done
709      */
710     public static ChannelFuture write(Channel channel, Object message, SocketAddress remoteAddress) {
711         ChannelFuture future = future(channel);
712         channel.getPipeline().sendDownstream(
713                 new DownstreamMessageEvent(channel, future, message, remoteAddress));
714         return future;
715     }
716 
717     /**
718      * Sends a {@code "write"} request to the
719      * {@link ChannelDownstreamHandler} which is placed in the closest
720      * downstream from the handler associated with the specified
721      * {@link ChannelHandlerContext}.
722      *
723      * @param ctx     the context
724      * @param future  the future which will be notified when the write
725      *                operation is done
726      * @param message the message to write to the channel
727      * @param remoteAddress  the destination of the message.
728      *                       {@code null} to use the default remote address.
729      */
730     public static void write(
731             ChannelHandlerContext ctx, ChannelFuture future,
732             Object message, SocketAddress remoteAddress) {
733         ctx.sendDownstream(
734                 new DownstreamMessageEvent(ctx.getChannel(), future, message, remoteAddress));
735     }
736 
737     /**
738      * Sends a {@code "setInterestOps"} request to the last
739      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
740      * the specified {@link Channel}.
741      *
742      * @param channel     the channel to change its interestOps
743      * @param interestOps the new interestOps
744      *
745      * @return the {@link ChannelFuture} which will be notified when the
746      *         interestOps is changed
747      */
748     public static ChannelFuture setInterestOps(Channel channel, int interestOps) {
749         validateInterestOps(interestOps);
750         interestOps = filterDownstreamInterestOps(interestOps);
751 
752         ChannelFuture future = future(channel);
753         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
754                 channel, future, ChannelState.INTEREST_OPS, interestOps));
755         return future;
756     }
757 
758     /**
759      * Sends a {@code "setInterestOps"} request to the
760      * {@link ChannelDownstreamHandler} which is placed in the closest
761      * downstream from the handler associated with the specified
762      * {@link ChannelHandlerContext}.
763      *
764      * @param ctx     the context
765      * @param future  the future which will be notified when the interestOps is
766      *                changed.
767      */
768     public static void setInterestOps(
769             ChannelHandlerContext ctx, ChannelFuture future, int interestOps) {
770         validateInterestOps(interestOps);
771         interestOps = filterDownstreamInterestOps(interestOps);
772 
773         ctx.sendDownstream(
774                 new DownstreamChannelStateEvent(
775                         ctx.getChannel(), future, ChannelState.INTEREST_OPS, interestOps));
776     }
777 
778     /**
779      * Sends a {@code "disconnect"} request to the last
780      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
781      * the specified {@link Channel}.
782      *
783      * @param channel  the channel to disconnect
784      *
785      * @return the {@link ChannelFuture} which will be notified on disconnection
786      */
787     public static ChannelFuture disconnect(Channel channel) {
788         ChannelFuture future = future(channel);
789         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
790                 channel, future, ChannelState.CONNECTED, null));
791         return future;
792     }
793 
794     /**
795      * Sends a {@code "disconnect"} request to the
796      * {@link ChannelDownstreamHandler} which is placed in the closest
797      * downstream from the handler associated with the specified
798      * {@link ChannelHandlerContext}.
799      *
800      * @param ctx     the context
801      * @param future  the future which will be notified on disconnection
802      */
803     public static void disconnect(
804             ChannelHandlerContext ctx, ChannelFuture future) {
805         ctx.sendDownstream(new DownstreamChannelStateEvent(
806                 ctx.getChannel(), future, ChannelState.CONNECTED, null));
807     }
808 
809     /**
810      * Sends a {@code "close"} request to the last
811      * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of
812      * the specified {@link Channel}.
813      *
814      * @param channel  the channel to close
815      *
816      * @return the {@link ChannelFuture} which will be notified on closure
817      */
818     public static ChannelFuture close(Channel channel) {
819         ChannelFuture future = channel.getCloseFuture();
820         channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
821                 channel, future, ChannelState.OPEN, Boolean.FALSE));
822         return future;
823     }
824 
825     /**
826      * Sends a {@code "close"} request to the
827      * {@link ChannelDownstreamHandler} which is placed in the closest
828      * downstream from the handler associated with the specified
829      * {@link ChannelHandlerContext}.
830      *
831      * @param ctx     the context
832      * @param future  the future which will be notified on closure
833      */
834     public static void close(
835             ChannelHandlerContext ctx, ChannelFuture future) {
836         ctx.sendDownstream(new DownstreamChannelStateEvent(
837                 ctx.getChannel(), future, ChannelState.OPEN, Boolean.FALSE));
838     }
839 
840     private static void validateInterestOps(int interestOps) {
841         switch (interestOps) {
842         case Channel.OP_NONE:
843         case Channel.OP_READ:
844         case Channel.OP_WRITE:
845         case Channel.OP_READ_WRITE:
846             break;
847         default:
848             throw new IllegalArgumentException(
849                     "Invalid interestOps: " + interestOps);
850         }
851     }
852 
853     private static int filterDownstreamInterestOps(int interestOps) {
854         return interestOps & ~Channel.OP_WRITE;
855     }
856 
857     private Channels() {
858         // Unused
859     }
860 }