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