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