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.concurrent.ConcurrentMap;
20  
21  import org.jboss.netty.util.internal.ConcurrentHashMap;
22  
23  /**
24   * A skeletal {@link Channel} implementation.
25   *
26   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
27   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
28   *
29   * @version $Rev: 2339 $, $Date: 2010-07-07 13:36:25 +0900 (Wed, 07 Jul 2010) $
30   *
31   */
32  public abstract class AbstractChannel implements Channel {
33  
34      static final ConcurrentMap<Integer, Channel> allChannels = new ConcurrentHashMap<Integer, Channel>();
35      private static final IdDeallocator ID_DEALLOCATOR = new IdDeallocator();
36  
37      private static Integer allocateId(Channel channel) {
38          Integer id = Integer.valueOf(System.identityHashCode(channel));
39          for (;;) {
40              // Loop until a unique ID is acquired.
41              // It should be found in one loop practically.
42              if (allChannels.putIfAbsent(id, channel) == null) {
43                  // Successfully acquired.
44                  return id;
45              } else {
46                  // Taken by other channel at almost the same moment.
47                  id = Integer.valueOf(id.intValue() + 1);
48              }
49          }
50      }
51  
52      private static final class IdDeallocator implements ChannelFutureListener {
53          IdDeallocator() {
54              super();
55          }
56  
57          public void operationComplete(ChannelFuture future) throws Exception {
58              allChannels.remove(future.getChannel().getId());
59          }
60      }
61  
62      private final Integer id;
63      private final Channel parent;
64      private final ChannelFactory factory;
65      private final ChannelPipeline pipeline;
66      private final ChannelFuture succeededFuture = new SucceededChannelFuture(this);
67      private final ChannelCloseFuture closeFuture = new ChannelCloseFuture();
68      private volatile int interestOps = OP_READ;
69  
70      /** Cache for the string representation of this channel */
71      private boolean strValConnected;
72      private String strVal;
73  
74      /**
75       * Creates a new instance.
76       *
77       * @param parent
78       *        the parent of this channel. {@code null} if there's no parent.
79       * @param factory
80       *        the factory which created this channel
81       * @param pipeline
82       *        the pipeline which is going to be attached to this channel
83       * @param sink
84       *        the sink which will receive downstream events from the pipeline
85       *        and send upstream events to the pipeline
86       */
87      protected AbstractChannel(
88              Channel parent, ChannelFactory factory,
89              ChannelPipeline pipeline, ChannelSink sink) {
90  
91          this.parent = parent;
92          this.factory = factory;
93          this.pipeline = pipeline;
94  
95          id = allocateId(this);
96          closeFuture.addListener(ID_DEALLOCATOR);
97  
98          pipeline.attach(this, sink);
99      }
100 
101     /**
102      * (Internal use only) Creates a new temporary instance with the specified
103      * ID.
104      *
105      * @param parent
106      *        the parent of this channel. {@code null} if there's no parent.
107      * @param factory
108      *        the factory which created this channel
109      * @param pipeline
110      *        the pipeline which is going to be attached to this channel
111      * @param sink
112      *        the sink which will receive downstream events from the pipeline
113      *        and send upstream events to the pipeline
114      */
115     protected AbstractChannel(
116             Integer id,
117             Channel parent, ChannelFactory factory,
118             ChannelPipeline pipeline, ChannelSink sink) {
119 
120         this.id = id;
121         this.parent = parent;
122         this.factory = factory;
123         this.pipeline = pipeline;
124         pipeline.attach(this, sink);
125     }
126 
127     public final Integer getId() {
128         return id;
129     }
130 
131     public Channel getParent() {
132         return parent;
133     }
134 
135     public ChannelFactory getFactory() {
136         return factory;
137     }
138 
139     public ChannelPipeline getPipeline() {
140         return pipeline;
141     }
142 
143     /**
144      * Returns the cached {@link SucceededChannelFuture} instance.
145      */
146     protected ChannelFuture getSucceededFuture() {
147         return succeededFuture;
148     }
149 
150     /**
151      * Returns the {@link FailedChannelFuture} whose cause is an
152      * {@link UnsupportedOperationException}.
153      */
154     protected ChannelFuture getUnsupportedOperationFuture() {
155         return new FailedChannelFuture(this, new UnsupportedOperationException());
156     }
157 
158     /**
159      * Returns the {@linkplain System#identityHashCode(Object) identity hash code}
160      * of this channel.
161      */
162     @Override
163     public final int hashCode() {
164         return System.identityHashCode(this);
165     }
166 
167     /**
168      * Returns {@code true} if and only if the specified object is identical
169      * with this channel (i.e: {@code this == o}).
170      */
171     @Override
172     public final boolean equals(Object o) {
173         return this == o;
174     }
175 
176     /**
177      * Compares the {@linkplain #getId() ID} of the two channels.
178      */
179     public final int compareTo(Channel o) {
180         return getId().compareTo(o.getId());
181     }
182 
183     public boolean isOpen() {
184         return !closeFuture.isDone();
185     }
186 
187     /**
188      * Marks this channel as closed.  This method is intended to be called by
189      * an internal component - please do not call it unless you know what you
190      * are doing.
191      *
192      * @return {@code true} if and only if this channel was not marked as
193      *                      closed yet
194      */
195     protected boolean setClosed() {
196         return closeFuture.setClosed();
197     }
198 
199     public ChannelFuture bind(SocketAddress localAddress) {
200         return Channels.bind(this, localAddress);
201     }
202 
203     public ChannelFuture unbind() {
204         return Channels.unbind(this);
205     }
206 
207     public ChannelFuture close() {
208         ChannelFuture returnedCloseFuture = Channels.close(this);
209         assert closeFuture == returnedCloseFuture;
210         return closeFuture;
211     }
212 
213     public ChannelFuture getCloseFuture() {
214         return closeFuture;
215     }
216 
217     public ChannelFuture connect(SocketAddress remoteAddress) {
218         return Channels.connect(this, remoteAddress);
219     }
220 
221     public ChannelFuture disconnect() {
222         return Channels.disconnect(this);
223     }
224 
225     public int getInterestOps() {
226         return interestOps;
227     }
228 
229     public ChannelFuture setInterestOps(int interestOps) {
230         return Channels.setInterestOps(this, interestOps);
231     }
232 
233     /**
234      * Sets the {@link #getInterestOps() interestOps} property of this channel
235      * immediately.  This method is intended to be called by an internal
236      * component - please do not call it unless you know what you are doing.
237      */
238     protected void setInterestOpsNow(int interestOps) {
239         this.interestOps = interestOps;
240     }
241 
242     public boolean isReadable() {
243         return (getInterestOps() & OP_READ) != 0;
244     }
245 
246     public boolean isWritable() {
247         return (getInterestOps() & OP_WRITE) == 0;
248     }
249 
250     public ChannelFuture setReadable(boolean readable) {
251         if (readable) {
252             return setInterestOps(getInterestOps() | OP_READ);
253         } else {
254             return setInterestOps(getInterestOps() & ~OP_READ);
255         }
256     }
257 
258     public ChannelFuture write(Object message) {
259         return Channels.write(this, message);
260     }
261 
262     public ChannelFuture write(Object message, SocketAddress remoteAddress) {
263         return Channels.write(this, message, remoteAddress);
264     }
265 
266     /**
267      * Returns the {@link String} representation of this channel.  The returned
268      * string contains the {@linkplain #getId() ID}, {@linkplain #getLocalAddress() local address},
269      * and {@linkplain #getRemoteAddress() remote address} of this channel for
270      * easier identification.
271      */
272     @Override
273     public String toString() {
274         boolean connected = isConnected();
275         if (strValConnected == connected && strVal != null) {
276             return strVal;
277         }
278 
279         StringBuilder buf = new StringBuilder(128);
280         buf.append("[id: 0x");
281         buf.append(getIdString());
282 
283         SocketAddress localAddress = getLocalAddress();
284         SocketAddress remoteAddress = getRemoteAddress();
285         if (remoteAddress != null) {
286             buf.append(", ");
287             if (getParent() == null) {
288                 buf.append(localAddress);
289                 buf.append(connected? " => " : " :> ");
290                 buf.append(remoteAddress);
291             } else {
292                 buf.append(remoteAddress);
293                 buf.append(connected? " => " : " :> ");
294                 buf.append(localAddress);
295             }
296         } else if (localAddress != null) {
297             buf.append(", ");
298             buf.append(localAddress);
299         }
300 
301         buf.append(']');
302 
303         String strVal = buf.toString();
304         this.strVal = strVal;
305         strValConnected = connected;
306         return strVal;
307     }
308 
309     private String getIdString() {
310         String answer = Integer.toHexString(id.intValue());
311         switch (answer.length()) {
312         case 0:
313             answer = "00000000";
314             break;
315         case 1:
316             answer = "0000000" + answer;
317             break;
318         case 2:
319             answer = "000000" + answer;
320             break;
321         case 3:
322             answer = "00000" + answer;
323             break;
324         case 4:
325             answer = "0000" + answer;
326             break;
327         case 5:
328             answer = "000" + answer;
329             break;
330         case 6:
331             answer = "00" + answer;
332             break;
333         case 7:
334             answer = "0" + answer;
335             break;
336         }
337         return answer;
338     }
339 
340     private final class ChannelCloseFuture extends DefaultChannelFuture {
341 
342         public ChannelCloseFuture() {
343             super(AbstractChannel.this, false);
344         }
345 
346         @Override
347         public boolean setSuccess() {
348             // User is not supposed to call this method - ignore silently.
349             return false;
350         }
351 
352         @Override
353         public boolean setFailure(Throwable cause) {
354             // User is not supposed to call this method - ignore silently.
355             return false;
356         }
357 
358         boolean setClosed() {
359             return super.setSuccess();
360         }
361     }
362 }