001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.util; 021 022import java.util.Collection; 023import java.util.Map; 024import java.util.Set; 025import java.util.concurrent.ConcurrentHashMap; 026import java.util.concurrent.ConcurrentMap; 027 028import org.apache.mina.core.buffer.IoBuffer; 029 030/** 031 * This map is specially useful when reads are much more frequent than writes and 032 * if the cost of instantiating the values is high like allocating an 033 * {@link IoBuffer} for example. 034 * 035 * Based on the final implementation of Memoizer written by Brian Goetz and Tim 036 * Peierls. This implementation will return an 037 * {@link UnsupportedOperationException} on each method that is not intended to 038 * be called by user code for performance reasons. 039 * 040 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 041 * @since MINA 2.0.0-M2 042 */ 043public class LazyInitializedCacheMap<K, V> implements Map<K, V> { 044 private ConcurrentMap<K, LazyInitializer<V>> cache; 045 046 /** 047 * This class provides a noop {@link LazyInitializer} meaning it 048 * will return the same object it received when instantiated. 049 */ 050 public class NoopInitializer extends LazyInitializer<V> { 051 private V value; 052 053 public NoopInitializer(V value) { 054 this.value = value; 055 } 056 057 public V init() { 058 return value; 059 } 060 } 061 062 /** 063 * Default constructor. Uses the default parameters to initialize its internal 064 * {@link ConcurrentHashMap}. 065 */ 066 public LazyInitializedCacheMap() { 067 this.cache = new ConcurrentHashMap<K, LazyInitializer<V>>(); 068 } 069 070 /** 071 * This constructor allows to provide a fine tuned {@link ConcurrentHashMap} 072 * to stick with each special case the user needs. 073 * 074 * @param map The map to use as a cache 075 */ 076 public LazyInitializedCacheMap(final ConcurrentHashMap<K, LazyInitializer<V>> map) { 077 this.cache = map; 078 } 079 080 /** 081 * {@inheritDoc} 082 */ 083 public V get(Object key) { 084 LazyInitializer<V> c = cache.get(key); 085 if (c != null) { 086 return c.get(); 087 } 088 089 return null; 090 } 091 092 /** 093 * {@inheritDoc} 094 */ 095 public V remove(Object key) { 096 LazyInitializer<V> c = cache.remove(key); 097 if (c != null) { 098 return c.get(); 099 } 100 101 return null; 102 } 103 104 /** 105 * If the specified key is not already associated 106 * with a value, associate it with the given value. 107 * This is equivalent to 108 * <pre> 109 * if (!map.containsKey(key)) 110 * return map.put(key, value); 111 * else 112 * return map.get(key);</pre> 113 * except that the action is performed atomically. 114 * 115 * @param key key with which the specified value is to be associated 116 * @param value a lazy initialized value object. 117 * 118 * @return the previous value associated with the specified key, 119 * or <tt>null</tt> if there was no mapping for the key 120 */ 121 public V putIfAbsent(K key, LazyInitializer<V> value) { 122 LazyInitializer<V> v = cache.get(key); 123 if (v == null) { 124 v = cache.putIfAbsent(key, value); 125 if (v == null) { 126 return value.get(); 127 } 128 } 129 130 return v.get(); 131 } 132 133 /** 134 * {@inheritDoc} 135 */ 136 public V put(K key, V value) { 137 LazyInitializer<V> c = cache.put(key, new NoopInitializer(value)); 138 if (c != null) { 139 return c.get(); 140 } 141 142 return null; 143 } 144 145 /** 146 * Throws {@link UnsupportedOperationException} as this method would imply 147 * performance drops. 148 */ 149 public boolean containsValue(Object value) { 150 throw new UnsupportedOperationException(); 151 } 152 153 /** 154 * Throws {@link UnsupportedOperationException} as this method would imply 155 * performance drops. 156 */ 157 public Collection<V> values() { 158 throw new UnsupportedOperationException(); 159 } 160 161 /** 162 * Throws {@link UnsupportedOperationException} as this method would imply 163 * performance drops. 164 */ 165 public Set<java.util.Map.Entry<K, V>> entrySet() { 166 throw new UnsupportedOperationException(); 167 } 168 169 /** 170 * {@inheritDoc} 171 */ 172 public void putAll(Map<? extends K, ? extends V> m) { 173 for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) { 174 cache.put(e.getKey(), new NoopInitializer(e.getValue())); 175 } 176 } 177 178 /** 179 * @return return the values from the cache 180 */ 181 public Collection<LazyInitializer<V>> getValues() { 182 return cache.values(); 183 } 184 185 /** 186 * {@inheritDoc} 187 */ 188 public void clear() { 189 cache.clear(); 190 } 191 192 /** 193 * {@inheritDoc} 194 */ 195 public boolean containsKey(Object key) { 196 return cache.containsKey(key); 197 } 198 199 /** 200 * {@inheritDoc} 201 */ 202 public boolean isEmpty() { 203 return cache.isEmpty(); 204 } 205 206 /** 207 * {@inheritDoc} 208 */ 209 public Set<K> keySet() { 210 return cache.keySet(); 211 } 212 213 /** 214 * {@inheritDoc} 215 */ 216 public int size() { 217 return cache.size(); 218 } 219}