go ahead ! I'm glad to contribute !
:-)

-----Message d'origine-----
De: Armin Waibel
A: OJB Users List
Date: 10/12/2004 16:14
Objet: Re: Another implementation of the Two Level Cache

Hi Jean-Baptiste,

CLARAMONTE Jean-Baptiste wrote:

> Hello,
> 
> As I was not satisfy with the implementation of the TwoLevelCache,
> mainly because it was not isolating the object in use in a transaction
> with others transactions, so I've tried to write another one and now I
> would like too share it with the community.

thanks for contribution!
You are right, the TwoLevelCache in CVS is only a alpha-version for a 
two-level caching (upcoming OJB 1.1 in CVS trunk will support a real 
two-level cache) and I never test it.


> For resuming here are the behavior of this cache :
> When you retrieve an object a flat copy are made

Your copy based on bean properties - or I'm wrong? I recommend to use 
the OJB metadata information (associated FieldDescriptor of the object) 
to make the copy. Because user could use different PersistentField 
implementations (e.g. standard beans, only fields no getter/setter, dyna

beans,..).

In OJB trunk I introduce a class called SnapshotBuilder (alpha version)
http://cvs.apache.org/viewcvs.cgi/db-ojb/src/java/org/apache/ojb/broker/
cache/SnapshotBuilder.java?view=markup


To get a new object instance, please use
ClassHelper#buildNewObjectInstance
This method include user defined object factories.


> and the added in the
> first level cache (a kind of broker session cache).

If OJB lookup an object from the cache it doesn't expect an flat object.

Here we have to modify source in OJB or the cache implementation should 
always return objects based on the metadata information (normally full 
materialized objects).
Think in method #lookup(...) it will be possible to materialize the full

object using PB instance. OK, this will be tricky but I think not 
impossible.


> A call to store() will not immediately put the objet in the second
> level cache but only in the first level (so this way other
> transactions are isolated from update on the objet since your
> transaction could finally abort)
> Rollbacking a transaction simply clear the first level cache (the
> broker session cache)
> Commiting the transaction transfert the object from the first level
> cache to the second level cache so that object update become visible
> to other transactions.

You do this in method PBStateListener#beforeCommit I would recommend to 
use method #afterCommit, because when the commit fails (e.g. timed out 
connection, ...) all changed objects will be in second level cache and 
no rollback will be possible. On the other hand method #afterCommit will

not be called when connection commit fails.

In OJB1.0.1 I introduced a bug in method ObjectCacheDefaultImpl#buildKey

which you adopt in your implementation. Please get latest version from 
OJB_1_0_RELEASE branch and replace that method (and add additional inner

class).

http://cvs.apache.org/viewcvs.cgi/db-ojb/src/java/org/apache/ojb/broker/
cache/ObjectCacheDefaultImpl.java?rev=1.24.2.2&only_with_tag=OJB_1_0_REL
EASE&view=markup

If you agree I will replace the old TwoLevelCache implementation with
yours.


regards,
Armin


