1 package org.apache.ojb.broker.cache;
2
3 /* Copyright 2004-2005 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 import java.lang.ref.ReferenceQueue;
19 import java.lang.ref.SoftReference;
20 import java.util.HashMap;
21 import java.util.Properties;
22
23 import org.apache.commons.collections.LRUMap;
24 import org.apache.ojb.broker.Identity;
25 import org.apache.ojb.broker.PersistenceBroker;
26 import org.apache.ojb.broker.util.configuration.Configurable;
27 import org.apache.ojb.broker.util.configuration.Configuration;
28 import org.apache.ojb.broker.util.configuration.ConfigurationException;
29 import org.apache.ojb.broker.util.configuration.impl.OjbConfigurator;
30
31 /**
32 * A global {@link ObjectCache} implementation.
33 *
34 * @author matthew.baird
35 * @version $Id: ObjectCacheSoftImpl.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
36 */
37 public class ObjectCacheSoftImpl implements ObjectCache, Configurable
38 {
39 /**
40 * The static the cache map
41 */
42 private static SoftHashMap cache = null;
43
44 /**
45 * The size of the cache
46 */
47 private static int size = 10000;
48
49 /**
50 * Constructor called by ojb
51 *
52 * @param broker ignored parameter
53 * @param properties ignored parameter
54 */
55 public ObjectCacheSoftImpl(PersistenceBroker broker, Properties properties)
56 {
57 if (cache == null)
58 {
59 OjbConfigurator.getInstance().configure(this);
60 cache = new SoftHashMap(size);
61 }
62 }
63
64 /**
65 * @see org.apache.ojb.broker.util.configuration.Configurable#configure(org.apache.ojb.broker.util.configuration.Configuration)
66 */
67 public void configure(Configuration configuration) throws ConfigurationException
68 {
69 size = configuration.getInteger("ObjectCacheSoftImpl", size);
70 }
71
72 /**
73 * @see org.apache.ojb.broker.cache.ObjectCache#cache(org.apache.ojb.broker.Identity, java.lang.Object)
74 */
75 public void cache(Identity oid, Object obj)
76 {
77 synchronized(cache)
78 {
79 cache.put(oid, obj);
80 }
81 }
82
83 public boolean cacheIfNew(Identity oid, Object obj)
84 {
85 synchronized(cache)
86 {
87 if(cache.get(oid) == null)
88 {
89 cache.put(oid, obj);
90 return true;
91 }
92 return false;
93 }
94 }
95
96 /**
97 * @see org.apache.ojb.broker.cache.ObjectCache#lookup(org.apache.ojb.broker.Identity)
98 */
99 public Object lookup(Identity oid)
100 {
101 return cache.get(oid);
102 }
103
104 /**
105 * @see org.apache.ojb.broker.cache.ObjectCache#remove(org.apache.ojb.broker.Identity)
106 */
107 public void remove(Identity oid)
108 {
109 synchronized(cache)
110 {
111 cache.remove(oid);
112 }
113 }
114
115 /**
116 * @see org.apache.ojb.broker.cache.ObjectCache#clear()
117 */
118 public void clear()
119 {
120 cache.clear();
121 }
122
123 /**
124 * Kind of map using SoftReference to store values
125 */
126 public static final class SoftHashMap
127 {
128 /**
129 * The internal HashMap that will hold the SoftReference.
130 */
131 private HashMap hash;
132 /**
133 * The FIFO list of hard references, order of last access.
134 */
135 private LRUMap hardCacheMap;
136 /**
137 * Reference queue for cleared SoftReference objects.
138 */
139 private ReferenceQueue queue;
140
141 /**
142 * Construct a new hash map with the specified size
143 *
144 * @param hardSize the maximum capacity of this map
145 */
146 public SoftHashMap(final int hardSize)
147 {
148 hash = new HashMap();
149 hardCacheMap = new LRUMap(hardSize);
150 queue = new ReferenceQueue();
151 }
152
153 /**
154 * Put the key, value pair into the HashMap using a SoftValue object
155 *
156 * @param key the key
157 * @param value the value
158 * @return the old value
159 */
160 public Object put(Object key, Object value)
161 {
162 //check null since hashtable doesn't support null key or null value
163 if (key == null || value == null)
164 {
165 return null;
166 }
167 processQueue(); // throw out garbage collected values first
168 hardCacheMap.put(key, value);
169 return hash.put(key, new SoftValue(value, key, queue));
170 }
171
172 /**
173 * Retrieve the value associated to a given key
174 *
175 * @param key the key
176 * @return the value associated to this key
177 */
178 public Object get(Object key)
179 {
180 // Check null since Hashtable doesn't support null key or null value
181 if (key == null)
182 {
183 return null;
184 }
185 Object result = null;
186 // We get the SoftReference represented by that key
187 SoftReference softRef = (SoftReference) hash.get(key);
188 if (softRef != null)
189 {
190 result = softRef.get();
191 if (result == null)
192 {
193 // If the value has been garbage collected, remove the
194 // entry from the HashMap.
195 hash.remove(key);
196 }
197 else
198 {
199 if (!hardCacheMap.containsKey(key))
200 {
201 hardCacheMap.put(key, result);
202 }
203 else
204 {
205 hardCacheMap.get(key);
206 }
207 }
208 }
209 return result;
210 }
211
212 /**
213 * Remove the entry for this key
214 *
215 * @param key the key
216 * @return the old value
217 */
218 public Object remove(Object key)
219 {
220 processQueue(); // throw out garbage collected values first
221 Object retval = null;
222 Object value = hash.remove(key);
223 if (value != null)
224 {
225 if (value instanceof SoftValue)
226 {
227 retval = ((SoftValue) value).get();
228 }
229 }
230 return retval;
231 }
232
233 /**
234 * Clear the map
235 */
236 public void clear()
237 {
238 processQueue();
239 hash.clear();
240 hardCacheMap.clear();
241 }
242
243 /**
244 * Class derived from SoftReference, used to
245 * store the key of the map.
246 */
247 private class SoftValue extends SoftReference
248 {
249 /**
250 * the key
251 */
252 private final Object key; // always make data member final
253
254 /**
255 * Create a SoftValue given the object, key and queue
256 *
257 * @param k the object
258 * @param key the key
259 * @param q the reference queue
260 */
261 private SoftValue(final Object k, final Object key, final ReferenceQueue q)
262 {
263 super(k, q);
264 this.key = key;
265 }
266 }
267
268 /**
269 * Removes keys and objects that have been garbaged
270 */
271 private void processQueue()
272 {
273 SoftValue sv;
274 while ((sv = (SoftValue) queue.poll()) != null)
275 {
276 hash.remove(sv.key); // we can access private data!
277 }
278 }
279
280 }
281 }