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.util.concurrent.ConcurrentMap;
19  
20  import org.jboss.netty.util.internal.ConcurrentIdentityWeakKeyHashMap;
21  
22  /**
23   * A global variable that is local to a {@link Channel}.  Think of this as a
24   * variation of {@link ThreadLocal} whose key is a {@link Channel} rather than
25   * a {@link Thread#currentThread()}.  One difference is that you always have to
26   * specify the {@link Channel} to access the variable.
27   * <p>
28   * Alternatively, you might want to use the
29   * {@link ChannelHandlerContext#setAttachment(Object) ChannelHandlerContext.attachment}
30   * property, which performs better.
31   *
32   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
33   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
34   * @version $Rev: 2080 $, $Date: 2010-01-26 18:04:19 +0900 (Tue, 26 Jan 2010) $
35   *
36   * @apiviz.stereotype utility
37   */
38  public class ChannelLocal<T> {
39  
40      private final ConcurrentMap<Channel, T> map =
41          new ConcurrentIdentityWeakKeyHashMap<Channel, T>();
42  
43      /**
44       * Creates a {@link Channel} local variable.
45       */
46      public ChannelLocal() {
47          super();
48      }
49  
50      /**
51       * Returns the initial value of the variable.  By default, it returns
52       * {@code null}.  Override it to change the initial value.
53       */
54      protected T initialValue(Channel channel) {
55          return null;
56      }
57  
58      /**
59       * Returns the value of this variable.
60       */
61      public T get(Channel channel) {
62          if (channel == null) {
63              throw new NullPointerException("channel");
64          }
65  
66          T value = map.get(channel);
67          if (value == null) {
68              value = initialValue(channel);
69              if (value != null) {
70                  T oldValue = setIfAbsent(channel, value);
71                  if (oldValue != null) {
72                      value = oldValue;
73                  }
74              }
75          }
76          return value;
77      }
78  
79      /**
80       * Sets the value of this variable.
81       *
82       * @return the old value. {@code null} if there was no old value.
83       */
84      public T set(Channel channel, T value) {
85          if (value == null) {
86              return remove(channel);
87          } else {
88              if (channel == null) {
89                  throw new NullPointerException("channel");
90              }
91              return map.put(channel, value);
92          }
93      }
94  
95      /**
96       * Sets the value of this variable only when no value was set.
97       *
98       * @return {@code null} if the specified value was set.
99       *         An existing value if failed to set.
100      */
101     public T setIfAbsent(Channel channel, T value) {
102         if (value == null) {
103             return get(channel);
104         } else {
105             if (channel == null) {
106                 throw new NullPointerException("channel");
107             }
108             return map.putIfAbsent(channel, value);
109         }
110     }
111 
112     /**
113      * Removes the variable and returns the removed value.  If no value was set,
114      * this method returns the return value of {@link #initialValue(Channel)},
115      * which is {@code null} by default.
116      *
117      * @return the removed value.
118      *         {@linkplain #initialValue(Channel) an initial value} (by default
119      *         {@code null}) if no value was set.
120      */
121     public T remove(Channel channel) {
122         if (channel == null) {
123             throw new NullPointerException("channel");
124         }
125         T removed = map.remove(channel);
126         if (removed == null) {
127             return initialValue(channel);
128         } else {
129             return removed;
130         }
131     }
132 }