1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 */
20 package org.apache.mina.util;
21
22 import java.util.Collection;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentMap;
27
28 import org.apache.mina.core.buffer.IoBuffer;
29
30 /**
31 * This map is specially useful when reads are much more frequent than writes and
32 * if the cost of instantiating the values is high like allocating an
33 * {@link IoBuffer} for example.
34 *
35 * Based on the final implementation of Memoizer written by Brian Goetz and Tim
36 * Peierls. This implementation will return an
37 * {@link UnsupportedOperationException} on each method that is not intended to
38 * be called by user code for performance reasons.
39 *
40 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
41 * @since MINA 2.0.0-M2
42 */
43 public class LazyInitializedCacheMap<K, V> implements Map<K, V> {
44 private ConcurrentMap<K, LazyInitializer<V>> cache;
45
46 /**
47 * This class provides a noop {@link LazyInitializer} meaning it
48 * will return the same object it received when instantiated.
49 */
50 public class NoopInitializer extends LazyInitializer<V> {
51 private V value;
52
53 public NoopInitializer(V value) {
54 this.value = value;
55 }
56
57 public V init() {
58 return value;
59 }
60 }
61
62 /**
63 * Default constructor. Uses the default parameters to initialize its internal
64 * {@link ConcurrentHashMap}.
65 */
66 public LazyInitializedCacheMap() {
67 this.cache = new ConcurrentHashMap<K, LazyInitializer<V>>();
68 }
69
70 /**
71 * This constructor allows to provide a fine tuned {@link ConcurrentHashMap}
72 * to stick with each special case the user needs.
73 */
74 public LazyInitializedCacheMap(final ConcurrentHashMap<K, LazyInitializer<V>> map) {
75 this.cache = map;
76 }
77
78 /**
79 * {@inheritDoc}
80 */
81 public V get(Object key) {
82 LazyInitializer<V> c = cache.get(key);
83 if (c != null) {
84 return c.get();
85 }
86
87 return null;
88 }
89
90 /**
91 * {@inheritDoc}
92 */
93 public V remove(Object key) {
94 LazyInitializer<V> c = cache.remove(key);
95 if (c != null) {
96 return c.get();
97 }
98
99 return null;
100 }
101
102 /**
103 * If the specified key is not already associated
104 * with a value, associate it with the given value.
105 * This is equivalent to
106 * <pre>
107 * if (!map.containsKey(key))
108 * return map.put(key, value);
109 * else
110 * return map.get(key);</pre>
111 * except that the action is performed atomically.
112 *
113 * @param key key with which the specified value is to be associated
114 * @param value a lazy initialized value object.
115 *
116 * @return the previous value associated with the specified key,
117 * or <tt>null</tt> if there was no mapping for the key
118 */
119 public V putIfAbsent(K key, LazyInitializer<V> value) {
120 LazyInitializer<V> v = cache.get(key);
121 if (v == null) {
122 v = cache.putIfAbsent(key, value);
123 if (v == null) {
124 return value.get();
125 }
126 }
127
128 return v.get();
129 }
130
131 /**
132 * {@inheritDoc}
133 */
134 public V put(K key, V value) {
135 LazyInitializer<V> c = cache.put(key, new NoopInitializer(value));
136 if (c != null) {
137 return c.get();
138 }
139
140 return null;
141 }
142
143 /**
144 * @throws {@link UnsupportedOperationException} as this method would imply
145 * performance drops.
146 */
147 public boolean containsValue(Object value) {
148 throw new UnsupportedOperationException();
149 }
150
151 /**
152 * @throws {@link UnsupportedOperationException} as this method would imply
153 * performance drops.
154 */
155 public Collection<V> values() {
156 throw new UnsupportedOperationException();
157 }
158
159 /**
160 * @throws {@link UnsupportedOperationException} as this method would imply
161 * performance drops.
162 */
163 public Set<java.util.Map.Entry<K, V>> entrySet() {
164 throw new UnsupportedOperationException();
165 }
166
167 /**
168 * {@inheritDoc}
169 */
170 public void putAll(Map<? extends K, ? extends V> m) {
171 for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
172 cache.put(e.getKey(), new NoopInitializer(e.getValue()));
173 }
174 }
175
176 /**
177 * {@inheritDoc}
178 */
179 public Collection<LazyInitializer<V>> getValues() {
180 return cache.values();
181 }
182
183 /**
184 * {@inheritDoc}
185 */
186 public void clear() {
187 cache.clear();
188 }
189
190 /**
191 * {@inheritDoc}
192 */
193 public boolean containsKey(Object key) {
194 return cache.containsKey(key);
195 }
196
197 /**
198 * {@inheritDoc}
199 */
200 public boolean isEmpty() {
201 return cache.isEmpty();
202 }
203
204 /**
205 * {@inheritDoc}
206 */
207 public Set<K> keySet() {
208 return cache.keySet();
209 }
210
211 /**
212 * {@inheritDoc}
213 */
214 public int size() {
215 return cache.size();
216 }
217 }