View Javadoc

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 }