KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > store > MRUMemoryStore


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

16 package org.apache.cocoon.components.store;
17
18 import org.apache.avalon.framework.activity.Disposable;
19 import org.apache.avalon.framework.component.ComponentException;
20 import org.apache.avalon.framework.component.ComponentManager;
21 import org.apache.avalon.framework.component.Composable;
22 import org.apache.avalon.framework.logger.AbstractLogEnabled;
23 import org.apache.avalon.framework.parameters.Parameters;
24 import org.apache.avalon.framework.parameters.ParameterException;
25 import org.apache.avalon.framework.parameters.Parameterizable;
26 import org.apache.avalon.framework.thread.ThreadSafe;
27
28 import org.apache.cocoon.util.ClassUtils;
29 import org.apache.cocoon.util.MRUBucketMap;
30
31 import java.io.IOException JavaDoc;
32 import java.net.URLEncoder JavaDoc;
33 import java.util.Enumeration JavaDoc;
34 import java.util.NoSuchElementException JavaDoc;
35 import java.util.Iterator JavaDoc;
36 import java.util.Map JavaDoc;
37
38 /**
39  * This class provides a cache algorithm for the requested documents.
40  * It combines a HashMap and a LinkedList to create a so called MRU
41  * (Most Recently Used) cache.
42  *
43  * <p>This implementation is based on MRUBucketMap - map with efficient
44  * O(1) implementation of MRU removal policy.
45  *
46  * <p>TODO: Port improvments to the Excalibur implementation
47  *
48  * @deprecated Use the Avalon Excalibur Store instead.
49  *
50  * @author <a HREF="mailto:g-froehlich@gmx.de">Gerhard Froehlich</a>
51  * @author <a HREF="mailto:dims@yahoo.com">Davanum Srinivas</a>
52  * @author <a HREF="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
53  * @version CVS $Id: MRUMemoryStore.java 30932 2004-07-29 17:35:38Z vgritsenko $
54  */

