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