KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > excalibur > store > impl > MRUMemoryStore


1 /*
2  * Copyright 2002-2004 The Apache Software Foundation
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  *
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.excalibur.store.impl;
18
19 import java.io.IOException;
20 import java.util.Enumeration;
21 import java.util.Hashtable;
22 import java.util.LinkedList;
23 import java.util.NoSuchElementException;
24
25 import org.apache.avalon.framework.activity.Disposable;
26 import org.apache.avalon.framework.logger.AbstractLogEnabled;
27 import org.apache.avalon.framework.parameters.ParameterException;
28 import org.apache.avalon.framework.parameters.Parameterizable;
29 import org.apache.avalon.framework.parameters.Parameters;
30 import org.apache.avalon.framework.service.ServiceException;
31 import org.apache.avalon.framework.service.ServiceManager;
32 import org.apache.avalon.framework.service.Serviceable;
33 import org.apache.avalon.framework.thread.ThreadSafe;
34 import org.apache.excalibur.instrument.CounterInstrument;
35 import org.apache.excalibur.instrument.Instrument;
36 import org.apache.excalibur.instrument.Instrumentable;
37 import org.apache.excalibur.instrument.ValueInstrument;
38 import org.apache.excalibur.store.Store;
39 import org.apache.excalibur.store.StoreJanitor;
40
41 /**
42  * This class provides a cache algorithm for the requested documents.
43  * It combines a HashMap and a LinkedList to create a so called MRU
44  * (Most Recently Used) cache.
45  *
46  * @avalon.component
47  * @avalon.service type=Store
48  * @x-avalon.info name=mru-store
49  * @x-avalon.lifestyle type=singleton
50  *
51  * @author <a HREF="mailto:dev@avalon.apache.org">Avalon Development Team</a>
52  * @version CVS $Id: MRUMemoryStore.java,v 1.5 2004/02/28 11:47:31 cziegeler Exp $
53  */