55 public final class MRUMemoryStore extends AbstractLogEnabled
56     implements Store, Parameterizable, Composable, Disposable, ThreadSafe {
57
58     private int maxobjects;
59     private boolean persistent;
60     protected MRUBucketMap cache;
61     private Store persistentStore;
62     private StoreJanitor storeJanitor;
63     private ComponentManager manager;
64
65     /**
66      * Get components of the ComponentManager
67      *
68      * @param manager The ComponentManager
69      */

70     public void compose(ComponentManager manager) throws ComponentException {
71         this.manager = manager;
72         if (getLogger().isDebugEnabled()) {
73             getLogger().debug("Looking up " + Store.PERSISTENT_CACHE);
74             getLogger().debug("Looking up " + StoreJanitor.ROLE);
75         }
76         this.persistentStore = (Store)manager.lookup(Store.PERSISTENT_CACHE);
77         this.storeJanitor = (StoreJanitor)manager.lookup(StoreJanitor.ROLE);
78     }
79
80     /**
81      * Initialize the MRUMemoryStore.
82      * A few options can be used:
83      * <UL>
84      * <LI>maxobjects: Maximum number of objects stored in memory (Default: 100 objects)</LI>
85      * <LI>use-persistent-cache: Use persistent cache to keep objects persisted after
86      * container shutdown or not (Default: false)</LI>
87      * </UL>
88      *
89      * @param params Store parameters
90      * @exception ParameterException
91      */

92     public void parameterize(Parameters params) throws ParameterException {
93         this.maxobjects = params.getParameterAsInteger("maxobjects", 100);
94         this.persistent = params.getParameterAsBoolean("use-persistent-cache", false);
95         if ((this.maxobjects < 1)) {
96             throw new ParameterException("MRUMemoryStore maxobjects must be at least 1!");
97         }
98
99         this.cache = new MRUBucketMap((int)(this.maxobjects * 1.2));
100         this.storeJanitor.register(this);
101     }
102
103     /**
104      * Dispose the component
105      */

106     public void dispose() {
107         if (this.manager != null) {
108             getLogger().debug("Disposing component!");
109
110             if (this.storeJanitor != null)
111                 this.storeJanitor.unregister(this);
112             this.manager.release(this.storeJanitor);
113             this.storeJanitor = null;
114
115             // save all cache entries to filesystem
116
if (this.persistent) {
117                 if (getLogger().isDebugEnabled()) {
118                     getLogger().debug("Final cache size: " + this.cache.size());
119                 }
120                 for (Iterator JavaDoc i = this.cache.keySet().iterator(); i.hasNext(); ) {
121                     Object JavaDoc key = i.next();
122                     try {
123                         Object JavaDoc value = this.cache.remove(key);
124                         if(checkSerializable(value)) {
125                              persistentStore.store(getFileName(key.toString()),
126                                                    value);
127                         }
128                     } catch (IOException JavaDoc ioe) {
129                         getLogger().error("Error in dispose()", ioe);
130                     }
131                 }
132             }
133             this.manager.release(this.persistentStore);
134             this.persistentStore = null;
135         }
136
137         this.manager = null;
138     }
139
140     /**
141      * Store the given object in a persistent state. It is up to the
142      * caller to ensure that the key has a persistent state across
143      * different JVM executions.
144      *
145      * @param key The key for the object to store
146      * @param value The object to store
147      */

148     public void store(Object JavaDoc key, Object JavaDoc value) {
149         this.hold(key,value);
150     }
151
152     /**
153      * This method holds the requested object in a HashMap combined
154      * with a LinkedList to create the MRU.
155      * It also stores objects onto the filesystem if configured.
156      *
157      * @param key The key of the object to be stored
158      * @param value The object to be stored
159      */

160     public void hold(Object JavaDoc key, Object JavaDoc value) {
161         if (getLogger().isDebugEnabled()) {
162             getLogger().debug("Holding object in memory:");
163             getLogger().debug(" key: " + key);
164             getLogger().debug(" value: " + value);
165         }
166
167         /** ...first test if the max. objects in cache is reached... */
168         while (this.cache.size() >= this.maxobjects) {
169             /** ...ok, heapsize is reached, remove the last element... */
170             this.free();
171         }
172
173         /** ..put the new object in the cache, on the top of course ... */
174         this.cache.put(key, value);
175     }
176
177     /**
178      * Get the object associated to the given unique key.
179      *
180      * @param key The key of the requested object
181      * @return the requested object
182      */

183     public Object JavaDoc get(Object JavaDoc key) {
184         Object JavaDoc value = this.cache.get(key);
185         if (value != null) {
186             if (getLogger().isDebugEnabled()) {
187                 getLogger().debug("Found key: " + key.toString());
188             }
189             return value;
190         }
191
192         if (getLogger().isDebugEnabled()) {
193             getLogger().debug("NOT Found key: " + key.toString());
194         }
195
196         /** try to fetch from filesystem */
197         if (this.persistent) {
198             value = this.persistentStore.get(getFileName(key.toString()));
199             if (value != null) {
200                 try {
201                     this.hold(key, value);
202                     return value;
203                 } catch (Exception JavaDoc e) {
204                     getLogger().error("Error in hold()!", e);
205                     return null;
206                 }
207             }
208         }
209
210         return null;
211     }
212
213     /**
214      * Remove the object associated to the given key.
215      *
216      * @param key The key of to be removed object
217      */

218     public void remove(Object JavaDoc key) {
219         if (getLogger().isDebugEnabled()) {
220             getLogger().debug("Removing object from store");
221             getLogger().debug(" key: " + key);
222         }
223         this.cache.remove(key);
224         if(this.persistent && key != null) {
225             this.persistentStore.remove(getFileName(key.toString()));
226         }
227     }
228
229     /**
230      * Indicates if the given key is associated to a contained object.
231      *
232      * @param key The key of the object
233      * @return true if the key exists
234      */

235     public boolean containsKey(Object JavaDoc key) {
236         return cache.containsKey(key) || (persistent && persistentStore.containsKey(key));
237     }
238
239     /**
240      * Returns the list of used keys as an Enumeration.
241      *
242      * @return the enumeration of the cache
243      */

244     public Enumeration JavaDoc keys() {
245         return new Enumeration JavaDoc() {
246             private Iterator JavaDoc i = cache.keySet().iterator();
247
248             public boolean hasMoreElements() {
249                 return i.hasNext();
250             }
251
252             public Object JavaDoc nextElement() {
253                 return i.next();
254             }
255         };
256     }
257
258     /**
259      * Returns count of the objects in the store, or -1 if could not be
260      * obtained.
261      */

262     public int size() {
263         return this.cache.size();
264     }
265
266     /**
267      * Frees some of the fast memory used by this store.
268      * It removes the last element in the store.
269      */

270     public void free() {
271         try {
272             if (this.cache.size() > 0) {
273                 // This can throw NoSuchElementException
274
Map.Entry node = this.cache.removeLast();
275                 if (getLogger().isDebugEnabled()) {
276                     getLogger().debug("Freeing cache.");
277                     getLogger().debug(" key: " + node.getKey());
278                     getLogger().debug(" value: " + node.getValue());
279                 }
280
281                 if (this.persistent) {
282                     // Swap object on fs.
283
if(checkSerializable(node.getValue())) {
284                         try {
285                             this.persistentStore.store(
286                                 getFileName(node.getKey().toString()), node.getValue());
287                         } catch(Exception JavaDoc e) {
288                             getLogger().error("Error storing object on fs", e);
289                         }
290                     }
291                 }
292             }
293         } catch (NoSuchElementException JavaDoc e) {
294             getLogger().warn("Concurrency error in free()", e);
295         } catch (Exception JavaDoc e) {
296             getLogger().error("Error in free()", e);
297         }
298     }
299
300     /**
301      * This method checks if an object is seriazable.
302      *
303      * @param object The object to be checked
304      * @return true if the object is storeable
305      */

306     private boolean checkSerializable(Object JavaDoc object) {
307
308         if (object == null) return false;
309
310         try {
311             String JavaDoc clazz = object.getClass().getName();
312             if((clazz.equals("org.apache.cocoon.caching.CachedEventObject"))
313               || (clazz.equals("org.apache.cocoon.caching.CachedStreamObject"))
314               || (ClassUtils.implementsInterface(clazz, "org.apache.cocoon.caching.CacheValidity"))) {
315                 return true;
316             } else {
317                 return false;
318             }
319         } catch (Exception JavaDoc e) {
320             getLogger().error("Error in checkSerializable()!", e);
321             return false;
322         }
323     }
324
325     /**
326      * This method puts together a filename for
327      * the object, which shall be stored on the
328      * filesystem.
329      *
330      * @param key The key of the object
331      * @return the filename of the key
332      */

333     private String JavaDoc getFileName(String JavaDoc key) {
334         return URLEncoder.encode(key.toString());
335     }
336 }
337
Popular Tags