001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.util;
018
019 import java.lang.ref.SoftReference;
020 import java.util.ArrayList;
021 import java.util.Collection;
022 import java.util.LinkedHashSet;
023 import java.util.Map;
024 import java.util.Set;
025
026 /**
027 * A Least Recently Used Cache which uses {@link SoftReference}.
028 * <p/>
029 * This implementation uses {@link java.lang.ref.SoftReference} for stored values in the cache, to support the JVM
030 * when it wants to reclaim objects when it's running out of memory. Therefore this implementation does
031 * not support <b>all</b> the {@link java.util.Map} methods.
032 * <p/>
033 * The following methods is <b>only</b> be be used:
034 * <ul>
035 * <li>containsKey - To determine if the key is in the cache and refers to a value</li>
036 * <li>entrySet - To return a set of all the entries (as key/value paris)</li>
037 * <li>get - To get a value from the cache</li>
038 * <li>isEmpty - To determine if the cache contains any values</li>
039 * <li>keySet - To return a set of the current keys which refers to a value</li>
040 * <li>put - To add a value to the cache</li>
041 * <li>putAll - To add values to the cache</li>
042 * <li>remove - To remove a value from the cache by its key</li>
043 * <li>size - To get the current size</li>
044 * <li>values - To return a copy of all the value in a list</li>
045 * </ul>
046 * <p/>
047 * The {@link #containsValue(Object)} method should <b>not</b> be used as it's not adjusted to check
048 * for the existence of a value without catering for the soft references.
049 *
050 * @see LRUCache
051 */
052 public class LRUSoftCache<K, V> extends LRUCache<K, V> {
053 private static final long serialVersionUID = 1L;
054
055 public LRUSoftCache(int maximumCacheSize) {
056 super(maximumCacheSize);
057 }
058
059 public LRUSoftCache(int initialCapacity, int maximumCacheSize) {
060 super(initialCapacity, maximumCacheSize);
061 }
062
063 @Override
064 @SuppressWarnings("unchecked")
065 public V put(K key, V value) {
066 SoftReference<V> put = new SoftReference<V>(value);
067 SoftReference<V> prev = (SoftReference<V>) super.put(key, (V) put);
068 return prev != null ? prev.get() : null;
069 }
070
071 @Override
072 @SuppressWarnings("unchecked")
073 public V get(Object o) {
074 SoftReference<V> ref = (SoftReference<V>) super.get(o);
075 return ref != null ? ref.get() : null;
076 }
077
078 @Override
079 public void putAll(Map<? extends K, ? extends V> map) {
080 for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
081 put(entry.getKey(), entry.getValue());
082 }
083 }
084
085 @Override
086 @SuppressWarnings("unchecked")
087 public V remove(Object o) {
088 SoftReference<V> ref = (SoftReference<V>) super.remove(o);
089 return ref != null ? ref.get() : null;
090 }
091
092 @Override
093 @SuppressWarnings("unchecked")
094 public Collection<V> values() {
095 // return a copy of all the active values
096 Collection<SoftReference<V>> col = (Collection<SoftReference<V>>) super.values();
097 Collection<V> answer = new ArrayList<V>();
098 for (SoftReference<V> ref : col) {
099 V value = ref.get();
100 if (value != null) {
101 answer.add(value);
102 }
103 }
104 return answer;
105 }
106
107 @Override
108 public int size() {
109 // only count as a size if there is a value
110 int size = 0;
111 for (V value : super.values()) {
112 SoftReference<?> ref = (SoftReference<?>) value;
113 if (ref != null && ref.get() != null) {
114 size++;
115 }
116 }
117 return size;
118 }
119
120 @Override
121 public boolean isEmpty() {
122 return size() == 0;
123 }
124
125 @Override
126 public boolean containsKey(Object o) {
127 // must lookup if the key has a value, as we only regard a key to be contained
128 // if the value is still there (the JVM can remove the soft reference if it need memory)
129 return get(o) != null;
130 }
131
132 @Override
133 public Set<Map.Entry<K, V>> entrySet() {
134 Set<Map.Entry<K, V>> original = super.entrySet();
135
136 // must use a copy to avoid concurrent modifications and be able to get/set value using
137 // the soft reference so the returned set is without the soft reference, and thus is
138 // use able for the caller to use
139 Set<Map.Entry<K, V>> answer = new LinkedHashSet<Map.Entry<K, V>>(original.size());
140 for (final Map.Entry<K, V> entry : original) {
141 Map.Entry<K, V> view = new Map.Entry<K, V>() {
142 @Override
143 public K getKey() {
144 return entry.getKey();
145 }
146
147 @Override
148 @SuppressWarnings("unchecked")
149 public V getValue() {
150 SoftReference<V> ref = (SoftReference<V>) entry.getValue();
151 return ref != null ? ref.get() : null;
152 }
153
154 @Override
155 @SuppressWarnings("unchecked")
156 public V setValue(V v) {
157 V put = (V) new SoftReference<V>(v);
158 SoftReference<V> prev = (SoftReference<V>) entry.setValue(put);
159 return prev != null ? prev.get() : null;
160 }
161 };
162 answer.add(view);
163 }
164
165 return answer;
166 }
167
168 @Override
169 public String toString() {
170 return "LRUSoftCache@" + ObjectHelper.getIdentityHashCode(this);
171 }
172 }