001    package org.apache.ojb.broker.cache;
002    
003    /* Copyright 2004-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * 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    
018    import java.lang.ref.ReferenceQueue;
019    import java.lang.ref.SoftReference;
020    import java.util.HashMap;
021    import java.util.Properties;
022    
023    import org.apache.commons.collections.LRUMap;
024    import org.apache.ojb.broker.Identity;
025    import org.apache.ojb.broker.PersistenceBroker;
026    import org.apache.ojb.broker.util.configuration.Configurable;
027    import org.apache.ojb.broker.util.configuration.Configuration;
028    import org.apache.ojb.broker.util.configuration.ConfigurationException;
029    import org.apache.ojb.broker.util.configuration.impl.OjbConfigurator;
030    
031    /**
032     * A global {@link ObjectCache} implementation.
033     *
034     * @author matthew.baird
035     * @version $Id: ObjectCacheSoftImpl.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
036     */
037    public class ObjectCacheSoftImpl implements ObjectCache, Configurable
038    {
039        /**
040         * The static the cache map
041         */
042        private static SoftHashMap cache = null;
043    
044        /**
045         * The size of the cache
046         */
047        private static int size = 10000;
048    
049        /**
050         * Constructor called by ojb
051         *
052         * @param broker     ignored parameter
053         * @param properties ignored parameter
054         */
055        public ObjectCacheSoftImpl(PersistenceBroker broker, Properties properties)
056        {
057            if (cache == null)
058            {
059                OjbConfigurator.getInstance().configure(this);
060                cache = new SoftHashMap(size);
061            }
062        }
063    
064        /**
065         * @see org.apache.ojb.broker.util.configuration.Configurable#configure(org.apache.ojb.broker.util.configuration.Configuration)
066         */
067        public void configure(Configuration configuration) throws ConfigurationException
068        {
069            size = configuration.getInteger("ObjectCacheSoftImpl", size);
070        }
071    
072        /**
073         * @see org.apache.ojb.broker.cache.ObjectCache#cache(org.apache.ojb.broker.Identity, java.lang.Object)
074         */
075        public void cache(Identity oid, Object obj)
076        {
077            synchronized(cache)
078            {
079                cache.put(oid, obj);
080            }
081        }
082    
083        public boolean cacheIfNew(Identity oid, Object obj)
084        {
085            synchronized(cache)
086            {
087                if(cache.get(oid) == null)
088                {
089                    cache.put(oid, obj);
090                    return true;
091                }
092                return false;
093            }
094        }
095    
096        /**
097         * @see org.apache.ojb.broker.cache.ObjectCache#lookup(org.apache.ojb.broker.Identity)
098         */
099        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    }