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 }