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