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