001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. 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: package org.apache.jetspeed.page.impl;
018:
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.LinkedList;
022: import java.util.List;
023: import java.util.Properties;
024:
025: import org.apache.jetspeed.om.folder.impl.FolderImpl;
026: import org.apache.jetspeed.page.PageManager;
027: import org.apache.jetspeed.page.document.impl.NodeImpl;
028: import org.apache.ojb.broker.Identity;
029: import org.apache.ojb.broker.PersistenceBroker;
030: import org.apache.ojb.broker.cache.ObjectCache;
031:
032: /**
033: * DatabasePageManagerCache
034: *
035: * @author <a href="mailto:rwatler@apache.org">Randy Watler</a>
036: * @version $Id: $
037: */
038: public class DatabasePageManagerCache implements ObjectCache {
039: private static HashMap cacheByOID;
040: private static LinkedList cacheLRUList;
041: private static HashMap cacheByPath;
042: private static int cacheSize;
043: private static int cacheExpiresSeconds;
044: private static boolean constraintsEnabled;
045: private static boolean permissionsEnabled;
046: private static PageManager pageManager;
047:
048: /**
049: * cacheInit
050: *
051: * Initialize cache using page manager configuration.
052: *
053: * @param pageManager configured page manager
054: */
055: public synchronized static void cacheInit(
056: DatabasePageManager dbPageManager) {
057: if (pageManager == null) {
058: cacheByOID = new HashMap();
059: cacheLRUList = new LinkedList();
060: cacheByPath = new HashMap();
061: cacheSize = dbPageManager.getCacheSize();
062: cacheExpiresSeconds = dbPageManager
063: .getCacheExpiresSeconds();
064: constraintsEnabled = dbPageManager.getConstraintsEnabled();
065: permissionsEnabled = dbPageManager.getPermissionsEnabled();
066: pageManager = dbPageManager;
067: }
068: }
069:
070: /**
071: * setPageManagerProxy
072: *
073: * @param proxy proxied page manager interface used to
074: * inject into Folder instances to provide
075: * transaction/interception
076: */
077: public synchronized static void setPageManagerProxy(
078: PageManager proxy) {
079: // set/reset page manager proxy and clear cache to
080: // flush any objects referencing replaced page manager
081: if (pageManager != proxy) {
082: pageManager = proxy;
083: cacheClear();
084: }
085: }
086:
087: /**
088: * cacheLookup
089: *
090: * Lookup node instances by unique path.
091: *
092: * @param path node unique path
093: * @return cached node
094: */
095: public synchronized static NodeImpl cacheLookup(String path) {
096: if (path != null) {
097: // return valid object cached by path
098: return (NodeImpl) cacheValidateEntry((Entry) cacheByPath
099: .get(path));
100: }
101: return null;
102: }
103:
104: /**
105: * cacheAdd
106: *
107: * Add object to cache and cache node instances by unique path;
108: * infuse nodes loaded by OJB with page manager configuration.
109: *
110: * @param oid object/node indentity
111: * @param obj object/node to cache
112: */
113: public synchronized static void cacheAdd(Identity oid, Object obj) {
114: Entry entry = (Entry) cacheByOID.get(oid);
115: if (entry != null) {
116: // update cache LRU order
117: cacheLRUList.remove(entry);
118: cacheLRUList.addFirst(entry);
119: // refresh cache entry
120: entry.touch();
121: } else {
122: // create new cache entry and map
123: entry = new Entry(obj, oid);
124: cacheByOID.put(oid, entry);
125: cacheLRUList.addFirst(entry);
126: // infuse node with page manager configuration
127: // or the page manager itself and add to the
128: // paths cache
129: if (obj instanceof NodeImpl) {
130: NodeImpl node = (NodeImpl) obj;
131: node.setConstraintsEnabled(constraintsEnabled);
132: node.setPermissionsEnabled(permissionsEnabled);
133: cacheByPath.put(node.getPath(), entry);
134: if (obj instanceof FolderImpl) {
135: ((FolderImpl) obj).setPageManager(pageManager);
136: }
137: }
138: // trim cache as required to maintain cache size
139: while (cacheLRUList.size() > cacheSize) {
140: cacheRemoveEntry((Entry) cacheLRUList.getLast(), true);
141: }
142: }
143: }
144:
145: /**
146: * cacheClear
147: *
148: * Clear object and node caches.
149: */
150: public synchronized static void cacheClear() {
151: // remove all cache entries
152: Iterator removeIter = cacheLRUList.iterator();
153: while (removeIter.hasNext()) {
154: cacheRemoveEntry((Entry) removeIter.next(), false);
155: }
156: // clear cache
157: cacheByOID.clear();
158: cacheLRUList.clear();
159: cacheByPath.clear();
160: }
161:
162: /**
163: * cacheLookup
164: *
165: * Lookup objects by identity.
166: *
167: * @param oid object identity
168: * @return cached object
169: */
170: public synchronized static Object cacheLookup(Identity oid) {
171: if (oid != null) {
172: // return valid object cached by oid
173: return cacheValidateEntry((Entry) cacheByOID.get(oid));
174: }
175: return null;
176: }
177:
178: /**
179: * cacheRemove
180: *
181: * Remove identified object from object and node caches.
182: *
183: * @param oid object identity
184: */
185: public synchronized static void cacheRemove(Identity oid) {
186: // remove from cache by oid
187: cacheRemoveEntry((Entry) cacheByOID.get(oid), true);
188: }
189:
190: /**
191: * cacheRemove
192: *
193: * Remove identified object from object and node caches.
194: *
195: * @param path object path
196: */
197: public synchronized static void cacheRemove(String path) {
198: // remove from cache by path
199: cacheRemoveEntry((Entry) cacheByPath.get(path), true);
200: }
201:
202: /**
203: * cacheValidateEntry
204: *
205: * Validate specified entry from cache, returning cached
206: * object if valid.
207: *
208: * @param entry cache entry to validate
209: * @return validated object from cache
210: */
211: private synchronized static Object cacheValidateEntry(Entry entry) {
212: if (entry != null) {
213: if (!entry.isExpired()) {
214: // update cache LRU order
215: cacheLRUList.remove(entry);
216: cacheLRUList.addFirst(entry);
217: // refresh cache entry and return object
218: entry.touch();
219: return entry.getObject();
220: } else {
221: // remove expired entry
222: cacheRemoveEntry(entry, true);
223: }
224: }
225: return null;
226: }
227:
228: /**
229: * cacheRemoveEntry
230: *
231: * Remove specified entry from cache.
232: *
233: * @param entry cache entry to remove
234: * @param remove enable removal from cache
235: */
236: private synchronized static void cacheRemoveEntry(Entry entry,
237: boolean remove) {
238: if (entry != null) {
239: Object removeObj = entry.getObject();
240: if (remove) {
241: // remove entry, optimize for removal from end
242: // of list as cache size is met or entries expire
243: if (cacheLRUList.getLast() == entry) {
244: cacheLRUList.removeLast();
245: } else {
246: int removeIndex = cacheLRUList.lastIndexOf(entry);
247: if (removeIndex > 0) {
248: cacheLRUList.remove(removeIndex);
249: }
250: }
251: // unmap entry
252: cacheByOID.remove(entry.getOID());
253: if (removeObj instanceof NodeImpl) {
254: cacheByPath
255: .remove(((NodeImpl) removeObj).getPath());
256: }
257: }
258: // reset internal FolderImpl caches
259: if (removeObj instanceof FolderImpl) {
260: ((FolderImpl) removeObj).resetAll(false);
261: }
262: }
263: }
264:
265: /**
266: * resetCachedSecurityConstraints
267: *
268: * Reset cached security constraints in all cached node objects.
269: */
270: public synchronized static void resetCachedSecurityConstraints() {
271: // reset cached objects
272: Iterator resetIter = cacheLRUList.iterator();
273: while (resetIter.hasNext()) {
274: Object obj = ((Entry) resetIter.next()).getObject();
275: if (obj instanceof NodeImpl) {
276: ((NodeImpl) obj).resetCachedSecurityConstraints();
277: }
278: }
279: }
280:
281: /**
282: * Entry
283: *
284: * Cache entry class adding entry timestamp to track expiration
285: */
286: private static class Entry {
287: public long timestamp;
288: public Object object;
289: public Identity oid;
290:
291: public Entry(Object object, Identity oid) {
292: touch();
293: this .object = object;
294: this .oid = oid;
295: }
296:
297: public boolean isExpired() {
298: if (DatabasePageManagerCache.cacheExpiresSeconds > 0) {
299: long now = System.currentTimeMillis();
300: if (((now - timestamp) / 1000) < DatabasePageManagerCache.cacheExpiresSeconds) {
301: timestamp = now;
302: return false;
303: }
304: return true;
305: }
306: return false;
307: }
308:
309: public void touch() {
310: if (DatabasePageManagerCache.cacheExpiresSeconds > 0) {
311: timestamp = System.currentTimeMillis();
312: }
313: }
314:
315: public Object getObject() {
316: return object;
317: }
318:
319: public Identity getOID() {
320: return oid;
321: }
322: }
323:
324: /**
325: * DatabasePageManagerCache
326: *
327: * Construct a cache instance using OJB compliant signatures.
328: *
329: * @param broker broker that is to own cache
330: * @param props attribute properties passed to cache
331: */
332: public DatabasePageManagerCache(PersistenceBroker broker,
333: Properties props) {
334: }
335:
336: /* (non-Javadoc)
337: * @see org.apache.ojb.broker.cache.ObjectCache#cache(org.apache.ojb.broker.Identity, java.lang.Object)
338: */
339: public void cache(Identity oid, Object obj) {
340: cacheAdd(oid, obj);
341: }
342:
343: /* (non-Javadoc)
344: * @see org.apache.ojb.broker.cache.ObjectCache#clear()
345: */
346: public void clear() {
347: cacheClear();
348: }
349:
350: /* (non-Javadoc)
351: * @see org.apache.ojb.broker.cache.ObjectCache#lookup(org.apache.ojb.broker.Identity)
352: */
353: public Object lookup(Identity oid) {
354: return cacheLookup(oid);
355: }
356:
357: /* (non-Javadoc)
358: * @see org.apache.ojb.broker.cache.ObjectCache#remove(org.apache.ojb.broker.Identity)
359: */
360: public void remove(Identity oid) {
361: cacheRemove(oid);
362: }
363:
364: public synchronized static void dump() {
365: System.out.println("--------------------------1");
366: Iterator dumpIter = cacheLRUList.iterator();
367: while (dumpIter.hasNext()) {
368: Entry entry = (Entry) dumpIter.next();
369: Object entryObject = entry.getObject();
370: if (entryObject instanceof NodeImpl) {
371: System.out.println("entry = "
372: + ((NodeImpl) entryObject).getPath() + ", "
373: + entry.getOID());
374: } else {
375: System.out.println("entry = <none>, " + entry.getOID());
376: }
377: }
378: System.out.println("--------------------------2");
379: }
380:
381: protected static ThreadLocal transactionedOperations = new ThreadLocal();
382:
383: public static List getTransactions() {
384: List operations = (List) transactionedOperations.get();
385: if (operations == null) {
386: operations = new LinkedList();
387: transactionedOperations.set(operations);
388: }
389:
390: return operations;
391: }
392:
393: /**
394: * @param principal
395: * The principal to set.
396: */
397: public static void addTransaction(TransactionedOperation operation) {
398: List transactions = getTransactions();
399: transactions.add(operation);
400: }
401:
402: public static void rollbackTransactions() {
403: Iterator transactions = getTransactions().iterator();
404: while (transactions.hasNext()) {
405: TransactionedOperation operation = (TransactionedOperation) transactions
406: .next();
407: cacheRemove(operation.getPath());
408: }
409: }
410: }
|