View Javadoc

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