On Wed, Sep 11, 2002 at 09:04:41AM +0200, Mahler Thomas wrote: > Yes, please this stuff !!!
ok, here we go: attached you'll find my PerThreadObjectCacheImpl.java and the testcase i wrote. Changes to singlevm.PersistenceBrokerImpl: public synchronized void abortTransaction() throws TransactionNotInProgressException { if (!inTransaction) { throw new TransactionNotInProgressException(); } inTransaction = false; this.connectionManager.localRollback(); // [EMAIL PROTECTED]: // clear List of modified objects and remove them from the // local cache if (objectCache instanceof org.apache.ojb.broker.cache.PerThreadObjectCacheImpl) { ((org.apache.ojb.broker.cache.PerThreadObjectCacheImpl) objectCache).clearUpdatedObjects (); } } public synchronized void commitTransaction() throws TransactionNotInProgressException, TransactionAbortedException { if (!inTransaction) { throw new TransactionNotInProgressException(); } inTransaction = false; this.connectionManager.localCommit(); // [EMAIL PROTECTED]: // remove modified objects from caches of other sessions: if (objectCache instanceof org.apache.ojb.broker.cache.PerThreadObjectCacheImpl) { ((org.apache.ojb.broker.cache.PerThreadObjectCacheImpl) objectCache).invalidateUpdatedObjects (); } } in method private void store(Object obj, boolean insert, Map markedForStore): [..] // if obj not present in db use INSERT if (insert) { dbAccess.executeInsert(cld, obj); } // else use UPDATE else { dbAccess.executeUpdate(cld, obj); // [EMAIL PROTECTED]: // register modified object for later removal // from caches of other sessions: if (objectCache instanceof org.apache.ojb.broker.cache.PerThreadObjectCacheImpl) { ((org.apache.ojb.broker.cache.PerThreadObjectCacheImpl) objectCache).registerUpdatedObject (obj); } } // cache object for symmetry with getObjectByXXX() objectCache.cache(obj); // 4. store 1:n and m:n associations storeCollections(obj, cld.getCollectionDescriptors(), markedForStore); [..] Maybe all those instanceofs could be replaced by something more elegant ? Any comments are very welcome. Greetings, Jens -- Jens Krämer [EMAIL PROTECTED]
package org.apache.ojb.broker; import org.apache.ojb.broker.Identity; import org.apache.ojb.broker.cache.PerThreadObjectCacheImpl; import org.apache.ojb.broker.cache.ObjectCache; import junit.framework.TestCase; public class PerThreadObjectCacheImplTest extends TestCase { private String repository; private PerThreadObjectCacheImpl cache; private int id = 1; public PerThreadObjectCacheImplTest (String testName) { super (testName); } public void testCacheLookup () { Article a1 = createArticle ("Article 1"); Article a2 = createArticle ("Article 2"); // first given article is stored by the thread -> t1 stores a1 but shouldn't find a2 TestCacheLookup t1 = new TestCacheLookup (this, a1, a2); TestCacheLookup t2 = new TestCacheLookup (this, a2, a1); try { t1.start (); t2.start (); t1.join (); t2.join (); } catch (Exception e) { fail ("caught exc.: " + e); e.printStackTrace (); } // end of try-catch } /** * tests switching a cache from one thread to another */ public void testRegisterCacheWithCurrentThread () { Article article = createArticle ("testname"); TestRegisterCacheWithCurrentThreadStore t1 = new TestRegisterCacheWithCurrentThreadStore (this, article); TestRegisterCacheWithCurrentThreadLookup t2 = new TestRegisterCacheWithCurrentThreadLookup (this, article); try { t1.start (); // store an article in the cache t1.join (); t2.setCache (t1.getCache ()); // hand over reference to cache instance to the new thread t2.start (); // retrieve the article within another thread t2.join (); } catch (Exception e) { fail ("caught exc.: " + e); e.printStackTrace (); } // end of try-catch } /** * stores an article in cache */ private class TestRegisterCacheWithCurrentThreadStore extends Thread { PerThreadObjectCacheImplTest testclass; Article article; ObjectCache cache = null; public TestRegisterCacheWithCurrentThreadStore (PerThreadObjectCacheImplTest testclass, Article article) { this.testclass = testclass; this.article = article; } /** * Get the value of cache. * @return Value of cache. */ public ObjectCache getCache() {return cache;} /** * Set the value of cache. * @param v Value to assign to cache. */ public void setCache (ObjectCache v) {this.cache = v;} public void run () { if (cache == null) { cache = testclass.cache.getCurrentCache (); } testclass.cache.cache (article); } } /** * tries to get the article stored by the TestRegisterCacheWithCurrentThreadStore thread */ private class TestRegisterCacheWithCurrentThreadLookup extends Thread { Article article; ObjectCache cache = null; PerThreadObjectCacheImplTest testclass; public TestRegisterCacheWithCurrentThreadLookup (PerThreadObjectCacheImplTest testclass, Article article) { this.article = article; this.testclass = testclass; } /** * Set the value of cache. * @param v Value to assign to cache. */ public void setCache(ObjectCache v) {this.cache = v;} public void run () { testclass.cache.registerCacheWithCurrentThread (cache); Article a = (Article) testclass.cache.lookup (new Identity (article)); assertNotNull (a); assertEquals (article.getArticleName (), a.getArticleName ()); } } private class TestCacheLookup extends Thread { PerThreadObjectCacheImplTest testclass; Article a1, a2; public TestCacheLookup (PerThreadObjectCacheImplTest testclass, Article a1, Article a2) { this.testclass = testclass; this.a1 = a1; this.a2 = a2; } public void run () { String name = a1.getArticleName (); testclass.cache.cache (a1); Article new_a1 = (Article) testclass.cache.lookup (new Identity (a1)); assertEquals (name, new_a1.getArticleName ()); try { sleep (10); } catch (Exception e) { } Article new_a2 = (Article) testclass.cache.lookup (new Identity (a2)); assertNull (new_a2); // a2 should not be found by this thread } } private synchronized Article createArticle (String name) { Article retval = new Article (); retval.setArticleId (id++); retval.setArticleName (name); return retval; } public void setUp () throws Exception { super.setUp (); /* repository = ((PersistenceBrokerConfiguration) PersistenceBrokerFactory .getConfigurator() .getConfigurationFor(null)) .getRepositoryFilename(); */ cache = new PerThreadObjectCacheImpl (); } public void tearDown () throws Exception { super.tearDown (); } }
package org.apache.ojb.broker.cache; /* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2001 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 acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" and * "Apache ObjectRelationalBridge" 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", * "Apache ObjectRelationalBridge", nor may "Apache" appear in their name, without * prior written permission of the Apache Software Foundation. * * 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/>. */ import org.apache.ojb.broker.Identity; import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException; import org.apache.ojb.broker.util.logging.*; import org.apache.ojb.broker.util.logging.Logger; import org.apache.ojb.broker.util.logging.LoggerFactory; import java.util.Vector; import java.util.Hashtable; import java.util.Collection; import java.util.Iterator; /** * holds multiple caches on a per-thread basis * so that every thread has its own room of objects that can be modified * * calling cache.remove (obj) results in removal of obj from all cache * instances to force refetching of changed objects in other sessions * * @todo what has to happen when deleting objects ? * * * @author <a href="mailto:[EMAIL PROTECTED]">Jens Kraemer<a> */ public class PerThreadObjectCacheImpl implements ObjectCache { private static Class defaultCacheClass = ObjectCacheDefaultImpl.class; /** * the hashtable holding all caches */ protected Hashtable cacheTable = null; /** * the hashtable holding the register of modified objects for each cache */ protected Hashtable registerTable = null; private long hitCount = 0; private long failCount = 0; /** * public Default Constructor */ public PerThreadObjectCacheImpl() { cacheTable = new Hashtable(); registerTable = new Hashtable(); } public void registerCacheWithCurrentThread (ObjectCache cache) { //cacheTable.remove (Thread.currentThread ()); cacheTable.put (Thread.currentThread (), cache); } /** * register obj for later removal from other caches * called by PersistenceBrokerImpl on store (...) */ public void registerUpdatedObject (Object obj) { getCurrentRegister ().add (obj); } /** * removes all Objects modified by the current thread from the other caches * called by PersistenceBrokerImpl on commitTransaction () */ public void invalidateUpdatedObjects () { removeFromOtherCaches (getCurrentRegister ()); } /** * removes all updated objects from the current cache to force refetching on next load, * afterwards clears the list of updated objects * called by PersistenceBrokerImpl on abortTransaction () */ public void clearUpdatedObjects () { removeFromCurrentCache (getCurrentRegister ()); getCurrentRegister ().clear (); } /** * Make object obj persistent to Objectcache. * compute objects identity and use it as key for the hashmap */ public void cache(Object obj) throws ClassNotPersistenceCapableException { Identity oid = new Identity(obj); this.cache(oid, obj); } /** * * clear all ObjectCaches * */ public void clear() { Iterator it = cacheTable.values ().iterator (); while (it.hasNext ()) { ((ObjectCache) it.next ()).clear (); } // end of while (it.hasNext ()) } /** * clear current Cache */ public void clearCurrentCache () { getCurrentCache ().clear (); } /** * * makes object persistent in the Objectcache of the current thread. * */ public void cache(Identity oid, Object obj) { if (oid != null && obj != null) { getCurrentCache ().cache (oid, obj); } } /** * * Lookup object with Identity oid in the cache of the current thread * * returns null if no matching id is found * */ public Object lookup(Identity oid) { Object retval = null; hitCount++; retval = getCurrentCache ().lookup (oid); if (retval == null) { failCount++; } // end of if (retval == null) return retval; } /** * * removes an Object from all caches. * */ public void remove(Object obj) { if (obj != null) { Iterator it = cacheTable.values ().iterator (); while (it.hasNext ()) { ((ObjectCache) it.next ()).remove (new Identity (obj)); } // end of while (it.hasNext ()) } } /** * remove an Object from current cache */ public void removeFromCurrentCache (Object obj) { getCurrentCache ().remove (obj); } /** * removes all objects in the given collection from current cache */ public void removeFromCurrentCache (Collection objects) { ObjectCache currentCache = getCurrentCache (); if (objects != null) { Iterator it = objects.iterator (); while (it.hasNext ()) { currentCache.remove (it.next ()); } // end of while (it.hasNext ()) } // end of if (objects != null) } /** * removes obj from all caches except the one belonging to the current thread */ public void removeFromOtherCaches (Object obj) { if (obj != null) { ObjectCache cache; ObjectCache currentCache = getCurrentCache (); Iterator it = cacheTable.values ().iterator (); if (obj instanceof Collection) { // remove collection of objects Iterator objects; while (it.hasNext ()) { cache = (ObjectCache) it.next (); if (cache != currentCache) { objects = ((Collection) obj).iterator (); while (objects.hasNext ()) { cache.remove (new Identity (objects.next ())); } // end of while (objects.hasNext ()) } } } else { // remove single object while (it.hasNext ()) { cache = (ObjectCache) it.next (); if (cache != currentCache) { cache.remove (new Identity (obj)); } // end of if (cache != currentCache) } // end of while (it.hasNext ()) } // end of else } } /** * @return the register of updated objects for the current thread */ private Collection getCurrentRegister () { Collection retval = (Collection) registerTable.get (getCurrentCache ()); if (retval == null) { retval = new Vector (); registerTable.put (getCurrentCache (), retval); } return retval; } /** * creates a new Instance of the selected Cache class and registers * it with the current thread */ public ObjectCache createNewCache () { ObjectCache retval = null; try { retval = (ObjectCache) defaultCacheClass.newInstance (); } catch (Exception e) { retval = new ObjectCacheDefaultImpl (); } cacheTable.put (Thread.currentThread (), retval); return retval; } /** * @return the ObjectCache instance for the current thread */ public ObjectCache getCurrentCache () { ObjectCache retval = (ObjectCache) cacheTable.get (Thread.currentThread ()); if (retval == null) { retval = createNewCache (); } // end of if (retval == null) return retval; } public void finalize() { Logger logger = LoggerFactory.getDefaultLogger(); logger.info("OJB CACHE STATISTICS"); logger.info("lookups: " + hitCount); logger.info("failures: " + failCount); // logger.info("reclaimed: " + gcCount); } }
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>