> 
> There is 3 java files :
> 
> AbstractTwoLevelObjectCache.java
> LocalTwoLevelObjectCache.java
> CopyHelper.java (you should use the last update for commons-bean for
this
> one)
> IFlatClone.java
> 
> Just declare in OJB the LocalTwoLevelObjectCache for your new cache
handler.
> 
> Here the code (sorry but it's not possible to attach file in the ojb
> user group) :
> 
> package com.mycompagny.fwk.cache;
> 
> import java.util.Hashtable;
> import java.util.Iterator;
> import java.util.Map;
> import java.util.Properties;
> import java.util.Map.Entry;
> import org.apache.commons.lang.builder.ToStringBuilder;
> import org.apache.commons.lang.builder.ToStringStyle;
> import org.apache.commons.lang.builder.HashCodeBuilder;
> import org.apache.ojb.broker.Identity;
> import org.apache.ojb.broker.PBStateEvent;
> import org.apache.ojb.broker.PBStateListener;
> import org.apache.ojb.broker.PersistenceBroker;
> import org.apache.ojb.broker.OJBRuntimeException;
> import org.apache.ojb.broker.cache.ObjectCache;
> import org.apache.ojb.broker.util.logging.Logger;
> import org.apache.ojb.broker.util.logging.LoggerFactory;
> 
> /**
> *
> *
> * This abstract class implements the behavior of the session cache
> (first level cache)
> * and declare the abstract methods for implementing the application
> cache (second level cache).
> *
> *
> *
> */
> public abstract class AbstractTwoLevelObjectCache implements
> ObjectCache, PBStateListener
> {
>        protected static final Logger LOG =
> LoggerFactory.getLogger(AbstractTwoLevelObjectCache.class);
> 
>    public static final String CACHING_KEY_TYPE_PROP =
"cachingKeyType";
> 
>    protected   PersistenceBroker       _broker;
>    private     Map                             _sessionCache;
> 
>    /**
>     * Determines how the key was build for the cached objects:
>     * <br/>
>     * 0 - Identity object was used as key
>     * 1 - Idenity + jcdAlias name was used as key
>     * 2 - Identity + model (DescriptorRepository) was used as key
>     * 3 - all together (1+2)
>     */
>    private int cachingKeyType;
> 
>    public AbstractTwoLevelObjectCache(PersistenceBroker broker,
> Properties prop)
>    {
>        this._broker = broker;
> 
>        cachingKeyType = prop == null ? 0 :
> (Integer.parseInt(prop.getProperty(CACHING_KEY_TYPE_PROP, "0")));
> 
>        if (broker != null)
>        {
>            // we add this instance as a permanent PBStateListener
>            broker.addListener(this, true);
>        }
> 
>        _sessionCache = new Hashtable();
>    }
> 
>    /**
>     * Clear the session cache(first level cache) and the application
> cache (second level cache)
>     */
>    public void clear()
>    {
>        clearApplicationCache();
>        _sessionCache.clear();
>    }
> 
>        /**
>     * Makes object persistent to the sessionCache.
>     * even if they are still cached.
>     */
>    public void cache(Identity oid, Object obj)
>    {
>        if ((obj != null))
>        {
>            addToSessionCache(oid, obj);
>        }
>    }
> 
>    /**
>     * Lookup object with Identity oid in :
>     * sessionCache, if not found in sessionCache the lookup the
> applicationCache.
>     * Returns null if no matching id is found
>     */
>    public Object lookup(Identity oid)
>    {
>        LOG.debug("lookup()");
>        Object result = null;
>        CacheEntry entry = null;
> 
>        // first lookup if the object is available in the application
cache
>        result = lookupSessionCache(oid);
>        if (result!=null)
>                return result;
> 
>        // If not then lookup in the application cache :
>        entry = lookUpApplicationCache(oid);
> 
>        if (entry != null &&
>
entry.getOid().getObjectsTopLevelClass().equals(oid.getObjectsTopLevelCl
ass(
> )))
>        {
>                LOG.debug("Object found in Application Cache");
> 
>            Object objectFromApplicationCache = entry.getObject();
> 
>            if (objectFromApplicationCache == null)
>            {
>                remove(oid);
>                // make sure that we return null
>                result = null;
>            }
>            else
>            {
>                // Make a copy WITHOUT any reference to other object
>                // (just copy the java type : have a look at
> CopyHelper.copyJavaTypeProperties()
>                // for more information)
>                // Implementing IFlatClone is for optimization
> consideration as it's a really faster method !
>                if (entry.getObject() instanceof IFlatClone)
>                        result =
((IFlatClone)entry.getObject()).flatClone();
>                else
>                        result =
> CopyHelper.copyJavaTypeProperties(entry.getObject());
>                LOG.debug("Making a copy and adding it to the session
> cache");
>                addToSessionCache(oid, result);
>            }
>        }
>        else
>        {
>                LOG.debug("object NOT found in Application Cache");
>        }
>        return result;
>    }
> 
>        /**
>         * lookup in the session cache.
>         * @param oid
>         * @return
>         */
>        public Object lookupSessionCache(Identity oid)
>    {
>        LOG.debug("lookupSessionCache()");
>        Object result = null;
>        CacheEntry entry = null;
> 
>        entry = (CacheEntry) _sessionCache.get(buildKey(oid));
> 
>        if (entry != null &&
>
entry.getOid().getObjectsTopLevelClass().equals(oid.getObjectsTopLevelCl
ass(
> )))
>        {
>                LOG.debug("Object found in Session Cache");
> 
>                result = entry.getObject();
> 
>            if (result == null)
>            {
>                remove(oid);
>                // make sure that we return null
>                result = null;
>            }
>        }
>        else
>        {
>                LOG.debug("object NOT found in Session Cache");
>        }
>        return result;
>    }
> 
>    /**
>     * Removes an Object from the session cache and the application
cache.
>     */
>    public void remove(Identity oid)
>    {
>        LOG.debug("remove()");
>        if (oid != null)
>        {
>            _sessionCache.remove(buildKey(oid));
>            removeFromApplicationCache(oid);
>        }
>    }
> 
>    /**
>     * Add an objet in the session cache
>     * @param oid
>     * @param o
>     */
>    private void addToSessionCache(Identity oid, Object o)
>    {
>        LOG.debug("addToSessionCache()");
>        if ((_broker != null) && _broker.isInTransaction())
>        {
>                LOG.debug("putting object to session cache : " + o);
>            _sessionCache.put(buildKey(oid), new CacheEntry(o, oid));
>        }
>    }
> 
>    protected Integer buildKey(Identity oid)
>    {
>        int key = 0;
>        switch(cachingKeyType)
>        {
>            case 0:
>                key = oid.hashCode();
>                break;
>            case 1:
>                key = new
>
HashCodeBuilder().append(_broker.getPBKey().getAlias().hashCode()).appen
d(oi
> d.hashCode()).toHashCode();
>                break;
>            case 2:
>                key = new
>
HashCodeBuilder().append(_broker.getDescriptorRepository().hashCode()).a
ppen
> d(oid.hashCode()).toHashCode();
>                break;
>            case 3:
>                key = new
>
HashCodeBuilder().append(_broker.getDescriptorRepository().hashCode()).a
ppen
>
d(_broker.getPBKey().getAlias().hashCode()).append(oid.hashCode()).toHas
hCod
> e();
>                break;
>            default:
>                throw new OJBRuntimeException("Unexpected error,
> 'cacheType ="+cachingKeyType+"' was not supported");
>        }
>        return new Integer(key);
>    }
> 
>        public String toString()
>    {
>        ToStringBuilder buf = new ToStringBuilder(this,
> ToStringStyle.DEFAULT_STYLE);
>        buf.append("Count of Session objects",
> _sessionCache.keySet().size());
>        return buf.toString();
>    }
> 
>    protected void finalize()
>    {
>        LoggerFactory.getDefaultLogger().debug("Finalize ObjectCache:
> " + this.toString());
>    }
> 
>    //-----------------------------------------------------------
>    // PBStateListener methods
>    //-----------------------------------------------------------
> 
>    /**
>     * Before rollbacking clear the session cache (first level cache)
>     */
>    public void beforeRollback(PBStateEvent event)
>    {
>        if (LOG.isDebugEnabled())
>                LOG.debug("beforeRollback()");
>        _sessionCache.clear();
>    }
> 
>    /**
>     * Before committing the transaction transfert (not copy) the objet
>     * from session cache ( 1st level cache) to the application cache
>     * (2d level cache). Finally, clear the session cache.
>     */
>    public void beforeCommit(PBStateEvent event)
>    {
>        if (LOG.isDebugEnabled())
>                LOG.debug("beforeCommit()");
> 
>        for (Iterator iter = _sessionCache.entrySet().iterator();
> iter.hasNext(); ) {
>                Map.Entry entry = (Entry)iter.next();
>                if (LOG.isDebugEnabled())
>                        LOG.debug("Session Cache -> Application Cache :
" +
> ((CacheEntry)entry.getValue()).getObject());
> 
>  
> addToApplicationCache(((CacheEntry)entry.getValue()).getOid(),
> ((CacheEntry)entry.getValue()).getObject());
>        }
>        _sessionCache.clear();
>    }
> 
>    /**
>     * Before closing the PersistenceBroker insure that the session
> cache is cleared
>     */
>        public void beforeClose(PBStateEvent event)
>    {
>                if (LOG.isDebugEnabled())
>                        LOG.debug("Clearing the session cache");
>        _sessionCache.clear();
>    }
> 
>    public void afterRollback(PBStateEvent event) {}
>    public void afterCommit(PBStateEvent event) {}
>    public void afterBegin(PBStateEvent event) {}
>    public void beforeBegin(PBStateEvent event) {}
>    public void afterOpen(PBStateEvent event) {}
> 
>    //-----------------------------------------------------------
>    // abstract methods
>    //-----------------------------------------------------------
> 
>    /**
>         * Clear the application cache (second level cache)
>         */
>        protected abstract void clearApplicationCache();
> 
>    /**
>     * Add an object to the application cache (second level cache)
>         * @param key
>         * @param object
>         */
>        protected abstract void addToApplicationCache(Identity oid,
Object
> object) ;
> 
>        /**
>         * Remove an object from the application cache
>         * @param oid
>         */
>        protected abstract void removeFromApplicationCache(Identity
oid);
> 
>    /**
>     * Lookup for an object in the Application cache
>         * @param oid
>         * @return
>         */
>        protected abstract CacheEntry lookUpApplicationCache(Identity
oid);
> 
>    //-----------------------------------------------------------
>    // inner class
>    //-----------------------------------------------------------
>    public class CacheEntry
>    {
> 
>                /**
>                 *
>                 * @uml.property name="_oid"
>                 */
>                private final Identity _oid;
> 
>                /**
>                 *
>                 * @uml.property name="_object"
>                 */
>                private final Object _object;
> 
>        public CacheEntry(Object object, final Identity k)
>        {
>                _object = object;
>            _oid = k;
>        }
> 
>                /**
>                 * @return Returns the object.
>                 *
>                 * @uml.property name="_object"
>                 */
>                public Object getObject() {
>                        return _object;
>                }
> 
>                /**
>                 * @return Returns the oid.
>                 *
>                 * @uml.property name="_oid"
>                 */
>                public Identity getOid() {
>                        return _oid;
>                }
> 
>    }
> 
> }
> 
> *************************************
> 
> /*
> * Created on 7 d�c. 2004
> *
> */
> package com.mycompagny.fwk.cache;
> 
> import java.util.Hashtable;
> import java.util.Map;
> import java.util.Properties;
> 
> import org.apache.ojb.broker.Identity;
> import org.apache.ojb.broker.PersistenceBroker;
> import org.apache.ojb.broker.util.logging.Logger;
> import org.apache.ojb.broker.util.logging.LoggerFactory;
> 
> /**
> *
> * This class implements the abstract methods declared in
> AbstractTwoLevelObjectCache.
> * It's implement the application cache (second level cache)
> *
> */
> public class LocalTwoLevelObjectCache extends
AbstractTwoLevelObjectCache {
>        protected static final Logger LOG =
> LoggerFactory.getLogger(LocalTwoLevelObjectCache.class);
> 
>        protected static Map applicationCache = new Hashtable();
> 
>        /**
>         * @param broker
>         * @param prop
>         */
>        public LocalTwoLevelObjectCache(PersistenceBroker broker,
Properties
> prop) {
>                super(broker, prop);
>        }
> 
>        /* (non-Javadoc)
>         * @see
>
com.inetpsa.fwk.cache.AbstractTwoLevelObjectCache#lookUpApplicationCache
(org
> .apache.ojb.broker.Identity)
>         */
>        protected CacheEntry lookUpApplicationCache(Identity oid) {
>                LOG.debug("lookUpApplicationCache()");
>        CacheEntry entry = null;
>        entry = (CacheEntry) applicationCache.get(buildKey(oid));
> 
>        return entry;
>        }
> 
>        /* (non-Javadoc)
>         * @see
>
com.inetpsa.fwk.cache.AbstractTwoLevelObjectCache#removeFromApplicationC
ache
> (org.apache.ojb.broker.Identity)
>         */
>        protected void removeFromApplicationCache(Identity oid) {
>                applicationCache.remove(buildKey(oid));
>        }
> 
>        /* (non-Javadoc)
>         * @see
>
com.inetpsa.fwk.cache.AbstractTwoLevelObjectCache#addToApplicationCache(
java
> .lang.Object,
> java.lang.Object)
>         */
>        protected void addToApplicationCache(Identity oid, Object
object) {
>                applicationCache.put(buildKey(oid), new
> AbstractTwoLevelObjectCache.CacheEntry(object, oid));
>        }
> 
>        /* (non-Javadoc)
>         * @see
>
com.inetpsa.fwk.cache.AbstractTwoLevelObjectCache#clearApplicationCache(
)
>         */
>        protected void clearApplicationCache() {
>                applicationCache.clear();
>        }
> 
>        public Map getApplicationCache() {
>                return applicationCache;
>        }
> }
> 
> ***********************************
> 
> package com.mycompagny.fwk.cache;
> 
> /**
> *
> */
> public interface IFlatClone {
>        Object flatClone();
> }
> 
> *****************************
> 
> package com.mycompagny.fwk.cache;
> 
> import java.beans.PropertyDescriptor;
> import java.lang.reflect.InvocationTargetException;
> 
> import org.apache.commons.beanutils.PropertyUtils;
> 
> /**
> * @author e232093
> *
> */
> public class CopyHelper {
> 
>        private final static String JAVA_TYPE[] = {     "class
> java.lang.Long",
>  
> "long",
>  
> "class java.lang.Integer",
>  
> "int",
>  
> "class java.lang.Double",
>  
> "double",
>  
> "class java.lang.Float",
>  
> "float",
>  
> "class java.lang.String",
>  
> "char",
>  
> "class java.lang.Boolean",
>  
> "boolean"};
> 
>        public static Object copyJavaTypeProperties(Object pSource) {
>                Object result = result = getNewInstance(pSource);
>                PropertyDescriptor propDesc[] =
> PropertyUtils.getPropertyDescriptors(pSource);
>                for (int i=0; i<propDesc.length;i++) {
>                        String type =
> propDesc[i].getPropertyType().toString();
>                        String name = propDesc[i].getDisplayName();
>                        boolean writeable =
> PropertyUtils.isWriteable(pSource, name);
> 
>                        try {
>                                if (writeable &&
isTypeJavaSimple(type)) {
>                                        Object value =
> PropertyUtils.getSimpleProperty(pSource, name);
>  
> PropertyUtils.setSimpleProperty(result, name, value);
>                                }
>                        } catch (IllegalAccessException e) {
>                                // TODO Auto-generated catch block
>                                e.printStackTrace();
>                        } catch (InvocationTargetException e) {
>                                // TODO Auto-generated catch block
>                                e.printStackTrace();
>                        } catch (NoSuchMethodException e) {
>                                // TODO Auto-generated catch block
>                                e.printStackTrace();
>                        }
>                }
>                return result;
>        }
> 
>        /**
>         * Retourne une nouvelle instance du m�me type port� par obj.
>         * @param obj
>         * @return
>         */
>        private static Object getNewInstance(Object obj) {
>                Object o = null;
>                try {
>                        o =  obj.getClass().newInstance();
>                } catch (InstantiationException e) {
>                        // TODO Auto-generated catch block
>                        e.printStackTrace();
>                } catch (IllegalAccessException e) {
>                        // TODO Auto-generated catch block
>                        e.printStackTrace();
>                }
>                return o;
>        }
> 
>        private static boolean isTypeJavaSimple(String pType) {
>                for(int i=0; i<JAVA_TYPE.length; i++) {
>                        if (pType.equalsIgnoreCase(JAVA_TYPE[i]))
>                                return true;
>                }
>                return false;
>        }
> }
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]
> 
> 
> 

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
*** We scanned this email for malicious content ***
*** IMPORTANT: Do not open attachments from unrecognized senders  ***
*** MailSystem ASTON ***

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to