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 * <p/> 050 * Notice that if the JVM reclaim memory the content of this cache may be garbage collected, without any 051 * eviction notifications. 052 * 053 * @see LRUCache 054 * @see LRUWeakCache 055 */ 056 public class LRUSoftCache<K, V> extends LRUCache<K, V> { 057 private static final long serialVersionUID = 1L; 058 059 public LRUSoftCache(int maximumCacheSize) { 060 super(maximumCacheSize); 061 } 062 063 public LRUSoftCache(int initialCapacity, int maximumCacheSize) { 064 super(initialCapacity, maximumCacheSize); 065 } 066 067 public LRUSoftCache(int initialCapacity, int maximumCacheSize, boolean stopOnEviction) { 068 super(initialCapacity, maximumCacheSize, stopOnEviction); 069 } 070 071 @Override 072 @SuppressWarnings("unchecked") 073 public V put(K key, V value) { 074 SoftReference<V> put = new SoftReference<V>(value); 075 SoftReference<V> prev = (SoftReference<V>) super.put(key, (V) put); 076 return prev != null ? prev.get() : null; 077 } 078 079 @Override 080 @SuppressWarnings("unchecked") 081 public V get(Object o) { 082 SoftReference<V> ref = (SoftReference<V>) super.get(o); 083 return ref != null ? ref.get() : null; 084 } 085 086 @Override 087 public void putAll(Map<? extends K, ? extends V> map) { 088 for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) { 089 put(entry.getKey(), entry.getValue()); 090 } 091 } 092 093 @Override 094 @SuppressWarnings("unchecked") 095 public V remove(Object o) { 096 SoftReference<V> ref = (SoftReference<V>) super.remove(o); 097 return ref != null ? ref.get() : null; 098 } 099 100 @Override 101 @SuppressWarnings("unchecked") 102 public Collection<V> values() { 103 // return a copy of all the active values 104 Collection<SoftReference<V>> col = (Collection<SoftReference<V>>) super.values(); 105 Collection<V> answer = new ArrayList<V>(); 106 for (SoftReference<V> ref : col) { 107 V value = ref.get(); 108 if (value != null) { 109 answer.add(value); 110 } 111 } 112 return answer; 113 } 114 115 @Override 116 public int size() { 117 // only count as a size if there is a value 118 int size = 0; 119 for (V value : super.values()) { 120 SoftReference<?> ref = (SoftReference<?>) value; 121 if (ref != null && ref.get() != null) { 122 size++; 123 } 124 } 125 return size; 126 } 127 128 @Override 129 public boolean isEmpty() { 130 return size() == 0; 131 } 132 133 @Override 134 public boolean containsKey(Object o) { 135 // must lookup if the key has a value, as we only regard a key to be contained 136 // if the value is still there (the JVM can remove the soft reference if it need memory) 137 return get(o) != null; 138 } 139 140 @Override 141 public Set<Map.Entry<K, V>> entrySet() { 142 Set<Map.Entry<K, V>> original = super.entrySet(); 143 144 // must use a copy to avoid concurrent modifications and be able to get/set value using 145 // the soft reference so the returned set is without the soft reference, and thus is 146 // use able for the caller to use 147 Set<Map.Entry<K, V>> answer = new LinkedHashSet<Map.Entry<K, V>>(original.size()); 148 for (final Map.Entry<K, V> entry : original) { 149 Map.Entry<K, V> view = new Map.Entry<K, V>() { 150 @Override 151 public K getKey() { 152 return entry.getKey(); 153 } 154 155 @Override 156 @SuppressWarnings("unchecked") 157 public V getValue() { 158 SoftReference<V> ref = (SoftReference<V>) entry.getValue(); 159 return ref != null ? ref.get() : null; 160 } 161 162 @Override 163 @SuppressWarnings("unchecked") 164 public V setValue(V v) { 165 V put = (V) new SoftReference<V>(v); 166 SoftReference<V> prev = (SoftReference<V>) entry.setValue(put); 167 return prev != null ? prev.get() : null; 168 } 169 }; 170 answer.add(view); 171 } 172 173 return answer; 174 } 175 176 @Override 177 public String toString() { 178 return "LRUSoftCache@" + ObjectHelper.getIdentityHashCode(this); 179 } 180 }