54 public class MRUMemoryStore
55     extends AbstractLogEnabled
56     implements Store, Parameterizable, Serviceable, Disposable, ThreadSafe, Instrumentable
57 {
58     private String m_instrumentableName;
59     private int m_maxobjects;
60     private boolean m_persistent;
61     private Hashtable m_cache;
62     private LinkedList m_mrulist;
63     private Store m_persistentStore;
64     private StoreJanitor m_storeJanitor;
65     private ServiceManager m_manager;
66     
67     private ValueInstrument m_sizeInstrument = new ValueInstrument("size");
68     private CounterInstrument m_hitsInstrument = new CounterInstrument("hits");
69     private CounterInstrument m_missesInstrument = new CounterInstrument("misses");
70
71     /**
72      * Get components of the ComponentLocator
73      *
74      * @param manager The ComponentLocator
75      * @avalon.dependency type=org.apache.excalibur.store.Store
76      * @avalon.dependency type=org.apache.excalibur.store.StoreJanitor
77      */

78     public void service( ServiceManager manager )
79         throws ServiceException
80     {
81         m_manager = manager;
82         if( getLogger().isDebugEnabled() )
83         {
84             getLogger().debug( "Looking up " + StoreJanitor.ROLE );
85         }
86         m_storeJanitor = (StoreJanitor)manager.lookup( StoreJanitor.ROLE );
87     }
88
89     /**
90      * Initialize the MRUMemoryStore.
91      * A few options can be used:
92      * <UL>
93      * <LI>maxobjects: Maximum number of objects stored in memory (Default: 100 objects)</LI>
94      * <LI>use-persistent-cache: Use persistent cache to keep objects persisted after
95      * container shutdown or not (Default: false)</LI>
96      * </UL>
97      *
98      * @param params Store parameters
99      * @exception ParameterException
100      */

101     public void parameterize( Parameters params ) throws ParameterException
102     {
103         m_maxobjects = params.getParameterAsInteger( "maxobjects", 100 );
104         m_persistent = params.getParameterAsBoolean( "use-persistent-cache", false );
105         if( ( m_maxobjects < 1 ) )
106         {
107             throw new ParameterException( "MRUMemoryStore maxobjects must be at least 1!" );
108         }
109
110         if ( m_persistent )
111         {
112             if( getLogger().isDebugEnabled() )
113             {
114                 getLogger().debug( "Looking up " + Store.PERSISTENT_STORE );
115             }
116             try
117             {
118                 m_persistentStore = (Store)m_manager.lookup( Store.PERSISTENT_STORE );
119             }
120             catch (ServiceException se)
121             {
122                 throw new ParameterException("Unable to look up persistent store.", se);
123             }
124         }
125
126         m_cache = new Hashtable( (int)( m_maxobjects * 1.2 ) );
127         m_mrulist = new LinkedList();
128         m_storeJanitor.register( this );
129     }
130
131     /**
132      * Dispose the component
133      */

134     public void dispose()
135     {
136         if( m_manager != null )
137         {
138             getLogger().debug( "Disposing component!" );
139
140             if( m_storeJanitor != null )
141             {
142                 m_storeJanitor.unregister( this );
143             }
144             m_manager.release( m_storeJanitor );
145             m_storeJanitor = null;
146
147             // save all cache entries to filesystem
148
if( m_persistent )
149             {
150                 getLogger().debug( "Final cache size: " + m_cache.size() );
151                 Enumeration enum = m_cache.keys();
152                 while( enum.hasMoreElements() )
153                 {
154                     Object key = enum.nextElement();
155                     if( key == null )
156                     {
157                         continue;
158                     }
159                     try
160                     {
161                         Object value = m_cache.remove( key );
162                         if( checkSerializable( value ) )
163                         {
164                             m_persistentStore.store( key, value );
165                         }
166                     }
167                     catch( IOException ioe )
168                     {
169                         getLogger().error( "Error in dispose()", ioe );
170                     }
171                 }
172             }
173             m_manager.release( m_persistentStore );
174             m_persistentStore = null;
175         }
176
177         m_manager = null;
178     }
179
180     /**
181      * Store the given object in a persistent state. It is up to the
182      * caller to ensure that the key has a persistent state across
183      * different JVM executions.
184      *
185      * @param key The key for the object to store
186      * @param value The object to store
187      */

188     public synchronized void store( Object key, Object value )
189     {
190         hold( key, value );
191     }
192
193     /**
194      * This method holds the requested object in a HashMap combined
195      * with a LinkedList to create the MRU.
196      * It also stores objects onto the filesystem if configured.
197      *
198      * @param key The key of the object to be stored
199      * @param value The object to be stored
200      */

201     public synchronized void hold( Object key, Object value )
202     {
203         if( getLogger().isDebugEnabled() )
204         {
205             getLogger().debug( "Holding object in memory:" );
206             getLogger().debug( " key: " + key );
207             getLogger().debug( " value: " + value );
208         }
209         /** ...first test if the max. objects in cache is reached... */
210         while( m_mrulist.size() >= m_maxobjects )
211         {
212             /** ...ok, heapsize is reached, remove the last element... */
213             free();
214         }
215         /** ..put the new object in the cache, on the top of course ... */
216         m_cache.put( key, value );
217         m_mrulist.remove( key );
218         m_mrulist.addFirst( key );
219         m_sizeInstrument.setValue( m_mrulist.size() );
220     }
221
222     /**
223      * Get the object associated to the given unique key.
224      *
225      * @param key The key of the requested object
226      * @return the requested object
227      */

228     public synchronized Object get( Object key )
229     {
230         Object value = m_cache.get( key );
231         if( value != null )
232         {
233             /** put the accessed key on top of the linked list */
234             m_mrulist.remove( key );
235             m_mrulist.addFirst( key );
236             if( getLogger().isDebugEnabled() )
237             {
238                 getLogger().debug( "Found key: " + key.toString() );
239             }
240             m_hitsInstrument.increment();
241             return value;
242         }
243
244         if( getLogger().isDebugEnabled() )
245         {
246             getLogger().debug( "NOT Found key: " + key.toString() );
247         }
248
249         /** try to fetch from filesystem */
250         if( m_persistent )
251         {
252             value = m_persistentStore.get( key );
253             if( value != null )
254             {
255                 try
256                 {
257                     if( !m_cache.containsKey( key ) )
258                     {
259                         hold( key, value );
260                     }
261                     m_hitsInstrument.increment();
262                     return value;
263                 }
264                 catch( Exception e )
265                 {
266                     getLogger().error( "Error in get()!", e );
267                 }
268             }
269         }
270         m_missesInstrument.increment();
271         return null;
272     }
273
274     /**
275      * Remove the object associated to the given key.
276      *
277      * @param key The key of to be removed object
278      */

279     public synchronized void remove( Object key )
280     {
281         if( getLogger().isDebugEnabled() )
282         {
283             getLogger().debug( "Removing object from store" );
284             getLogger().debug( " key: " + key );
285         }
286         m_cache.remove( key );
287         m_mrulist.remove( key );
288         m_sizeInstrument.setValue( m_mrulist.size() );
289         
290         if( m_persistent && key != null )
291         {
292             m_persistentStore.remove( key );
293         }
294     }
295
296     /**
297      * Clear the Store of all elements
298      */

299     public synchronized void clear()
300     {
301         Enumeration enum = m_cache.keys();
302         while( enum.hasMoreElements() )
303         {
304             Object key = enum.nextElement();
305             if( key == null )
306             {
307                 continue;
308             }
309             remove( key );
310         }
311         m_sizeInstrument.setValue( 0 );
312     }
313
314     /**
315      * Indicates if the given key is associated to a contained object.
316      *
317      * @param key The key of the object
318      * @return true if the key exists
319      */

320     public synchronized boolean containsKey( Object key )
321     {
322         if( m_persistent )
323         {
324             return ( m_cache.containsKey( key ) || m_persistentStore.containsKey( key ) );
325         }
326         else
327         {
328             return m_cache.containsKey( key );
329         }
330     }
331
332     /**
333      * Returns the list of used keys as an Enumeration.
334      *
335      * @return the enumeration of the cache
336      */

337     public synchronized Enumeration keys()
338     {
339         return m_cache.keys();
340     }
341
342     /**
343      * Returns count of the objects in the store, or -1 if could not be
344      * obtained.
345      */

346     public synchronized int size()
347     {
348         return m_cache.size();
349     }
350
351     /**
352      * Frees some of the fast memory used by this store.
353      * It removes the last element in the store.
354      */

355     public synchronized void free()
356     {
357         try
358         {
359             if( m_cache.size() > 0 )
360             {
361                 // This can throw NoSuchElementException
362
Object key = m_mrulist.removeLast();
363                 Object value = m_cache.remove( key );
364                 if( value == null )
365                 {
366                     getLogger().warn( "Concurrency condition in free()" );
367                 }
368
369                 if( getLogger().isDebugEnabled() )
370                 {
371                     getLogger().debug( "Freeing cache." );
372                     getLogger().debug( " key: " + key );
373                     getLogger().debug( " value: " + value );
374                 }
375
376                 if( m_persistent )
377                 {
378                     // Swap object on fs.
379
if( checkSerializable( value ) )
380                     {
381                         try
382                         {
383                             m_persistentStore.store( key, value );
384                         }
385                         catch( Exception e )
386                         {
387                             getLogger().error( "Error storing object on fs", e );
388                         }
389                     }
390                 }
391                 
392                 m_sizeInstrument.setValue( m_mrulist.size() );
393             }
394         }
395         catch( NoSuchElementException e )
396         {
397             getLogger().warn( "Concurrency error in free()", e );
398         }
399         catch( Exception e )
400         {
401             getLogger().error( "Error in free()", e );
402         }
403     }
404
405     /**
406      * This method checks if an object is seriazable.
407      *
408      * @param object The object to be checked
409      * @return true if the object is storeable
410      */

411     private boolean checkSerializable( Object object )
412     {
413
414         if( object == null ) return false;
415
416         return ( object instanceof java.io.Serializable );
417     }
418
419     public void setInstrumentableName(String name)
420     {
421         m_instrumentableName = name;
422     }
423
424     public String getInstrumentableName()
425     {
426         return m_instrumentableName;
427     }
428
429     public Instrument[] getInstruments()
430     {
431         return new Instrument[] { m_sizeInstrument, m_hitsInstrument, m_missesInstrument };
432     }
433
434     public Instrumentable[] getChildInstrumentables() {
435         return Instrumentable.EMPTY_INSTRUMENTABLE_ARRAY;
436     }
437 }
438
439
Popular Tags