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