View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  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  package org.apache.jetspeed.page.impl;
18  
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.LinkedList;
22  import java.util.List;
23  import java.util.Properties;
24  
25  import org.apache.jetspeed.om.folder.impl.FolderImpl;
26  import org.apache.jetspeed.page.PageManager;
27  import org.apache.jetspeed.page.document.impl.NodeImpl;
28  import org.apache.ojb.broker.Identity;
29  import org.apache.ojb.broker.PersistenceBroker;
30  import org.apache.ojb.broker.cache.ObjectCache;
31  
32  /***
33   * DatabasePageManagerCache
34   * 
35   * @author <a href="mailto:rwatler@apache.org">Randy Watler</a>
36   * @version $Id: $
37   */
38  public class DatabasePageManagerCache implements ObjectCache
39  {
40      private static HashMap cacheByOID;
41      private static LinkedList cacheLRUList;
42      private static HashMap cacheByPath;
43      private static int cacheSize;
44      private static int cacheExpiresSeconds;
45      private static boolean constraintsEnabled;
46      private static boolean permissionsEnabled;
47      private static PageManager pageManager;
48  
49      /***
50       * cacheInit
51       *
52       * Initialize cache using page manager configuration.
53       *
54       * @param pageManager configured page manager
55       */
56      public synchronized static void cacheInit(DatabasePageManager dbPageManager)
57      {
58          if (pageManager == null)
59          {
60              cacheByOID = new HashMap();
61              cacheLRUList = new LinkedList();
62              cacheByPath = new HashMap();
63              cacheSize = dbPageManager.getCacheSize();
64              cacheExpiresSeconds = dbPageManager.getCacheExpiresSeconds();
65              constraintsEnabled = dbPageManager.getConstraintsEnabled();
66              permissionsEnabled = dbPageManager.getPermissionsEnabled();
67              pageManager = dbPageManager;
68          }
69      }
70  
71      /***
72       * setPageManagerProxy
73       *
74       * @param proxy proxied page manager interface used to
75       *              inject into Folder instances to provide
76       *              transaction/interception
77       */
78      public synchronized static void setPageManagerProxy(PageManager proxy)
79      {
80          // set/reset page manager proxy and clear cache to
81          // flush any objects referencing replaced page manager
82          if (pageManager != proxy)
83          {
84              pageManager = proxy;
85              cacheClear();
86          }
87      }
88  
89      /***
90       * cacheLookup
91       *
92       * Lookup node instances by unique path.
93       *
94       * @param path node unique path
95       * @return cached node
96       */
97      public synchronized static NodeImpl cacheLookup(String path)
98      {
99          if (path != null)
100         {
101             // return valid object cached by path
102             return (NodeImpl)cacheValidateEntry((Entry)cacheByPath.get(path));
103         }
104         return null;
105     }
106 
107     /***
108      * cacheAdd
109      *
110      * Add object to cache and cache node instances by unique path;
111      * infuse nodes loaded by OJB with page manager configuration.
112      *
113      * @param oid object/node indentity
114      * @param obj object/node to cache
115      */
116     public synchronized static void cacheAdd(Identity oid, Object obj)
117     {
118         Entry entry = (Entry)cacheByOID.get(oid);
119         if (entry != null)
120         {
121             // update cache LRU order
122             cacheLRUList.remove(entry);
123             cacheLRUList.addFirst(entry);
124             // refresh cache entry
125             entry.touch();
126         }
127         else
128         {
129             // create new cache entry and map
130             entry = new Entry(obj, oid);
131             cacheByOID.put(oid, entry);
132             cacheLRUList.addFirst(entry);
133             // infuse node with page manager configuration
134             // or the page manager itself and add to the
135             // paths cache 
136             if (obj instanceof NodeImpl)
137             {
138                 NodeImpl node = (NodeImpl)obj;
139                 node.setConstraintsEnabled(constraintsEnabled);
140                 node.setPermissionsEnabled(permissionsEnabled);
141                 cacheByPath.put(node.getPath(), entry);
142                 if (obj instanceof FolderImpl)
143                 {
144                     ((FolderImpl)obj).setPageManager(pageManager);
145                 }
146             }
147             // trim cache as required to maintain cache size
148             while (cacheLRUList.size() > cacheSize)
149             {
150                 cacheRemoveEntry((Entry)cacheLRUList.getLast(), true);
151             }
152         }
153     }
154 
155     /***
156      * cacheClear
157      *
158      * Clear object and node caches.
159      */
160     public synchronized static void cacheClear()
161     {
162         // remove all cache entries
163         Iterator removeIter = cacheLRUList.iterator();
164         while (removeIter.hasNext())
165         {
166             cacheRemoveEntry((Entry)removeIter.next(), false);
167         }
168         // clear cache
169         cacheByOID.clear();
170         cacheLRUList.clear();
171         cacheByPath.clear();
172     }
173 
174     /***
175      * cacheLookup
176      *
177      * Lookup objects by identity.
178      *
179      * @param oid object identity
180      * @return cached object
181      */
182     public synchronized static Object cacheLookup(Identity oid)
183     {
184         if (oid != null)
185         {
186             // return valid object cached by oid
187             return cacheValidateEntry((Entry)cacheByOID.get(oid));
188         }
189         return null;
190     }
191 
192     /***
193      * cacheRemove
194      *
195      * Remove identified object from object and node caches.
196      *
197      * @param oid object identity
198      */
199     public synchronized static void cacheRemove(Identity oid)
200     {
201         // remove from cache by oid
202         cacheRemoveEntry((Entry)cacheByOID.get(oid), true);
203     }
204 
205     /***
206      * cacheRemove
207      *
208      * Remove identified object from object and node caches.
209      *
210      * @param path object path
211      */
212     public synchronized static void cacheRemove(String path)
213     {
214         // remove from cache by path
215         cacheRemoveEntry((Entry)cacheByPath.get(path), true);
216     }
217     
218     /***
219      * cacheValidateEntry
220      *
221      * Validate specified entry from cache, returning cached
222      * object if valid.
223      *
224      * @param entry cache entry to validate
225      * @return validated object from cache
226      */
227     private synchronized static Object cacheValidateEntry(Entry entry)
228     {
229         if (entry != null)
230         {
231             if (!entry.isExpired())
232             {
233                 // update cache LRU order
234                 cacheLRUList.remove(entry);
235                 cacheLRUList.addFirst(entry);
236                 // refresh cache entry and return object
237                 entry.touch();
238                 return entry.getObject();
239             }
240             else
241             {
242                 // remove expired entry
243                 cacheRemoveEntry(entry, true);
244             }
245         }
246         return null;
247     }
248     
249     /***
250      * cacheRemoveEntry
251      *
252      * Remove specified entry from cache.
253      *
254      * @param entry cache entry to remove
255      * @param remove enable removal from cache
256      */
257     private synchronized static void cacheRemoveEntry(Entry entry, boolean remove)
258     {
259         if (entry != null)
260         {
261             Object removeObj = entry.getObject();
262             if (remove)
263             {
264                 // remove entry, optimize for removal from end
265                 // of list as cache size is met or entries expire
266                 if (cacheLRUList.getLast() == entry)
267                 {
268                     cacheLRUList.removeLast();
269                 }
270                 else
271                 {
272                     int removeIndex = cacheLRUList.lastIndexOf(entry);
273                     if (removeIndex > 0)
274                     {
275                         cacheLRUList.remove(removeIndex);
276                     }
277                 }
278                 // unmap entry
279                 cacheByOID.remove(entry.getOID());
280                 if (removeObj instanceof NodeImpl)
281                 {
282                     cacheByPath.remove(((NodeImpl)removeObj).getPath());
283                 }
284             }
285             // reset internal FolderImpl caches
286             if (removeObj instanceof FolderImpl)
287             {
288                 ((FolderImpl)removeObj).resetAll(false);
289             }
290         }
291     }
292 
293     /***
294      * resetCachedSecurityConstraints
295      *
296      * Reset cached security constraints in all cached node objects.
297      */
298     public synchronized static void resetCachedSecurityConstraints()
299     {
300         // reset cached objects
301         Iterator resetIter = cacheLRUList.iterator();
302         while (resetIter.hasNext())
303         {
304             Object obj = ((Entry)resetIter.next()).getObject();
305             if (obj instanceof NodeImpl)
306             {
307                 ((NodeImpl)obj).resetCachedSecurityConstraints();
308             }
309         }
310     }
311 
312     /***
313      * Entry
314      *
315      * Cache entry class adding entry timestamp to track expiration
316      */
317     private static class Entry
318     {
319         public long timestamp;
320         public Object object;
321         public Identity oid;
322 
323         public Entry(Object object, Identity oid)
324         {
325             touch();
326             this.object = object;
327             this.oid = oid;
328         }
329 
330         public boolean isExpired()
331         {
332             if (DatabasePageManagerCache.cacheExpiresSeconds > 0)
333             {
334                 long now = System.currentTimeMillis();
335                 if (((now - timestamp) / 1000) < DatabasePageManagerCache.cacheExpiresSeconds)
336                 {
337                     timestamp = now;
338                     return false;
339                 }
340                 return true;
341             }
342             return false;
343         }
344         
345         public void touch()
346         {
347             if (DatabasePageManagerCache.cacheExpiresSeconds > 0)
348             {
349                 timestamp = System.currentTimeMillis();
350             }
351         }
352 
353         public Object getObject()
354         {
355             return object;
356         }
357 
358         public Identity getOID()
359         {
360             return oid;
361         }
362     }
363 
364     /***
365      * DatabasePageManagerCache
366      *
367      * Construct a cache instance using OJB compliant signatures.
368      *
369      * @param broker broker that is to own cache
370      * @param props attribute properties passed to cache
371      */
372     public DatabasePageManagerCache(PersistenceBroker broker, Properties props)
373     {
374     }
375 
376     /* (non-Javadoc)
377      * @see org.apache.ojb.broker.cache.ObjectCache#cache(org.apache.ojb.broker.Identity, java.lang.Object)
378      */
379     public void cache(Identity oid, Object obj)
380     {
381         cacheAdd(oid, obj);
382     }
383 
384     /* (non-Javadoc)
385      * @see org.apache.ojb.broker.cache.ObjectCache#clear()
386      */
387     public void clear()
388     {
389         cacheClear();
390     }
391 
392     /* (non-Javadoc)
393      * @see org.apache.ojb.broker.cache.ObjectCache#lookup(org.apache.ojb.broker.Identity)
394      */
395     public Object lookup(Identity oid)
396     {
397         return cacheLookup(oid);
398     }
399 
400     /* (non-Javadoc)
401      * @see org.apache.ojb.broker.cache.ObjectCache#remove(org.apache.ojb.broker.Identity)
402      */
403     public void remove(Identity oid)
404     {
405         cacheRemove(oid);
406     }
407 
408     public synchronized static void dump()
409     {
410         System.out.println("--------------------------1");        
411         Iterator dumpIter = cacheLRUList.iterator();
412         while (dumpIter.hasNext())
413         {
414             Entry entry = (Entry)dumpIter.next();
415             Object entryObject = entry.getObject();
416             if (entryObject instanceof NodeImpl)
417             {
418                 System.out.println("entry = " + ((NodeImpl)entryObject).getPath() + ", " + entry.getOID());
419             }
420             else
421             {
422                 System.out.println("entry = <none>, " + entry.getOID());
423             }
424         }
425         System.out.println("--------------------------2");
426     }
427     
428     protected static ThreadLocal transactionedOperations = new ThreadLocal();
429     
430     public static List getTransactions()
431     {
432         List operations = (List)transactionedOperations.get();
433         if (operations == null)
434         {
435             operations = new LinkedList();
436             transactionedOperations.set(operations);
437         }
438         
439         return operations;
440     }
441 
442     /***
443      * @param principal
444      *            The principal to set.
445      */
446     public static void addTransaction(TransactionedOperation operation)
447     {
448         List transactions = getTransactions();        
449         transactions.add(operation);
450     }
451     
452     public static void rollbackTransactions()
453     {
454         Iterator transactions = getTransactions().iterator();
455         while (transactions.hasNext())
456         {
457             TransactionedOperation operation = (TransactionedOperation)transactions.next();
458             cacheRemove(operation.getPath());
459         }
460     }
461 }