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]>

Reply via email to