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 io.netty.channel;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  /**
22   * A {@link ChannelHandler} that appends the specified {@link ChannelHandler}s right next to itself.
23   * By default, it removes itself from the {@link ChannelPipeline} once the specified {@link ChannelHandler}s
24   * are added. Optionally, you can keep it in the {@link ChannelPipeline} by specifying a {@code boolean}
25   * parameter at construction time.
26   */
27  public class ChannelHandlerAppender extends ChannelHandlerAdapter {
28  
29      private static final class Entry {
30          final String name;
31          final ChannelHandler handler;
32  
33          Entry(String name, ChannelHandler handler) {
34              this.name = name;
35              this.handler = handler;
36          }
37      }
38  
39      private final boolean selfRemoval;
40      private final List<Entry> handlers = new ArrayList<Entry>();
41      private boolean added;
42  
43      /**
44       * Creates a new uninitialized instance. A class that extends this handler must invoke
45       * {@link #add(ChannelHandler...)} before adding this handler into a {@link ChannelPipeline}.
46       */
47      protected ChannelHandlerAppender() {
48          this(true);
49      }
50  
51      /**
52       * Creates a new uninitialized instance. A class that extends this handler must invoke
53       * {@link #add(ChannelHandler...)} before adding this handler into a {@link ChannelPipeline}.
54       *
55       * @param selfRemoval {@code true} to remove itself from the {@link ChannelPipeline} after appending
56       *                    the {@link ChannelHandler}s specified via {@link #add(ChannelHandler...)}.
57       */
58      protected ChannelHandlerAppender(boolean selfRemoval) {
59          this.selfRemoval = selfRemoval;
60      }
61  
62      /**
63       * Creates a new instance that appends the specified {@link ChannelHandler}s right next to itself.
64       */
65      public ChannelHandlerAppender(Iterable<? extends ChannelHandler> handlers) {
66          this(true, handlers);
67      }
68  
69      /**
70       * Creates a new instance that appends the specified {@link ChannelHandler}s right next to itself.
71       */
72      public ChannelHandlerAppender(ChannelHandler... handlers) {
73          this(true, handlers);
74      }
75  
76      /**
77       * Creates a new instance that appends the specified {@link ChannelHandler}s right next to itself.
78       *
79       * @param selfRemoval {@code true} to remove itself from the {@link ChannelPipeline} after appending
80       *                    the specified {@link ChannelHandler}s
81       */
82      public ChannelHandlerAppender(boolean selfRemoval, Iterable<? extends ChannelHandler> handlers) {
83          this.selfRemoval = selfRemoval;
84          add(handlers);
85      }
86  
87      /**
88       * Creates a new instance that appends the specified {@link ChannelHandler}s right next to itself.
89       *
90       * @param selfRemoval {@code true} to remove itself from the {@link ChannelPipeline} after appending
91       *                    the specified {@link ChannelHandler}s
92       */
93      public ChannelHandlerAppender(boolean selfRemoval, ChannelHandler... handlers) {
94          this.selfRemoval = selfRemoval;
95          add(handlers);
96      }
97  
98      /**
99       * Adds the specified handler to the list of the appended handlers.
100      *
101      * @param name the name of the appended handler. {@code null} to auto-generate
102      * @param handler the handler to append
103      *
104      * @throws IllegalStateException if {@link ChannelHandlerAppender} has been added to the pipeline already
105      */
106     protected final ChannelHandlerAppender add(String name, ChannelHandler handler) {
107         if (handler == null) {
108             throw new NullPointerException("handler");
109         }
110 
111         if (added) {
112             throw new IllegalStateException("added to the pipeline already");
113         }
114 
115         handlers.add(new Entry(name, handler));
116         return this;
117     }
118 
119     /**
120      * Adds the specified handler to the list of the appended handlers with the auto-generated handler name.
121      *
122      * @param handler the handler to append
123      *
124      * @throws IllegalStateException if {@link ChannelHandlerAppender} has been added to the pipeline already
125      */
126     protected final ChannelHandlerAppender add(ChannelHandler handler) {
127         return add(null, handler);
128     }
129 
130     /**
131      * Adds the specified handlers to the list of the appended handlers. The handlers' names are auto-generated.
132      *
133      * @throws IllegalStateException if {@link ChannelHandlerAppender} has been added to the pipeline already
134      */
135     protected final ChannelHandlerAppender add(Iterable<? extends ChannelHandler> handlers) {
136         if (handlers == null) {
137             throw new NullPointerException("handlers");
138         }
139 
140         for (ChannelHandler h: handlers) {
141             if (h == null) {
142                 break;
143             }
144             add(h);
145         }
146 
147         return this;
148     }
149 
150     /**
151      * Adds the specified handlers to the list of the appended handlers. The handlers' names are auto-generated.
152      *
153      * @throws IllegalStateException if {@link ChannelHandlerAppender} has been added to the pipeline already
154      */
155     protected final ChannelHandlerAppender add(ChannelHandler... handlers) {
156         if (handlers == null) {
157             throw new NullPointerException("handlers");
158         }
159 
160         for (ChannelHandler h: handlers) {
161             if (h == null) {
162                 break;
163             }
164 
165             add(h);
166         }
167 
168         return this;
169     }
170 
171     /**
172      * Returns the {@code index}-th appended handler.
173      */
174     @SuppressWarnings("unchecked")
175     protected final <T extends ChannelHandler> T handlerAt(int index) {
176         return (T) handlers.get(index).handler;
177     }
178 
179     @Override
180     public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
181         added = true;
182 
183         AbstractChannelHandlerContext dctx = (AbstractChannelHandlerContext) ctx;
184         DefaultChannelPipeline pipeline = (DefaultChannelPipeline) dctx.pipeline();
185         String name = dctx.name();
186         try {
187             for (Entry e: handlers) {
188                 String oldName = name;
189                 if (e.name == null) {
190                     name = pipeline.generateName(e.handler);
191                 } else {
192                     name = e.name;
193                 }
194 
195                 // Note that we do not use dctx.invoker() because it raises an IllegalStateExxception
196                 // if the Channel is not registered yet.
197                 pipeline.addAfter(dctx.invoker, oldName, name, e.handler);
198             }
199         } finally {
200             if (selfRemoval) {
201                 pipeline.remove(this);
202             }
203         }
204     }
205 }