You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-dev@db.apache.org by ar...@apache.org on 2004/01/04 03:34:37 UTC

cvs commit: db-ojb/src/java/org/apache/ojb/broker/cache ObjectCacheDefaultImpl.java

arminw      2004/01/03 18:34:37

  Modified:    xdocs    objectcache.xml
               src/test/org/apache/ojb repository_database.xml
               src/java/org/apache/ojb/broker/cache
                        ObjectCacheDefaultImpl.java
  Log:
  - add a simple mechanism to keep ObjectCache implementation
  in sync with DB
  
  - enable 'useAutoSync' to fix TransactionImpl#flush() bug
  
  - update documentation
  
  Revision  Changes    Path
  1.8       +38 -13    db-ojb/xdocs/objectcache.xml
  
  Index: objectcache.xml
  ===================================================================
  RCS file: /home/cvs/db-ojb/xdocs/objectcache.xml,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- objectcache.xml	2 Dec 2003 01:52:32 -0000	1.7
  +++ objectcache.xml	4 Jan 2004 02:34:37 -0000	1.8
  @@ -128,14 +128,16 @@
   <ul>
       <li>
       in <a href="OJB.properties.txt">OJB.properties</a> file, to declare the standard
  -    (default) ObjectCache implementation.
  +    (default) ObjectCache implementation. The declared ObjectCache implementation was
  +        initialized with default properties, it's not possible to pass additional
  +        configuration properties on this level of declaration.
       <source>
   #-------------------------------------------------------------------
   # Object cache
   #-------------------------------------------------------------------
   # The ObjectCacheClass entry tells OJB which concrete instance Cache
   # implementation is to be used.
  -ObjectCacheClass=org.apache.ojb.broker.cache.ObjectCacheDefaultImpl
  +ObjectCacheClass=org.apache.ojb.broker.cache.ObjectCachePerBrokerImpl
   #
       </source>
       <br/>
  @@ -143,11 +145,14 @@
   
   
       <li>on jdbc-connection-descriptor level, to declare ObjectCache implementation
  -    on a per connection/user level, e.g.
  +    on a per connection/user level. Additional configuration properties can be passed
  +    by using <tt>attribute</tt> element entries:
       <source><![CDATA[
   <jdbc-connection-descriptor ...>
   ...
  -    <object-cache class="org.apache.ojb.broker.cache.ObjectCachePerBrokerImpl">
  +    <object-cache class="org.apache.ojb.broker.cache.ObjectCacheDefaultImpl">
  +        <attribute attribute-name="timeout" attribute-value="900"/>
  +        <attribute attribute-name="useAutoSync" attribute-value="true"/>
       </object-cache>
   ...
   </jdbc-connection-descriptor>
  @@ -156,7 +161,8 @@
       </li>
   
       <li>on class-descriptor level, to declare ObjectCache implementation
  -    on a per class level, e.g.
  +    on a per class level.  Additional configuration properties can be passed
  +    by using <tt>attribute</tt> element entries:
       <source><![CDATA[
   <class-descriptor
      	  class="org.apache.ojb.broker.util.sequence.HighLowSequence"
  @@ -173,7 +179,7 @@
   
   The priority of the declared object-cache elements are:
   <p>
  -<i>per class > per jdbc descriptor > standard</i>
  +<i>per class > per jdbc-connection-descriptor > standard</i>
   </p>
   
   E.g. if you declare ObjectCache 'DefaultCache' as standard and set
  @@ -235,16 +241,38 @@
             If expired, the cached object was discarded - default was 900 sec.
      </td>
   </tr>
  +<tr>
  +    <td>autoSync</td>
  +    <td>
  +         If set <tt>true</tt> all cached/looked up objects within a PB-transaction are traced.
  +         If the the PB-transaction was aborted all traced objects will be removed from
  +         cache. Default is <tt>false</tt>.
  +         <p>
  +         NOTE: This does not prevent "dirty-reads" by concurrent threads (more info see above).
  +         </p>
  +         <p>
  +         It's not a smart solution for keeping cache in sync with DB but should do the job
  +         in most cases.
  +         <br/>
  +         E.g. if you lookup 1000 objects within a transaction and modify one object and then abort the
  +         transaction, 1000 objects will be passed to cache, 1000 objects will be traced and
  +         all 1000 objects will be removed from cache. If you read these objects without tx or
  +         in a former tx and then modify one object in a tx and abort the tx, only one object was
  +         traced/removed.
  +         </p>
  +   </td>
  +</tr>
   </table>
   </p>
   
   <p>
       Recommendation:
       <br/>If you take care of cache synchronization and be aware of dirty
  -    reads, this implementation is useful for read-only or less insert/update
  +    reads, this implementation is useful for read-only or less update
       centric classes.
   </p>
   
  +
   <br/><p>
   <b>ObjectCachePerBrokerImpl</b><br/>
   This local cache implementation allows to have dedicated caches per PersistenceBroker instance.
  @@ -260,7 +288,6 @@
   </p>
   
   
  -
   <br/><p>
   <b>ObjectCacheJCSImpl</b><br/>
   A shared <code>ObjectCache</code> implementation using a JCS region for
  @@ -281,17 +308,15 @@
   
   <br/><p>
   <b>ObjectCacheOSCacheImpl</b><br/>
  -
  -<br/>
   A implementation using OpenSymphony's OSCache. More
   info see <a href="howto-work-with-clustering.html">here</a>.
   </p>
   
   
  -<br/><p>
  -In <code>cache</code> package you can find some more implementations.
  +<p>
  +Additional <tt>ObjectCache</tt> implementations can be found in
  +<tt>org.apache.ojb.broker.cache</tt> package.
   </p>
  -<br/>
   
   </subsection>
   
  
  
  
  1.18      +6 -1      db-ojb/src/test/org/apache/ojb/repository_database.xml
  
  Index: repository_database.xml
  ===================================================================
  RCS file: /home/cvs/db-ojb/src/test/org/apache/ojb/repository_database.xml,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- repository_database.xml	27 Nov 2003 16:42:12 -0000	1.17
  +++ repository_database.xml	4 Jan 2004 02:34:37 -0000	1.18
  @@ -31,6 +31,11 @@
           ignoreAutoCommitExceptions="false"
        >
   
  +        <object-cache class="org.apache.ojb.broker.cache.ObjectCacheDefaultImpl">
  +            <attribute attribute-name="timeout" attribute-value="900"/>
  +            <attribute attribute-name="useAutoSync" attribute-value="true"/>
  +        </object-cache>
  +
           <connection-pool
               maxActive="21"
               validationQuery="" />
  
  
  
  1.17      +119 -7    db-ojb/src/java/org/apache/ojb/broker/cache/ObjectCacheDefaultImpl.java
  
  Index: ObjectCacheDefaultImpl.java
  ===================================================================
  RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/cache/ObjectCacheDefaultImpl.java,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- ObjectCacheDefaultImpl.java	31 Dec 2003 12:01:56 -0000	1.16
  +++ ObjectCacheDefaultImpl.java	4 Jan 2004 02:34:37 -0000	1.17
  @@ -59,11 +59,16 @@
   import java.util.Hashtable;
   import java.util.Map;
   import java.util.Properties;
  +import java.util.List;
  +import java.util.ArrayList;
  +import java.util.Iterator;
   
   import org.apache.commons.lang.builder.ToStringBuilder;
   import org.apache.commons.lang.builder.ToStringStyle;
   import org.apache.ojb.broker.Identity;
   import org.apache.ojb.broker.PersistenceBroker;
  +import org.apache.ojb.broker.PBStateListener;
  +import org.apache.ojb.broker.PBStateEvent;
   import org.apache.ojb.broker.util.logging.Logger;
   import org.apache.ojb.broker.util.logging.LoggerFactory;
   
  @@ -71,7 +76,8 @@
    * This global ObjectCache stores all Objects loaded by the <code>PersistenceBroker</code>
    * from a DB using a static {@link java.util.Map}. This means each {@link ObjectCache}
    * instance associated with all {@link PersistenceBroker} instances use the same
  - * <code>Map</code> to cache objects.
  + * <code>Map</code> to cache objects. This could lead in "dirty-reads" (similar to read-uncommitted
  + * mode in DB) when a concurrent thread look up same object modified by another thread.
    * <br/>
    * When the PersistenceBroker tries to get an Object by its {@link Identity}.
    * It first lookups the cache if the object has been already loaded and cached.
  @@ -96,8 +102,31 @@
    *          If expired the cached object was not returned
    *          on lookup call (and removed from cache). Default timeout
    *          value is 900 seconds.
  - *          <br/>NOTE: Objects internal cached via {@link SoftReference}, so
  + *          <p>
  + *          NOTE: Objects internal cached via {@link SoftReference}, so
    *          lifetime of cached object is limited by the GC too.
  + *          </p>
  + *    </td>
  + * </tr>
  + * <tr>
  + *     <td>autoSync</td>
  + *     <td>
  + *          If set <tt>true</tt> all cached/looked up objects within a PB-transaction are traced.
  + *          If the the PB-transaction was aborted all traced objects will be removed from
  + *          cache. Default is <tt>false</tt>.
  + *          <p>
  + *          NOTE: This does not prevent "dirty-reads" (more info see above).
  + *          </p>
  + *          <p>
  + *          It's not a smart solution for keeping cache in sync with DB but should do the job
  + *          in most cases.
  + *          <br/>
  + *          E.g. if you lookup 1000 objects within a transaction and modify one object and then abort the
  + *          transaction, 1000 objects will be passed to cache, 1000 objects will be traced and
  + *          all 1000 objects will be removed from cache. If you read these objects without tx or
  + *          in a former tx and then modify one object in a tx and abort the tx, only one object was
  + *          traced/removed.
  + *          </p>
    *    </td>
    * </tr>
    * </table>
  @@ -107,10 +136,10 @@
    * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
    * @version $Id$
    */
  -public class ObjectCacheDefaultImpl implements ObjectCache
  +public class ObjectCacheDefaultImpl implements ObjectCache, PBStateListener
   {
       /**
  -     * the hashtable holding all cached object
  +     * static Map held all cached objects
        */
       protected static Map objectTable = new Hashtable();
   
  @@ -118,6 +147,9 @@
       private static long failCount = 0;
       private static long gcCount = 0;
   
  +    protected PersistenceBroker broker;
  +    private List identitiesInWork;
  +    private boolean useAutoSync;
       /**
        * Timeout of the cached objects. Default was 900 seconds.
        */
  @@ -129,6 +161,22 @@
       public ObjectCacheDefaultImpl(PersistenceBroker broker, Properties prop)
       {
           timeout = prop == null ? timeout : ( Long.parseLong( prop.getProperty( "timeout", "" + timeout ) ) *1000);
  +
  +        useAutoSync = prop == null ?
  +                false : ( Boolean.valueOf((prop.getProperty( "autoSync", "false")).trim())).booleanValue();
  +        if(useAutoSync && broker != null)
  +        {
  +            // we add this instance as a permanent PBStateListener
  +            broker.addListener(this, true);
  +        }
  +        if(useAutoSync && broker == null)
  +        {
  +            LoggerFactory.getDefaultLogger().info("[" + ObjectCacheDefaultImpl.class.getName() +
  +                    "] Can't enable 'useAutoSync', because given PB instance is null");
  +        }
  +
  +        identitiesInWork = new ArrayList();
  +        this.broker = broker;
       }
   
       /**
  @@ -137,6 +185,7 @@
       public void clear()
       {
           objectTable.clear();
  +        identitiesInWork.clear();
       }
   
       /**
  @@ -148,6 +197,7 @@
       {
           if ((obj != null))
           {
  +            traceIdentity(oid);
               objectTable.put(oid, new CacheEntry(obj));
           }
       }
  @@ -172,10 +222,14 @@
                   remove CacheEntry from map
                   */
                   gcCount++;
  -                objectTable.remove(oid);
  +                remove(oid);
                   // make sure that we return null
                   result = null;
               }
  +            else
  +            {
  +                traceIdentity(oid);
  +            }
           }
           else
           {
  @@ -191,6 +245,7 @@
       {
           if (oid != null)
           {
  +            removeTracedIdentity(oid);
               objectTable.remove(oid);
           }
       }
  @@ -205,10 +260,67 @@
           return buf.toString();
       }
   
  -    public void finalize()
  +    protected void finalize()
       {
           Logger logger = LoggerFactory.getDefaultLogger();
           logger.debug("Finalize ObjectCache: "+this.toString());
  +    }
  +
  +    private void traceIdentity(Identity oid)
  +    {
  +        if(useAutoSync && broker != null && broker.isInTransaction())
  +        {
  +            identitiesInWork.add(oid);
  +        }
  +    }
  +
  +    private void removeTracedIdentity(Identity oid)
  +    {
  +        identitiesInWork.remove(oid);
  +    }
  +
  +    private void synchronizeWithTracedObjects()
  +    {
  +        Identity oid;
  +        for (Iterator iterator = identitiesInWork.iterator(); iterator.hasNext();)
  +        {
  +            oid = (Identity) iterator.next();
  +            objectTable.remove(oid);
  +        }
  +    }
  +
  +    public void beforeRollback(PBStateEvent event)
  +    {
  +        synchronizeWithTracedObjects();
  +    }
  +
  +    public void beforeClose(PBStateEvent event)
  +    {
  +        identitiesInWork.clear();
  +    }
  +
  +    public void afterRollback(PBStateEvent event)
  +    {
  +    }
  +
  +    public void afterCommit(PBStateEvent event)
  +    {
  +    }
  +
  +    public void beforeCommit(PBStateEvent event)
  +    {
  +    }
  +
  +    public void afterBegin(PBStateEvent event)
  +    {
  +    }
  +
  +    public void beforeBegin(PBStateEvent event)
  +    {
  +    }
  +
  +    public void afterOpen(PBStateEvent event)
  +    {
       }
   
       //-----------------------------------------------------------
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org