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