I think setting the log chanel name to TxLRUObjectCache.name or org.apache.slide.util.TxLRUObjectCache.name in TxLRUObjectCache.java, line 113 would make it easier to configure it in log4j.
I agree. As there can be multiple top level stores I would also suggest to include the name of the store (in our example tx). This might result in channels like
TxLRUObjectCache(tx(object)).
Or is this too much?
I will attach the according version. I also added configurable cache sizes. You can configure them in Domain.xml like this:
... <store name="tx" classname="org.apache.slide.store.TxCacheStore"> <parameter name="cache-size">1000</parameter> ... </store> ...
Comments, please...
Again: Any ideas about simple caching of content? Or do you think this is not so important?
Oliver
/* * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/store/StandardStore.java,v 1.19 2002/11/11 13:54:19 pnever Exp $ * $Revision: 1.19 $ * $Date: 2002/11/11 13:54:19 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999-2002 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Slide", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */
package org.apache.slide.util; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.slide.util.logger.Logger; import org.apache.commons.collections.LRUMap; /** * Transactional LRU object cache. Caches objects using a least-recently-used strategy. * * It provides basic isolation from other transactions and atomicity of all operations. As * no locking is used (which is undesirable mainly as a cache should never block and a commit must never fail) * serializability needs to be guaranteed by underlying stores. * <br> * <br> * <em>Caution</em>: Only global caches are limited by given size. * Size of temporary data inside a transaction is unlimited. This is needed to assure correctness of global cache. * * @author <a href="mailto:[EMAIL PROTECTED]">Oliver Zeigermann</a> * @version $Revision: 1.0$ */ public class TxLRUObjectCache { protected Map globalCache; protected Map txChangeCaches; protected Map txDeleteCaches; protected int hits = 0; protected int misses = 0; protected String name; protected Logger logger; protected String logChannel; protected final boolean loggingEnabled; public TxLRUObjectCache(int globalCacheSize, String name, Logger logger) { globalCache = new LRUMap(globalCacheSize); txChangeCaches = new HashMap(); txDeleteCaches = new HashMap(); this.name = name; this.logger = logger; logChannel = "TxLRUObjectCache"; if (name != null) { logChannel += "(" + name + ")"; } // used for guarded logging as preparation is expensive loggingEnabled = logger.isEnabled(logChannel, Logger.DEBUG); } public TxLRUObjectCache(int globalCacheSize) { this(globalCacheSize, null, null); } public synchronized void clear() { globalCache.clear(); txChangeCaches.clear(); txDeleteCaches.clear(); } public synchronized Object get(Object txId, Object key) { if (txId != null) { Set deleteCache = (Set) txDeleteCaches.get(txId); if (deleteCache.contains(key)) { hit(txId, key); // reflects that entry has been deleted in this tx return null; } Map changeCache = (Map) txChangeCaches.get(txId); Object changed = changeCache.get(key); if (changed != null) { hit(txId, key); // if object has been changed in this tx, get the local one return changed; } } // as fall back return value from global cache (if present) Object global = globalCache.get(key); if (global != null) { hit(txId, key); } else { miss(txId, key); } return global; } public synchronized void put(Object txId, Object key, Object value) { if (txId != null) { // if it has been deleted before, undo this Set deleteCache = (Set) txDeleteCaches.get(txId); deleteCache.remove(key); Map changeCache = (Map) txChangeCaches.get(txId); changeCache.put(key, value); if (loggingEnabled) { logger.log(txId + " added '" + key + "'", logChannel, Logger.DEBUG); } } else { globalCache.put(key, value); if (loggingEnabled) { logger.log("Added '" + key + "'", logChannel, Logger.DEBUG); } } } public synchronized void remove(Object txId, Object key) { if (txId != null) { // if it has been changed before, undo this Map changeCache = (Map) txChangeCaches.get(txId); changeCache.remove(key); Set deleteCache = (Set) txDeleteCaches.get(txId); deleteCache.add(key); // guard logging as preparation is expensive if (loggingEnabled) { logger.log(txId + " removed '" + key + "'", logChannel, Logger.DEBUG); } } else { globalCache.remove(key); if (loggingEnabled) { logger.log("Removed '" + key + "'", logChannel, Logger.DEBUG); } } } public synchronized void start(Object txId) { if (txId != null) { txChangeCaches.put(txId, new HashMap()); txDeleteCaches.put(txId, new HashSet()); } } public synchronized void rollback(Object txId) { if (txId != null) { if (loggingEnabled) { Map changeCache = (Map) txChangeCaches.get(txId); Set deleteCache = (Set) txDeleteCaches.get(txId); logger.log( txId + " rolled back " + changeCache.size() + " changes and " + deleteCache.size() + " scheduled deletes", logChannel, Logger.DEBUG); } // simply forget about tx forget(txId); } } public synchronized void commit(Object txId) { if (txId != null) { // apply local changes and deletes (is atomic as we have a global lock on this TxCache) Map changeCache = (Map) txChangeCaches.get(txId); for (Iterator it = changeCache.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); globalCache.put(entry.getKey(), entry.getValue()); } Set deleteCache = (Set) txDeleteCaches.get(txId); for (Iterator it = deleteCache.iterator(); it.hasNext();) { Object key = it.next(); globalCache.remove(key); } if (loggingEnabled) { logger.log( txId + " committed " + changeCache.size() + " changes and " + deleteCache.size() + " scheduled deletes", logChannel, Logger.DEBUG); } // finally forget about tx forget(txId); } } public synchronized void forget(Object txId) { if (txId != null) { txChangeCaches.remove(txId); txDeleteCaches.remove(txId); } } protected void hit(Object txId, Object key) { hits++; log(txId, key, true); } protected void miss(Object txId, Object key) { misses++; log(txId, key, false); } protected void log(Object txId, Object key, boolean hit) { if (loggingEnabled) { StringBuffer log = new StringBuffer(); if (txId != null) { Map changeCache = (Map) txChangeCaches.get(txId); Set deleteCache = (Set) txDeleteCaches.get(txId); log.append(txId + " (" + changeCache.size() + ", " + deleteCache.size() + ") "); } log.append( (hit ? "Cache Hit: '" : "Cache Miss: '") + key + "' " + hits + " / " + misses + " / " + globalCache.size()); logger.log(log.toString(), logChannel, Logger.DEBUG); } } }
/* * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/store/StandardStore.java,v 1.19 2002/11/11 13:54:19 pnever Exp $ * $Revision: 1.19 $ * $Date: 2002/11/11 13:54:19 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999-2002 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Slide", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.slide.store; import java.util.Hashtable; import org.apache.slide.common.Service; import org.apache.slide.common.ServiceAccessException; import org.apache.slide.common.ServiceParameterErrorException; import org.apache.slide.common.ServiceParameterMissingException; import org.apache.slide.store.txfile.rm.impl.XidWrapper; import org.apache.slide.util.*; import org.apache.slide.util.ObjectCache; import javax.transaction.xa.XAException; import javax.transaction.xa.Xid; import org.apache.slide.util.logger.Logger; /** * Store that allows for transactional caching of data. * * @author <a href="mailto:[EMAIL PROTECTED]">Oliver Zeigermann</a> * @version $Revision: 1.0$ */ public class TxCacheStore extends StandardStore { private static final String LOG_CHANNEL = TxCacheStore.class.getName(); protected static final int DEFAULT_GLOBAL_CACHE_SIZE = 10000; protected static final String GLOBAL_CACHE_SIZE_PARAMETER = "cache-size"; // there might be at least one active transaction branch per thread protected ThreadLocal activeTransactionBranch = new ThreadLocal(); public void setParameters(Hashtable parameters) throws ServiceParameterErrorException, ServiceParameterMissingException { super.setParameters(parameters); String globalCacheSizeString = (String) parameters.get(GLOBAL_CACHE_SIZE_PARAMETER); int globalCacheSize = DEFAULT_GLOBAL_CACHE_SIZE; if (globalCacheSizeString != null) { try { globalCacheSize = Integer.parseInt(globalCacheSizeString); } catch (NumberFormatException nfe) { getLogger().log("Cache size must be an integer! Using default size", Logger.WARNING); } } getLogger().log("Setting cache size for store " + getName() + " to " + globalCacheSize, Logger.INFO); init(globalCacheSize); } public void forget(Xid xid) throws XAException { super.forget(xid); Xid txId = (Xid) XidWrapper.wrap(xid); activeTransactionBranch.set(null); ((TxCacheWrapper) objectsCache).getTxCache().forget(txId); ((TxCacheWrapper) permissionsCache).getTxCache().forget(txId); ((TxCacheWrapper) locksCache).getTxCache().forget(txId); ((TxCacheWrapper) descriptorsCache).getTxCache().forget(txId); ((TxCacheWrapper) descriptorCache).getTxCache().forget(txId); } public void rollback(Xid xid) throws XAException { super.rollback(xid); Xid txId = (Xid) XidWrapper.wrap(xid); activeTransactionBranch.set(null); ((TxCacheWrapper) objectsCache).getTxCache().rollback(txId); ((TxCacheWrapper) permissionsCache).getTxCache().rollback(txId); ((TxCacheWrapper) locksCache).getTxCache().rollback(txId); ((TxCacheWrapper) descriptorsCache).getTxCache().rollback(txId); ((TxCacheWrapper) descriptorCache).getTxCache().rollback(txId); } public void commit(Xid xid, boolean onePhase) throws XAException { super.commit(xid, onePhase); Xid txId = (Xid) XidWrapper.wrap(xid); activeTransactionBranch.set(null); ((TxCacheWrapper) objectsCache).getTxCache().commit(txId); ((TxCacheWrapper) permissionsCache).getTxCache().commit(txId); ((TxCacheWrapper) locksCache).getTxCache().commit(txId); ((TxCacheWrapper) descriptorsCache).getTxCache().commit(txId); ((TxCacheWrapper) descriptorCache).getTxCache().commit(txId); } public void start(Xid xid, int flags) throws XAException { super.start(xid, flags); // XXX we do not suspend, so we do not resume if (flags == TMNOFLAGS || flags == TMJOIN) { Xid txId = (Xid) XidWrapper.wrap(xid); activeTransactionBranch.set(txId); ((TxCacheWrapper) objectsCache).getTxCache().start(txId); ((TxCacheWrapper) permissionsCache).getTxCache().start(txId); ((TxCacheWrapper) locksCache).getTxCache().start(txId); ((TxCacheWrapper) descriptorsCache).getTxCache().start(txId); ((TxCacheWrapper) descriptorCache).getTxCache().start(txId); } } // have it outside ctor to make it overloadable protected void init(int globalCacheSize) { objectsCache = new TxCacheWrapper(globalCacheSize, "object"); permissionsCache = new TxCacheWrapper(globalCacheSize, "permission"); locksCache = new TxCacheWrapper(globalCacheSize, "locks"); descriptorsCache = new TxCacheWrapper(globalCacheSize, "descriptors"); descriptorCache = new TxCacheWrapper(globalCacheSize, "descriptor"); } protected void enlist(Service service) throws ServiceAccessException { // we need to register ourselves to be part of the tx // XXX be careful not to call super.enlist() as this will call this method recursively super.enlist(this); super.enlist(service); } protected void delist(Service service, boolean success) throws ServiceAccessException { // as we have registered we need to deregister // XXX be careful not to call super.delist(success) as this will call this method recursively super.delist(this, success); super.delist(service, success); } protected void resetCaches() { // StandardStore calls this upon delist for a rudimentary tx control // we have one of our own tx control, so do nothing here } protected class TxCacheWrapper implements ObjectCache { private TxLRUObjectCache txCache; public TxCacheWrapper(TxLRUObjectCache txCache) { this.txCache = txCache; } public TxCacheWrapper(int globalCacheSize, String name) { this(new TxLRUObjectCache(globalCacheSize, getName() + "(" + name + ")", getLogger())); } public Object get(Object key) { Xid txId = (Xid) activeTransactionBranch.get(); return txCache.get(txId, key); } public void put(Object key, Object value) { Xid txId = (Xid) activeTransactionBranch.get(); txCache.put(txId, key, value); } public void remove(Object key) { Xid txId = (Xid) activeTransactionBranch.get(); txCache.remove(txId, key); } public void clear() { txCache.clear(); } public TxLRUObjectCache getTxCache() { return txCache; } } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]