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 2006/07/15 16:20:27 UTC
svn commit: r422231 [3/4] - in /db/ojb/trunk/src/java/org/apache/ojb/broker:
./ accesslayer/ accesslayer/sql/ cache/ core/ locking/ metadata/
metadata/fieldaccess/
Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerInMemoryImpl.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerInMemoryImpl.java?rev=422231&r1=422230&r2=422231&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerInMemoryImpl.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerInMemoryImpl.java Sat Jul 15 07:20:25 2006
@@ -16,15 +16,16 @@
*/
import java.io.Serializable;
-import java.util.Collection;
import java.util.HashMap;
-import java.util.Hashtable;
import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
+import org.apache.commons.collections.list.TreeList;
+import org.apache.commons.lang.SystemUtils;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
-import org.apache.commons.lang.SystemUtils;
/**
* This implementation of the {@link LockManager} interface supports a simple, fast, non-blocking
@@ -32,17 +33,20 @@
*
* @version $Id$
*/
-public class LockManagerInMemoryImpl implements LockManager
+public class LockManagerInMemoryImpl implements LockManager, Serializable
{
private Logger log = LoggerFactory.getLogger(LockManagerInMemoryImpl.class);
- private static long CLEANUP_FREQUENCY = 1000; // 1000 milliseconds.
- private static int MAX_LOCKS_TO_CLEAN = 300;
+ /** The period to search for timed out locks. */
+ private long cleanupFrequency = 10000; // milliseconds.
+ /** The number of lock entries to check for timeout */
+ private int maxLocksToClean = 500;
/**
* MBAIRD: a LinkedHashMap returns objects in the order you put them in,
* while still maintaining an O(1) lookup like a normal hashmap. We can then
* use this to get the oldest entries very quickly, makes cleanup a breeze.
*/
- private final HashMap locktable = new HashMap();
+ private final Map resourceLockMap = new LinkedHashMap(70);
+ private final Map keyLockMap = new HashMap();
private final LockIsolationManager lockStrategyManager = new LockIsolationManager();
private long m_lastCleanupAt = System.currentTimeMillis();
private long lockTimeout;
@@ -54,6 +58,41 @@
this.lockTimeout = DEFAULT_LOCK_TIMEOUT;
}
+
+ /** The period to search for timed out locks. */
+ public long getCleanupFrequency()
+ {
+ return cleanupFrequency;
+ }
+
+ /** Set the period to search for timed out locks. */
+ public void setCleanupFrequency(long cleanupFrequency)
+ {
+ this.cleanupFrequency = cleanupFrequency;
+ }
+
+ /**
+ * The number of lock entries to check for timeout
+ * on each lock cleanup run.
+ *
+ * @see #getCleanupFrequency()
+ */
+ public int getMaxLocksToClean()
+ {
+ return maxLocksToClean;
+ }
+
+ /**
+ * Set the number of lock entries to check for timeout
+ * on each lock cleanup run.
+ *
+ * @see #setCleanupFrequency(long)
+ */
+ public void setMaxLocksToClean(int maxLocksToClean)
+ {
+ this.maxLocksToClean = maxLocksToClean;
+ }
+
public long getLockTimeout()
{
return lockTimeout;
@@ -66,6 +105,7 @@
/**
* NOOP
+ *
* @return Always '0'
*/
public long getBlockTimeout()
@@ -73,439 +113,199 @@
return 0;
}
- /**
- * NOOP
- */
+ /** NOOP */
public void setBlockTimeout(long timeout)
{
+ log.info("block-timeout setting is not supported");
}
public String getLockInfo()
{
+ // synchronize locks before return locking information
+ checkTimedOutLocks();
+
String eol = SystemUtils.LINE_SEPARATOR;
StringBuffer msg = new StringBuffer("Class: " + LockManagerInMemoryImpl.class.getName() + eol);
msg.append("lock timeout: ").append(getLockTimeout()).append(" [ms]").append(eol);
- msg.append("concurrent lock owners: ").append(locktable.size()).append(eol);
+ msg.append("cleanup frequency: ").append(getCleanupFrequency()).append(" [ms]").append(eol);
+ msg.append("max locks to clean: ").append(getMaxLocksToClean()).append(eol);
+ msg.append(eol);
+ msg.append("lock map size: ").append(resourceLockMap.size()).append(eol);
msg.append("timed out write locks: ").append(timeoutCounterWrite).append(eol);
msg.append("timed out read locks: ").append(timeoutCounterRead).append(eol);
return msg.toString();
}
- public boolean readLock(Object key, Object resourceId, IsolationLevels isolationLevel)
+ public boolean readLock(final Object key, final Object resourceId, final IsolationLevels isolationLevel)
{
+ if(isolationLevel.ignoreForLock()) return true;
+
if(log.isDebugEnabled()) log.debug("LM.readLock(tx-" + key + ", " + resourceId + ")");
checkTimedOutLocks();
- if(isolationLevel.ignoreForLock())
- {
- return true;
- }
- else
- {
- LockEntry reader = new LockEntry(resourceId,
- key,
- System.currentTimeMillis(),
- isolationLevel,
- LockEntry.LOCK_READ);
- LockIsolation ls = lockStrategyManager.getStrategyFor(isolationLevel);
- return addReaderIfPossibleInternal(reader, ls.allowMultipleRead(), ls.allowReadWhenWrite());
- }
- }
+ LockEntry reader = new LockEntry(resourceId,
+ key,
+ System.currentTimeMillis(),
+ LockEntry.LOCK_READ);
+ LockIsolation ls = lockStrategyManager.getStrategyFor(isolationLevel);
- private boolean addReaderIfPossibleInternal(LockEntry reader, boolean allowMultipleReader,
- boolean allowReaderWhenWriteLock)
- {
- boolean result = false;
+ boolean result;
ObjectLocks objectLocks;
- Object oid = reader.getResourceId();
/**
* MBAIRD: We need to synchronize the get/put so we don't have two threads
* competing to check if something is locked and double-locking it.
*/
- synchronized(locktable)
+ synchronized(resourceLockMap)
{
- objectLocks = (ObjectLocks) locktable.get(oid);
+ objectLocks = (ObjectLocks) resourceLockMap.get(resourceId);
if(objectLocks == null)
{
// no write or read lock, go on
- objectLocks = new ObjectLocks();
- locktable.put(oid, objectLocks);
- objectLocks.addReader(reader);
- result = true;
- }
- else
- {
- // ObjectLocks exist, first check for a write lock
- LockEntry writer = objectLocks.getWriter();
- if(writer != null)
- {
- // if writer is owned by current entity, read lock is
- // successful (we have an write lock)
- if(writer.isOwnedBy(reader.getKey()))
- {
- result = true;
- }
- else
- {
- // if read lock is allowed when different entity hold write lock
- // go on if multiple reader allowed, else do nothing
- if(allowReaderWhenWriteLock && allowMultipleReader)
- {
- objectLocks.addReader(reader);
- result = true;
- }
- else
- {
- result = false;
- }
- }
- }
- else
- {
- // no write lock exist, check for existing read locks
- if(objectLocks.getReaders().size() > 0)
- {
- // if we have already an read lock, do nothing
- if(objectLocks.getReader(reader.getKey()) != null)
- {
- result = true;
- }
- else
- {
- // we have read locks of other entities, add read lock
- // if allowed
- if(allowMultipleReader)
- {
- objectLocks.addReader(reader);
- result = true;
- }
- }
- }
- else
- {
- // no read locks exist, so go on
- objectLocks.addReader(reader);
- result = true;
- }
- }
- }
- }
- return result;
- }
-
- /**
- * Remove an read lock.
- */
- public boolean removeReader(Object key, Object resourceId)
- {
- boolean result = false;
- ObjectLocks objectLocks;
- synchronized(locktable)
- {
- objectLocks = (ObjectLocks) locktable.get(resourceId);
- if(objectLocks != null)
- {
- /**
- * MBAIRD, last one out, close the door and turn off the lights.
- * if no locks (readers or writers) exist for this object, let's remove
- * it from the locktable.
- */
- Map readers = objectLocks.getReaders();
- result = readers.remove(key) != null;
- if((objectLocks.getWriter() == null) && (readers.size() == 0))
- {
- locktable.remove(resourceId);
- }
- }
- }
- return result;
- }
-
- /**
- * Remove an write lock.
- */
- public boolean removeWriter(Object key, Object resourceId)
- {
- boolean result = false;
- ObjectLocks objectLocks;
- synchronized(locktable)
- {
- objectLocks = (ObjectLocks) locktable.get(resourceId);
- if(objectLocks != null)
- {
- /**
- * MBAIRD, last one out, close the door and turn off the lights.
- * if no locks (readers or writers) exist for this object, let's remove
- * it from the locktable.
- */
- LockEntry entry = objectLocks.getWriter();
- if(entry != null && entry.isOwnedBy(key))
- {
- objectLocks.setWriter(null);
- result = true;
-
- // no need to check if writer is null, we just set it.
- if(objectLocks.getReaders().size() == 0)
- {
- locktable.remove(resourceId);
- }
- }
+ objectLocks = new ObjectLocks(resourceId);
+ resourceLockMap.put(resourceId, objectLocks);
}
+ result = objectLocks.addReader(reader, ls.allowMultipleRead(), ls.allowReadWhenWrite());
+ if(result) associateKeyWithObjectLock(reader.getKey(), objectLocks);
}
return result;
}
- public boolean releaseLock(Object key, Object resourceId)
+ public boolean releaseLock(final Object key, final Object resourceId)
{
if(log.isDebugEnabled()) log.debug("LM.releaseLock(tx-" + key + ", " + resourceId + ")");
- boolean result = removeReader(key, resourceId);
- // if no read lock could be removed, try write lock
- if(!result)
+
+ synchronized(resourceLockMap)
{
- result = removeWriter(key, resourceId);
+ ObjectLocks lock = (ObjectLocks) resourceLockMap.get(resourceId);
+ return lock != null && lock.releaseLock(key);
}
- return result;
}
- /**
- * @see LockManager#releaseLocks(Object)
- */
- public void releaseLocks(Object key)
+ /** @see LockManager#releaseLocks(Object) */
+ public void releaseLocks(final Object key)
{
if(log.isDebugEnabled()) log.debug("LM.releaseLocks(tx-" + key + ")");
checkTimedOutLocks();
- releaseLocksInternal(key);
+ doReleaseLocks(key);
+ //System.out.println("resourceLockMap: " + resourceLockMap.size());
+ //System.out.println("keyLockMap: " + keyLockMap.size());
}
- private void releaseLocksInternal(Object key)
+ public boolean writeLock(final Object key, final Object resourceId, final IsolationLevels isolationLevel)
{
- synchronized(locktable)
- {
- Collection values = locktable.values();
- ObjectLocks entry;
- for(Iterator iterator = values.iterator(); iterator.hasNext();)
- {
- entry = (ObjectLocks) iterator.next();
- entry.removeReader(key);
- if(entry.getWriter() != null && entry.getWriter().isOwnedBy(key))
- {
- entry.setWriter(null);
- }
- }
- }
- }
+ if(isolationLevel.ignoreForLock()) return true;
- public boolean writeLock(Object key, Object resourceId, IsolationLevels isolationLevel)
- {
if(log.isDebugEnabled()) log.debug("LM.writeLock(tx-" + key + ", " + resourceId + ")");
checkTimedOutLocks();
- if(isolationLevel.ignoreForLock())
- {
- return true;
- }
- else
- {
- LockEntry writer = new LockEntry(resourceId,
- key,
- System.currentTimeMillis(),
- isolationLevel,
- LockEntry.LOCK_WRITE);
- LockIsolation ls = lockStrategyManager.getStrategyFor(isolationLevel);
- return setWriterIfPossibleInternal(writer, ls.allowWriteWhenRead());
- }
- }
+ LockEntry writer = new LockEntry(resourceId,
+ key,
+ System.currentTimeMillis(),
+ LockEntry.LOCK_WRITE);
+ LockIsolation ls = lockStrategyManager.getStrategyFor(isolationLevel);
- private boolean setWriterIfPossibleInternal(LockEntry writer, boolean allowReaders)
- {
- boolean result = false;
+ boolean result;
ObjectLocks objectLocks;
/**
* MBAIRD: We need to synchronize the get/put so we don't have two threads
* competing to check if something is locked and double-locking it.
*/
- synchronized(locktable)
+ synchronized(resourceLockMap)
{
- objectLocks = (ObjectLocks) locktable.get(writer.getResourceId());
+ objectLocks = (ObjectLocks) resourceLockMap.get(resourceId);
// if we don't upgrade, go on
if(objectLocks == null)
{
// no locks for current entity exist, so go on
- objectLocks = new ObjectLocks();
- objectLocks.setWriter(writer);
- locktable.put(writer.getResourceId(), objectLocks);
- result = true;
- }
- else
- {
- // the ObjectLock exist, check if there is already a write lock
- LockEntry oldWriter = objectLocks.getWriter();
- if(oldWriter != null)
- {
- // if already a write lock exists, check owner
- if(oldWriter.isOwnedBy(writer.getKey()))
- {
- // if current entity has already a write lock
- // signal success
- result = true;
- }
- }
- else
- {
- // current ObjectLock has no write lock, so check for readers
- int readerSize = objectLocks.getReaders().size();
- if(readerSize > 0)
- {
- // does current entity have already an read lock
- if(objectLocks.getReader(writer.getKey()) != null)
- {
- if(readerSize == 1)
- {
- // only current entity has a read lock, so go on
- objectLocks.readers.remove(writer.getKey());
- objectLocks.setWriter(writer);
- result = true;
- }
- else
- {
- // current entity and others have already a read lock
- // if aquire a write is allowed, go on
- if(allowReaders)
- {
- objectLocks.readers.remove(writer.getKey());
- objectLocks.setWriter(writer);
- result = true;
- }
- }
- }
- else
- {
- // current entity has no read lock, but others
- // if aquire a write is allowed, go on
- if(allowReaders)
- {
- objectLocks.setWriter(writer);
- result = true;
- }
- }
- }
- else
- {
- // no readers and writers, so go on if we don't upgrade
- objectLocks.setWriter(writer);
- result = true;
- }
- }
+ objectLocks = new ObjectLocks(resourceId);
+ resourceLockMap.put(resourceId, objectLocks);
}
+ result = objectLocks.addWriter(writer, ls.allowWriteWhenRead());
+ if(result) associateKeyWithObjectLock(writer.getKey(), objectLocks);
}
return result;
}
- public boolean upgradeLock(Object key, Object resourceId, IsolationLevels isolationLevel)
+ public boolean upgradeLock(final Object key, final Object resourceId, final IsolationLevels isolationLevel)
{
+ if(isolationLevel.ignoreForLock()) return true;
+
if(log.isDebugEnabled()) log.debug("LM.upgradeLock(tx-" + key + ", " + resourceId + ")");
return writeLock(key, resourceId, isolationLevel);
}
- /**
- * @see LockManager#hasWrite(Object, Object)
- */
- public boolean hasWrite(Object key, Object resourceId)
+ /** @see LockManager#hasWrite(Object, Object) */
+ public boolean hasWrite(final Object key, final Object resourceId)
{
if(log.isDebugEnabled()) log.debug("LM.hasWrite(tx-" + key + ", " + resourceId + ")");
checkTimedOutLocks();
- return hasWriteLockInternal(resourceId, key);
- }
- private boolean hasWriteLockInternal(Object resourceId, Object key)
- {
- boolean result = false;
ObjectLocks objectLocks;
- synchronized(locktable)
+ synchronized(resourceLockMap)
{
- objectLocks = (ObjectLocks) locktable.get(resourceId);
+ objectLocks = (ObjectLocks) resourceLockMap.get(resourceId);
if(objectLocks != null)
{
- LockEntry writer = objectLocks.getWriter();
- if(writer != null)
- {
- result = writer.isOwnedBy(key);
- }
+ return objectLocks.hasWriteLock(key);
}
}
- return result;
+ return false;
}
- public boolean hasUpgrade(Object key, Object resourceId)
+ public boolean hasUpgrade(final Object key, final Object resourceId)
{
if(log.isDebugEnabled()) log.debug("LM.hasUpgrade(tx-" + key + ", " + resourceId + ")");
return hasWrite(key, resourceId);
}
- /**
- * @see LockManager#hasRead(Object, Object)
- */
- public boolean hasRead(Object key, Object resourceId)
+ /** @see LockManager#hasRead(Object, Object) */
+ public boolean hasRead(final Object key, final Object resourceId)
{
if(log.isDebugEnabled()) log.debug("LM.hasRead(tx-" + key + ", " + resourceId + ')');
checkTimedOutLocks();
- return hasReadLockInternal(resourceId, key);
- }
- private boolean hasReadLockInternal(Object resourceId, Object key)
- {
- boolean result = false;
ObjectLocks objectLocks;
- synchronized(locktable)
+ synchronized(resourceLockMap)
{
- objectLocks = (ObjectLocks) locktable.get(resourceId);
+ objectLocks = (ObjectLocks) resourceLockMap.get(resourceId);
if(objectLocks != null)
{
- LockEntry reader = objectLocks.getReader(key);
- if(reader != null || (objectLocks.getWriter() != null && objectLocks.getWriter().isOwnedBy(key)))
- {
- result = true;
- }
+ return objectLocks.hasReadLock(key);
}
}
- return result;
+ return false;
}
- /**
- *
- */
+ /** The number of locked objects. */
public int lockedObjects()
{
- return locktable.size();
+ return resourceLockMap.size();
}
+ /** Internal method to detect and remove timed out locks. */
private void checkTimedOutLocks()
{
- if(System.currentTimeMillis() - m_lastCleanupAt > CLEANUP_FREQUENCY)
+ if(System.currentTimeMillis() - m_lastCleanupAt > cleanupFrequency)
{
removeTimedOutLocks(getLockTimeout());
m_lastCleanupAt = System.currentTimeMillis();
}
}
- /**
- * removes all timed out lock entries from the persistent storage.
- * The timeout value can be set in the OJB properties file.
- */
+ /** removes all timed out lock entries from the persistent storage. */
private void removeTimedOutLocks(long timeout)
{
int count = 0;
long maxAge = System.currentTimeMillis() - timeout;
- boolean breakFromLoop = false;
ObjectLocks temp;
- synchronized(locktable)
+ synchronized(resourceLockMap)
{
- Iterator it = locktable.values().iterator();
+ Iterator it = resourceLockMap.values().iterator();
/**
* run this loop while:
* - we have more in the iterator
* - the breakFromLoop flag hasn't been set
* - we haven't removed more than the limit for this cleaning iteration.
*/
- while(it.hasNext() && !breakFromLoop && (count <= MAX_LOCKS_TO_CLEAN))
+ while(it.hasNext() && (count <= maxLocksToClean))
{
temp = (ObjectLocks) it.next();
if(temp.getWriter() != null)
@@ -517,32 +317,54 @@
++timeoutCounterWrite;
}
}
- if(temp.getYoungestReader() < maxAge)
+ if(temp.readers != null && temp.readers.size() > 0)
{
// all readers are older than timeout.
- temp.getReaders().clear();
- ++timeoutCounterRead;
- if(temp.getWriter() == null)
+ if(temp.lastReader < maxAge)
{
- // all readers and writer are older than timeout,
- // remove the objectLock from the iterator (which
- // is backed by the map, so it will be removed.
- it.remove();
+ timeoutCounterRead += temp.getReaders().size();
+ if(temp.getWriter() == null)
+ {
+ // all readers and writer are older than timeout,
+ // remove the objectLock from the iterator (which
+ // is backed by the map, so it will be removed.
+ it.remove();
+ }
+ else
+ {
+ temp.getReaders().clear();
+ }
+ }
+ else
+ {
+ // there are outdated read locks
+ if(temp.eldestReader < maxAge)
+ {
+ // we need to walk each reader.
+ Iterator readerIt = temp.getReaders().values().iterator();
+ LockEntry readerLock;
+ while(readerIt.hasNext())
+ {
+ readerLock = (LockEntry) readerIt.next();
+ if(readerLock.getTimestamp() < maxAge)
+ {
+ // this read lock is old, remove it.
+ readerIt.remove();
+ ++timeoutCounterRead;
+ }
+ }
+ }
+ else
+ {
+ // nothing to do
+ }
}
}
else
{
- // we need to walk each reader.
- Iterator readerIt = temp.getReaders().values().iterator();
- LockEntry readerLock;
- while(readerIt.hasNext())
+ if(temp.getWriter() == null)
{
- readerLock = (LockEntry) readerIt.next();
- if(readerLock.getTimestamp() < maxAge)
- {
- // this read lock is old, remove it.
- readerIt.remove();
- }
+ it.remove();
}
}
count++;
@@ -550,168 +372,294 @@
}
}
+ private void associateKeyWithObjectLock(Object key, ObjectLocks lock)
+ {
+ List list = (List) keyLockMap.get(key);
+ if(list == null)
+ {
+ list = new TreeList();
+ keyLockMap.put(key, list);
+ }
+ if(!list.contains(lock)) list.add(lock);
+ }
+
+ private void doReleaseLocks(Object key)
+ {
+ synchronized(resourceLockMap)
+ {
+ List list = (List) keyLockMap.get(key);
+ if(list != null)
+ {
+ for(int i = 0; i < list.size(); i++)
+ {
+ ObjectLocks lock = (ObjectLocks) list.get(i);
+ lock.releaseLock(key);
+ if(lock.isEmpty()) resourceLockMap.remove(lock.getResourceId());
+ }
+ keyLockMap.remove(key);
+ }
+ }
+ }
+
//===============================================================
// inner class
//===============================================================
- static final class ObjectLocks
+ private static final class ObjectLocks implements Serializable
{
private LockEntry writer;
- private Hashtable readers;
- private long m_youngestReader = 0;
+ private Map readers;
+ private Object resourceId;
+ private long lastReader = 0;
+ private long eldestReader = Long.MAX_VALUE;
- ObjectLocks()
+ ObjectLocks(Object resourceId)
{
- this(null);
+ this.resourceId = resourceId;
}
- ObjectLocks(LockEntry writer)
+ private void newReaderMap()
{
- this.writer = writer;
- readers = new Hashtable();
+ this.readers = new HashMap();
+ }
+
+ boolean isEmpty()
+ {
+ return writer == null && (readers == null || readers.size() == 0);
+ }
+
+ boolean releaseLock(Object key)
+ {
+ if(writer != null && writer.isOwnedBy(key))
+ {
+ writer = null;
+ return true;
+ }
+ else if(readers != null)
+ {
+ return readers.remove(key) != null;
+ }
+ return false;
+ }
+
+ public Object getResourceId()
+ {
+ return resourceId;
+ }
+
+ boolean addWriter(LockEntry writer, boolean allowReaders)
+ {
+ boolean result = false;
+ if(this.writer != null)
+ {
+ result = this.writer.isOwnedBy(writer.getKey());
+ }
+ else // writer was not set
+ {
+ // current ObjectLock has no write lock, so check for readers
+ int readerSize = readers != null ? readers.size() : 0;
+ if(readerSize > 0)
+ {
+ // does current entity have already an read lock
+ if(getReader(writer.getKey()) != null)
+ {
+ if(readerSize == 1)
+ {
+ // only current entity has a read lock, so go on
+ setWriter(writer);
+ removeReader(this.writer.getKey());
+ result = true;
+ }
+ else
+ {
+ // current entity and others have already a read lock
+ // if aquire a write is allowed, go on
+ if(allowReaders)
+ {
+ setWriter(writer);
+ removeReader(this.writer.getKey());
+ result = true;
+ }
+ }
+ }
+ else
+ {
+ // current entity has no read lock, but others
+ // if aquire a write is allowed, go on
+ if(allowReaders)
+ {
+ setWriter(writer);
+ result = true;
+ }
+ }
+ }
+ else
+ {
+ setWriter(writer);
+ result = true;
+ }
+ }
+ return result;
}
LockEntry getWriter()
{
- return writer;
+ return this.writer;
}
- void setWriter(LockEntry writer)
+ boolean hasWriteLock(Object key)
+ {
+ return writer != null && writer.isOwnedBy(key);
+ }
+
+ private void setWriter(LockEntry writer)
{
this.writer = writer;
+ if(this.writer != null) this.resourceId = writer.getResourceId();
}
- Hashtable getReaders()
+ Map getReaders()
{
return readers;
}
- void addReader(LockEntry reader)
+ boolean hasReadLock(Object key)
{
- /**
- * MBAIRD:
- * we want to track the youngest reader so we can remove all readers at timeout
- * if the youngestreader is older than the timeoutperiod.
- */
- if((reader.getTimestamp() < m_youngestReader) || (m_youngestReader == 0))
+ if(writer != null)
+ {
+ return writer.isOwnedBy(key);
+ }
+ else if(readers != null)
{
- m_youngestReader = reader.getTimestamp();
+ LockEntry lock = (LockEntry) readers.get(key);
+ return lock != null && lock.isOwnedBy(key);
}
- this.readers.put(reader.getKey(), reader);
+ return false;
}
- long getYoungestReader()
+ boolean addReader(LockEntry reader, boolean allowMultipleReader, boolean allowReaderWhenWriteLock)
{
- return m_youngestReader;
+ if(writer == null)
+ {
+ return internalAddReader(reader, allowMultipleReader);
+ }
+ else if(writer.isOwnedBy(reader.getKey()))
+ {
+ return true;
+ }
+ else if(allowReaderWhenWriteLock)
+ {
+ return internalAddReader(reader, allowMultipleReader);
+ }
+ return false;
+ }
+
+ private boolean internalAddReader(LockEntry reader, boolean allowMultipleReader)
+ {
+ boolean result = true;
+ if(readers == null)
+ {
+ newReaderMap();
+ readers.put(reader.getKey(), reader);
+ }
+ else if(readers.size() == 0)
+ {
+ readers.put(reader.getKey(), reader);
+ }
+ else
+ {
+ if(this.readers.get(reader.getKey()) != null)
+ {
+ }
+ else if(allowMultipleReader)
+ {
+ this.readers.put(reader.getKey(), reader);
+ }
+ else
+ {
+ result = false;
+ }
+ }
+ if(result)
+ {
+ if(reader.timestamp > lastReader) lastReader = reader.timestamp;
+ if(reader.timestamp < eldestReader) eldestReader = reader.timestamp;
+ }
+ return result;
}
LockEntry getReader(Object key)
{
- return (LockEntry) this.readers.get(key);
+ return this.readers != null ? (LockEntry) this.readers.get(key) : null;
}
LockEntry removeReader(Object key)
{
- return (LockEntry) this.readers.remove(key);
+ return this.readers != null ? (LockEntry) this.readers.remove(key) : null;
}
}
-
//===============================================================
// inner class
//===============================================================
- /**
- * A lock entry encapsulates locking information.
- */
- final class LockEntry implements Serializable
+
+ /** A lock entry encapsulates locking information. */
+ private static final class LockEntry implements Serializable
{
- /**
- * marks a Read Lock.
- */
+ /** marks a Read Lock. */
static final int LOCK_READ = 0;
- /**
- * marks a Write Lock.
- */
+ /** marks a Write Lock. */
static final int LOCK_WRITE = 1;
- /**
- * the object to be locked.
- */
+ /** the object to be locked. */
private Object resourceId;
- /**
- * key for locked object
- */
+ /** key for locked object */
private Object key;
- /**
- * the timestamp marking the time of acquisition of this lock
- */
+ /** the timestamp marking the time of acquisition of this lock */
private long timestamp;
/**
- * the isolationlevel for this lock.
- */
- private IsolationLevels isolationLevel;
-
- /**
* marks if this is a read or a write lock.
* LOCK_READ = 0;
* LOCK_WRITE = 1;
*/
private int lockType;
- /**
- * Multiargument constructor for fast loading of LockEntries by OJB.
- */
+ /** Multiargument constructor for fast loading of LockEntries by OJB. */
public LockEntry(Object resourceId,
Object key,
long timestamp,
- IsolationLevels isolationLevel,
int lockType)
{
this.resourceId = resourceId;
this.key = key;
this.timestamp = timestamp;
- this.isolationLevel = isolationLevel;
this.lockType = lockType;
}
- /**
- * Returns the resource id of the locked object (or the locked object itself).
- */
+ /** Returns the resource id of the locked object (or the locked object itself). */
public Object getResourceId()
{
return resourceId;
}
- /**
- * Returns lock key.
- */
+ /** Returns lock key. */
public Object getKey()
{
return key;
}
- /**
- * returns the timestamp of the acqusition of the lock.
- */
+ /** returns the timestamp of the acqusition of the lock. */
public long getTimestamp()
{
return timestamp;
}
/**
- * returns the isolation level of this lock
- */
- public IsolationLevels getIsolationLevel()
- {
- return isolationLevel;
- }
-
- /**
* returns the locktype of this lock.
*
* @return LOCK_READ if lock is a readlock,
@@ -732,23 +680,10 @@
this.lockType = locktype;
}
- /**
- * Returns true if this lock is owned by the specified key.
- */
+ /** Returns true if this lock is owned by the specified key. */
public boolean isOwnedBy(Object key)
{
return this.getKey().equals(key);
- }
-
-
- /**
- * Sets the isolationLevel.
- *
- * @param isolationLevel The isolationLevel to set
- */
- public void setIsolationLevel(IsolationLevels isolationLevel)
- {
- this.isolationLevel = isolationLevel;
}
/**
Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerRemoteImpl.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerRemoteImpl.java?rev=422231&r1=422230&r2=422231&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerRemoteImpl.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerRemoteImpl.java Sat Jul 15 07:20:25 2006
@@ -16,9 +16,11 @@
*/
import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
@@ -27,16 +29,21 @@
import java.net.ProtocolException;
import java.net.URL;
+import org.apache.commons.lang.SystemUtils;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.ojb.broker.util.logging.Logger;
+import org.apache.ojb.broker.util.logging.LoggerFactory;
+
/**
* This implementation of the {@link LockManager} interface supports locking
* in distributed environments in combination with a specific lock servlet.
*
- * @see LockManagerServlet
* @version $Id$
+ * @see LockManagerServlet
*/
public class LockManagerRemoteImpl implements LockManager
{
- // private Logger log = LoggerFactory.getLogger(LockManagerRemoteImpl.class);
+ private Logger log = LoggerFactory.getLogger(LockManagerRemoteImpl.class);
public static final byte METHOD_READ_LOCK = 'a';
public static final byte METHOD_WRITE_LOCK = 's';
@@ -59,24 +66,6 @@
{
}
-// /**
-// * @see org.apache.ojb.broker.util.configuration.Configurable#configure(org.apache.ojb.broker.util.configuration.Configuration)
-// */
-// public void configure(Configuration pConfig) throws ConfigurationException
-// {
-// String url = pConfig.getString("LockServletUrl", "http://127.0.0.1:8080/ojb-lockserver");
-// log.info("Lock server servlet URL: " + url);
-// try
-// {
-// lockservlet = new URL(url);
-// }
-// catch(MalformedURLException e)
-// {
-// throw new ConfigurationException("Invalid LockServlet Url was specified: " + url, e);
-// }
-//
-// }
-
public String getLockServletUrl()
{
return lockServletUrl;
@@ -95,23 +84,14 @@
}
}
-
/**
* noop
+ *
* @param timeout
*/
public void setLockTimeout(long timeout)
{
-// LockInfo info = new LockInfo(timeout, METHOD_LOCK_TIMEOUT_SET);
-// try
-// {
-// byte[] requestBarr = serialize(info);
-// performRequestObject(requestBarr);
-// }
-// catch(Throwable t)
-// {
-// throw new LockRuntimeException("Can't set locking timeout", t);
-// }
+ log.info("Can't set lock-timeout, it's managed by the lock-servlet");
}
public long getLockTimeout()
@@ -124,6 +104,15 @@
}
catch(Throwable t)
{
+ Throwable tmp = ExceptionUtils.getRootCause(t);
+ if(tmp != null)
+ {
+ tmp.printStackTrace();
+ }
+ else
+ {
+ t.printStackTrace();
+ }
throw new LockRuntimeException("Can't get locking info", t);
}
}
@@ -142,21 +131,10 @@
}
}
- /**
- * noop
- */
+ /** noop */
public void setBlockTimeout(long timeout)
{
-// LockInfo info = new LockInfo(timeout, METHOD_BLOCK_TIMEOUT_SET);
-// try
-// {
-// byte[] requestBarr = serialize(info);
-// performRequestObject(requestBarr);
-// }
-// catch(Throwable t)
-// {
-// throw new LockRuntimeException("Can't set block timeout value", t);
-// }
+ log.info("Can't set lock-timeout, it's managed by the lock-servlet");
}
public String getLockInfo()
@@ -210,21 +188,6 @@
}
}
-// public boolean removeReader(Object key, Object resourceId)
-// {
-// LockInfo info = new LockInfo(key, resourceId, METHOD_RELEASE_SINGLE_LOCK);
-// try
-// {
-// byte[] requestBarr = serialize(info);
-// return performRequest(requestBarr);
-// }
-// catch(Throwable t)
-// {
-// throw new LockRuntimeException("Cannot remove read lock for '"
-// + resourceId + "' using key '" + key + "'", t);
-// }
-// }
-
public void releaseLocks(Object key)
{
LockInfo info = new LockInfo(key, null, METHOD_RELEASE_LOCKS);
@@ -325,25 +288,6 @@
}
}
- private HttpURLConnection getHttpUrlConnection()
- throws MalformedURLException, IOException, ProtocolException
- {
- URL lockserver = getLockserverUrl();
- HttpURLConnection conn = (HttpURLConnection) lockserver.openConnection();
-
- conn.setDoInput(true);
- conn.setDoOutput(true);
- conn.setRequestMethod("POST");
- conn.setAllowUserInteraction(false);
- conn.setUseCaches(false);
- return conn;
- }
-
- private URL getLockserverUrl()
- {
- return lockservlet;
- }
-
public byte[] serialize(Object obj) throws IOException
{
ByteArrayOutputStream bao = new ByteArrayOutputStream();
@@ -363,7 +307,7 @@
}
else
{
- throw new LockRuntimeException("Remote lock server error, expect return value of type 'Boolean'");
+ throw new LockRuntimeException("Remote lock server error, expect return value of type 'Boolean' but object was: " + result);
}
}
@@ -376,7 +320,7 @@
}
else
{
- throw new LockRuntimeException("Remote lock server error, expect return value of type 'String'");
+ throw new LockRuntimeException("Remote lock server error, expect return value of type 'String' but object was: " + result);
}
}
@@ -389,29 +333,66 @@
}
else
{
- throw new LockRuntimeException("Remote lock server error, expect return value of type 'String'");
+ throw new LockRuntimeException("Remote lock server error, expect return value of type 'Long' but object was: " + result);
}
}
private Object performRequestObject(byte[] requestBarr) throws IOException, ClassNotFoundException
{
HttpURLConnection conn = getHttpUrlConnection();
+ Object result = null;
+ try
+ {
+ conn.connect();
- //post request
- BufferedOutputStream out = new BufferedOutputStream(conn.getOutputStream());
- out.write(requestBarr, 0, requestBarr.length);
- out.flush();
-
- // read result from
- InputStream in = conn.getInputStream();
- ObjectInputStream ois = new ObjectInputStream(in);
- Object result = ois.readObject();
-
- // cleanup
- ois.close();
- out.close();
- conn.disconnect();
+ //post request
+ BufferedOutputStream out = new BufferedOutputStream(conn.getOutputStream());
+ out.write(requestBarr, 0, requestBarr.length);
+ out.flush();
+
+ // read result from
+ InputStream in = conn.getInputStream();
+ ObjectInputStream ois = new ObjectInputStream(in);
+ result = ois.readObject();
+ // cleanup
+ ois.close();
+ out.close();
+ }
+ catch(IOException e)
+ {
+ e.printStackTrace();
+ InputStream errStream = conn.getErrorStream();
+ if(errStream != null)
+ {
+ BufferedReader err = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
+ String errLine;
+ StringBuffer buf = new StringBuffer();
+ while((errLine = err.readLine()) != null)
+ {
+ buf.append(SystemUtils.LINE_SEPARATOR).append(errLine);
+ }
+ log.error("Remote Error:" + buf.toString());
+ }
+ throw e;
+ }
+ catch(RuntimeException e)
+ {
+ e.printStackTrace();
+ throw e;
+ }
+ finally
+ {
+ try
+ {
+ if(conn != null) conn.disconnect();
+ }
+ catch(Exception ignore)
+ {
+ // ignore
+ }
+ }
+ conn.disconnect();
if(result instanceof Throwable)
{
throw new LockRuntimeException("Remote lock server error", (Throwable) result);
@@ -422,14 +403,24 @@
}
}
+ private HttpURLConnection getHttpUrlConnection()
+ throws MalformedURLException, IOException, ProtocolException
+ {
+ HttpURLConnection connection = (HttpURLConnection) lockservlet.openConnection();
+ connection.setDoInput(true);
+ connection.setDoOutput(true);
+ connection.setRequestMethod("PUT");
+ connection.setAllowUserInteraction(false);
+ connection.setUseCaches(false);
+ return connection;
+ }
+
public static final class LockInfo implements Serializable
{
- public Object key;
- public Object resourceId;
+ public String key;
+ public String resourceId;
public int isolationLevel;
public byte methodName;
- public long lockTimeout;
- public long blockTimeout;
public LockInfo(byte methodName)
{
@@ -438,30 +429,17 @@
public LockInfo(Object key, Object resourceId, byte methodName)
{
- this.key = key;
- this.resourceId = resourceId;
+ this.key = key != null ? key.toString() : null;
+ this.resourceId = resourceId != null ? resourceId.toString() : null;
this.methodName = methodName;
}
public LockInfo(Object key, Object resourceId, int isolationLevel, byte methodName)
{
- this.key = key;
- this.resourceId = resourceId;
+ this.key = key != null ? key.toString() : null;
+ this.resourceId = resourceId != null ? resourceId.toString() : null;
this.isolationLevel = isolationLevel;
this.methodName = methodName;
}
-
-// public LockInfo(long timeout, byte methodName)
-// {
-// if(methodName == METHOD_LOCK_TIMEOUT_SET)
-// {
-// this.lockTimeout = timeout;
-// }
-// else
-// {
-// this.blockTimeout = timeout;
-// }
-// this.methodName = methodName;
-// }
}
}
Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerServlet.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerServlet.java?rev=422231&r1=422230&r2=422231&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerServlet.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/locking/LockManagerServlet.java Sat Jul 15 07:20:25 2006
@@ -26,8 +26,11 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
+import java.util.Date;
+import java.util.Enumeration;
import org.apache.commons.lang.math.NumberUtils;
+import org.apache.commons.lang.BooleanUtils;
import org.apache.ojb.broker.util.ClassHelper;
@@ -40,6 +43,7 @@
static final String STR_LOCK_TIMEOUT = "lockTimeout";
static final String STR_BLOCK_TIMEOUT = "blockTimeout";
static final String STR_LOCK_MANAGER = "lockManager";
+ private static final String prefix = "[ojb-lockserver] ";
private static long numRequests;
private static Throwable lastError = null;
@@ -47,20 +51,24 @@
public void init(ServletConfig servletConfig) throws ServletException
{
super.init(servletConfig);
+
// if lock manager was instantiated not yet
if(lockmanager == null)
{
lastError = null;
numRequests = 0;
+ System.out.println(prefix + "Start ojb-locking servlet");
String strLockManager = servletConfig.getInitParameter(STR_LOCK_MANAGER);
try
{
lockmanager = (LockManager) (strLockManager != null ?
ClassHelper.newInstance(strLockManager) : ClassHelper.newInstance(LockManagerInMemoryImpl.class));
+ System.out.println(prefix + "Used LockManager: " + lockmanager.getClass());
}
catch(Exception e)
{
- lastError = new LockRuntimeException("Can't instance lock manager, init parameter 'lockManager': " + strLockManager);
+ lastError = new LockRuntimeException(
+ "Can't instance lock manager, init parameter '" + STR_LOCK_MANAGER + "': " + strLockManager);
e.printStackTrace();
}
String strTimeout = servletConfig.getInitParameter(STR_LOCK_TIMEOUT);
@@ -70,12 +78,14 @@
{
Long lockTimeout = NumberUtils.createLong(strTimeout);
lockmanager.setLockTimeout(lockTimeout.longValue());
+ System.out.println(prefix + "Set lock-timeout=" + lockTimeout);
}
catch(Exception e)
{
if(lastError == null)
{
- lastError = new LockRuntimeException("Can't convert 'lockTimeout' init parameter: " + strTimeout);
+ lastError = new LockRuntimeException(
+ "Can't convert init parameter '" + STR_LOCK_TIMEOUT + "': " + strTimeout);
}
e.printStackTrace();
}
@@ -86,7 +96,8 @@
try
{
Long blockTimeout = NumberUtils.createLong(strBlock);
- lockmanager.setLockTimeout(blockTimeout.longValue());
+ lockmanager.setBlockTimeout(blockTimeout.longValue());
+ System.out.println(prefix + "Set block-timeout=" + blockTimeout);
}
catch(Exception e)
{
@@ -113,23 +124,35 @@
out.println("<html><head><title>OJB Distributed Locking Servlet Status Page</title>");
out.println("</head><body><h1>OJB Distributed Locking Servlet</h1>");
- out.println("The servlet is running.<p>");
if(lastError == null)
{
- out.println("The LockServer is running.<p>");
- out.println("LockManager info: " + lockmanager.getLockInfo() + "<p>");
- out.println("Processed Lock Request: " + numRequests + "<p>");
+ out.println("<strong>The LockManager is running </strong>(" + new Date().toString() +")<p/>");
+ out.println("<strong>LockManager info:</strong><pre>" + lockmanager.getLockInfo() + "</pre><p/>");
+ out.println("<strong>Processed Lock Request: </strong>" + numRequests + "<p/>");
}
else
{
out.println("<h2>The LockServer has a problem!</h2>");
- out.println("The error message is:<p>");
- out.println(lastError.getMessage() + "<p>");
+ out.println("The error message is:<p/>");
+ out.println(lastError.getMessage() + "<p/>");
lastError.printStackTrace(out);
lastError = null;
}
+ out.println("<h3>Servlet Information</h3>");
+ out.println("<table border=\"0\" width=\"100%\">");
+ Enumeration names = request.getHeaderNames();
+ while(names.hasMoreElements())
+ {
+ String name = (String) names.nextElement();
+ out.println("<tr>");
+ out.println(" <th align=\"right\">" + name + ":</th>");
+ out.println(" <td>" + request.getHeader(name) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
+
out.println("</body></html>");
}
@@ -139,6 +162,15 @@
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
+ doPut(request, response);
+ }
+
+ protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ //response.setContentType("image/x-pixmap");
+ response.setContentType("application/xml");
+ //response.setContentType("text/html");
+ response.setHeader("pragma", "no-cache");
// update counter
numRequests++;
@@ -154,12 +186,12 @@
{
case LockManagerRemoteImpl.METHOD_READ_LOCK:
{
- result = lockmanager.readLock(info.key, info.resourceId, IsolationLevels.getEnum(info.isolationLevel)) ? Boolean.TRUE : Boolean.FALSE;
+ result = BooleanUtils.toBooleanObject(lockmanager.readLock(info.key, info.resourceId, IsolationLevels.getEnum(info.isolationLevel)));
break;
}
case LockManagerRemoteImpl.METHOD_RELEASE_SINGLE_LOCK:
{
- result = lockmanager.releaseLock(info.key, info.resourceId) ? Boolean.TRUE : Boolean.FALSE;
+ result = BooleanUtils.toBooleanObject(lockmanager.releaseLock(info.key, info.resourceId));
break;
}
case LockManagerRemoteImpl.METHOD_RELEASE_LOCKS:
@@ -170,27 +202,29 @@
}
case LockManagerRemoteImpl.METHOD_WRITE_LOCK:
{
- result = lockmanager.writeLock(info.key, info.resourceId, IsolationLevels.getEnum(info.isolationLevel)) ? Boolean.TRUE : Boolean.FALSE;
+ result = BooleanUtils.toBooleanObject(lockmanager.writeLock(info.key, info.resourceId,
+ IsolationLevels.getEnum(info.isolationLevel)));
break;
}
case LockManagerRemoteImpl.METHOD_UPGRADE_LOCK:
{
- result = lockmanager.upgradeLock(info.key, info.resourceId, IsolationLevels.getEnum(info.isolationLevel)) ? Boolean.TRUE : Boolean.FALSE;
+ result = BooleanUtils.toBooleanObject(lockmanager.upgradeLock(info.key,
+ info.resourceId, IsolationLevels.getEnum(info.isolationLevel)));
break;
}
case LockManagerRemoteImpl.METHOD_CHECK_READ:
{
- result = lockmanager.hasRead(info.key, info.resourceId) ? Boolean.TRUE : Boolean.FALSE;
+ result = BooleanUtils.toBooleanObject(lockmanager.hasRead(info.key, info.resourceId));
break;
}
case LockManagerRemoteImpl.METHOD_CHECK_WRITE:
{
- result = lockmanager.hasWrite(info.key, info.resourceId) ? Boolean.TRUE : Boolean.FALSE;
+ result = BooleanUtils.toBooleanObject(lockmanager.hasWrite(info.key, info.resourceId));
break;
}
case LockManagerRemoteImpl.METHOD_CHECK_UPGRADE:
{
- result = lockmanager.hasUpgrade(info.key, info.resourceId) ? Boolean.TRUE : Boolean.FALSE;
+ result = BooleanUtils.toBooleanObject(lockmanager.hasUpgrade(info.key, info.resourceId));
break;
}
case LockManagerRemoteImpl.METHOD_LOCK_INFO:
@@ -208,17 +242,6 @@
result = new Long(lockmanager.getBlockTimeout());
break;
}
-// case LockManagerRemoteImpl.METHOD_LOCK_TIMEOUT_SET:
-// {
-// lockmanager.setLockTimeout(info.lockTimeout);
-// break;
-// }
-//
-// case LockManagerRemoteImpl.METHOD_BLOCK_TIMEOUT_SET:
-// {
-// lockmanager.setBlockTimeout(info.blockTimeout);
-// break;
-// }
default :
{
throw new LockRuntimeException("Unknown command:" + info.methodName);
@@ -227,6 +250,7 @@
}
catch(RuntimeException e)
{
+ e.printStackTrace();
result = new LockRuntimeException("Error while invoke specified method in servlet.", e);
}
@@ -242,13 +266,12 @@
}
}
- private Object buildObjectFromRequest(HttpServletRequest request) throws IOException, ClassNotFoundException
+ private Object buildObjectFromRequest(final HttpServletRequest request) throws IOException, ClassNotFoundException
{
- Object obj;
// get the body of the request as binary data
InputStream is = request.getInputStream();
ObjectInputStream objInputStream = new ObjectInputStream(is);
- obj = objInputStream.readObject();
+ Object obj = objInputStream.readObject();
objInputStream.close();
is.close();
return obj;
Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java?rev=422231&r1=422230&r2=422231&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/ClassDescriptor.java Sat Jul 15 07:20:25 2006
@@ -119,6 +119,10 @@
private boolean batchable = true;
private int useIdentityColumn = 0;
+ /**
+ * Indicate whether or not this class has LOB fields.
+ */
+ private FieldDescriptor[] lobFields;
private String baseClass = null;
/**
@@ -370,6 +374,7 @@
m_lockingFieldDescriptors = null;
m_RwFieldDescriptors = null;
m_RwNonPkFieldDescriptors = null;
+ lobFields = null;
}
public boolean removeFieldDescriptor(FieldDescriptor fld)
@@ -387,6 +392,7 @@
m_lockingFieldDescriptors = null;
m_RwFieldDescriptors = null;
m_RwNonPkFieldDescriptors = null;
+ lobFields = null;
return result;
}
@@ -1282,7 +1288,6 @@
return getTableName() != null;
}
-
/**
* @return boolean true if the mapped class is abstract
*/
@@ -1879,6 +1884,46 @@
{
return (SuperReferenceDescriptor) getObjectReferenceDescriptorByName(SuperReferenceDescriptor.SUPER_FIELD_INTERNAL_NAME);
}
+
+ /**
+ * Returns <em>true</em> if this class has one or more LOB fields (Blob, Clob).
+ *
+ * @return <em>True</em> if this class (with inherited) has LOB fields.
+ */
+ public boolean hasLobField()
+ {
+ return getLobFields().length > 0;
+ }
+
+ /**
+ * Return the LOB fields (including inherited) of this class.
+ *
+ * @return The LOB fields of this class or empty array.
+ */
+ public FieldDescriptor[] getLobFields()
+ {
+ if(lobFields == null)
+ {
+ FieldDescriptor[] result = new FieldDescriptor[]{};
+ ArrayList tmp = new ArrayList();
+ FieldDescriptor[] fields = getFieldDescriptor(true);
+ for(int i = 0; i < fields.length; i++)
+ {
+ FieldDescriptor field = fields[i];
+ if(field.isLobFieldType())
+ {
+ tmp.add(field);
+ }
+ }
+ if(tmp.size() > 0)
+ {
+ result = (FieldDescriptor[]) tmp.toArray(new FieldDescriptor[tmp.size()]);
+ }
+ lobFields = result;
+ }
+ return lobFields;
+ }
+
/**
* Return a string representation of this class.
Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/CollectionDescriptor.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/CollectionDescriptor.java?rev=422231&r1=422230&r2=422231&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/CollectionDescriptor.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/CollectionDescriptor.java Sat Jul 15 07:20:25 2006
@@ -64,6 +64,17 @@
super(descriptor);
}
+ /**
+ * Override method from base class to declare foreign key
+ * field as inverse.
+ * @param fkField
+ * @param targetField
+ */
+ public void addForeignKeyField(Object fkField, String targetField)
+ {
+ addForeignKeyField(fkField, targetField, true);
+ }
+
public String[] getFksToThisClass()
{
if (fksToThisClassAry == null)
Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/AnonymousPersistentField.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/AnonymousPersistentField.java?rev=422231&r1=422230&r2=422231&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/AnonymousPersistentField.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/AnonymousPersistentField.java Sat Jul 15 07:20:25 2006
@@ -17,15 +17,13 @@
import java.util.Map;
+import org.apache.commons.collections.map.ReferenceIdentityMap;
import org.apache.ojb.broker.metadata.MetadataException;
-import org.apache.ojb.broker.util.ReferenceMap;
-
-//import org.apache.commons.collections.ReferenceMap;
/**
- * This class handle an anonymous persistent fiels for 1-1 association,
- * and ojbConcreteClass
- * @author Houar TINE
+ * This class handle an anonymous persistent fiels for 1-1 association
+ * and ojbConcreteClass.
+
* @version $Id$
*/
public class AnonymousPersistentField implements PersistentField
@@ -51,30 +49,31 @@
}
/*
- Use WeakIdentityHashMap instead WeakHashMap to hold anonymous field values
- Here is an snip of the mail from Andy Malakov:
-
-I found that usage of database identity in Java produces quite interesting problem in OJB:
-In my application all persistent Java objects use database identity instead of Java reference identity
-(i.e. Persistable.equals() is redefined so that two persistent objects are the same if they have the same
-primary key and top-level class).
-
-In OJB, for each field declared in repository there is dedicated instance of AnonymousPersistentField that stores
-object-to-field-value mapping in WeakHashMap (in fkCache attribute). Despite usage of cache
-it is possible that identical DB objects will end up as different Java objects during retrieval
-of complex objects.
-
-Now imagine what happens when two identical instances are retrieved:
-1)
-When first instance is retrieved it stores its foreign keys in AnonymousPersistentField.fkCache under instance's
-identity. (happens in RowReaderDefaultImpl.buildWithReflection())
-2)
-When second object is retrieved and stored in fkCache, first instance is probably still cached
-[WeakHashMap entries are cleaned up only during GC]. Since keys are identical WeakHashMap only updates entry
-value and DOES NOT update entry key.
-3)
-If Full GC happens after that moment it will dispose fcCache entry if the FIRST reference becomes
-soft-referenced only.
+ Use ReferenceIdentityMap (with weak key and hard value setting) instead of
+ WeakHashMap to hold anonymous field values. Here is an snip of the mail from Andy Malakov:
+ <snip>
+ I found that usage of database identity in Java produces quite interesting problem in OJB:
+ In my application all persistent Java objects use database identity instead of Java reference identity
+ (i.e. Persistable.equals() is redefined so that two persistent objects are the same if they have the same
+ primary key and top-level class).
+
+ In OJB, for each field declared in repository there is dedicated instance of AnonymousPersistentField that stores
+ object-to-field-value mapping in WeakHashMap (in fkCache attribute). Despite usage of cache
+ it is possible that identical DB objects will end up as different Java objects during retrieval
+ of complex objects.
+
+ Now imagine what happens when two identical instances are retrieved:
+ 1)
+ When first instance is retrieved it stores its foreign keys in AnonymousPersistentField.fkCache under instance's
+ identity. (happens in RowReaderDefaultImpl.buildWithReflection())
+ 2)
+ When second object is retrieved and stored in fkCache, first instance is probably still cached
+ [WeakHashMap entries are cleaned up only during GC]. Since keys are identical WeakHashMap only updates entry
+ value and DOES NOT update entry key.
+ 3)
+ If Full GC happens after that moment it will dispose fcCache entry if the FIRST reference becomes
+ soft-referenced only.
+ </snip>
*/
protected void putToFieldCache(Object key, Object value)
{
@@ -82,7 +81,7 @@
{
if (fkCache == null)
{
- fkCache = new ReferenceMap (ReferenceMap.WEAK, ReferenceMap.HARD, 10, 0.75f, true);
+ fkCache = new ReferenceIdentityMap (ReferenceIdentityMap.WEAK, ReferenceIdentityMap.HARD, true);
}
if (value != null)
fkCache.put(key, value);
Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentField.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentField.java?rev=422231&r1=422230&r2=422231&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentField.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentField.java Sat Jul 15 07:20:25 2006
@@ -20,31 +20,39 @@
import org.apache.ojb.broker.metadata.MetadataException;
/**
- * Represents a persistent field, eg. a field that (depending on the access setting of
- * the corresponding field descriptor) shall be read and set by OJB.
+ * Provide methods to manage a field of a persistence capable object.
*
- * @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
* @version $Id$
*/
public interface PersistentField extends Serializable
{
/**
- * Returns the class where the field is declared.
+ * The type of the field declared in the persistence capable object.
*
- * @return The declaring class
+ * @return The declared field type.
+ * @see #getType()
*/
public Class getDeclaringClass();
/**
* Returns the name of the field.
*
- * @return The name
+ * @return The field name
*/
public String getName();
/**
- * Returns the type of the field.
- * @return The type
+ * Returns the mapped type of the field.
+ * <p/>
+ * In most cases
+ * the <em>mapped type</em> and the {@link #getDeclaringClass() declared}
+ * type are the same, except when using <em>nested fields</em> (see OJB docs).
+ * Then the declared type is the nested class (a nested class encapsulates
+ * 'normal' fields, e.g. nested class <code>Unit</code> has fields
+ * <code>String unitName</code> and <code>int value</code>) itself and mapped type is
+ * one of the mapped fields.
+ *
+ * @see #getDeclaringClass()
*/
public Class getType();
Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldAutoProxyImpl.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldAutoProxyImpl.java?rev=422231&r1=422230&r2=422231&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldAutoProxyImpl.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldAutoProxyImpl.java Sat Jul 15 07:20:25 2006
@@ -19,14 +19,17 @@
import org.apache.ojb.broker.metadata.MetadataException;
import org.apache.ojb.broker.util.ClassHelper;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.SystemUtils;
/**
- * PeristentField implementation that attempts to detect the nature of
+ * A {@link PersistentField} implementation that attempts to detect the nature of
* the field it is persisting.
* <p>
* First checks to see if it is a Field, then Property, then DynaBean. Dependend
* on the made checks this implementation internal use one of the following implementations:
* <p>
+ * - {@link PersistentCGLibImpl} <br/>
* - {@link PersistentFieldDirectImpl} <br/>
* - {@link PersistentFieldIntrospectorImpl} <br/>
* - {@link PersistentFieldPrivilegedImpl} <br/>
@@ -45,7 +48,8 @@
* setection order.
*/
protected Class[] persistentFieldClasses = new Class[]{
- PersistentFieldDirectImpl.class
+ PersistentFieldCGLibImpl.class
+ , PersistentFieldDirectImpl.class
, PersistentFieldIntrospectorImpl.class
, PersistentFieldPrivilegedImpl.class
, PersistentFieldDynaBeanImpl.class};
@@ -67,8 +71,11 @@
{
index = 0;
currentPF = null;
- throw new AutoDetectException("Can't autodetect valid PersistentField implementation: "
- + latestException.message, latestException.exception);
+ String eol = SystemUtils.LINE_SEPARATOR;
+ throw new AutoDetectException(eol + "Can't autodetect valid PersistentField implementation using "
+ + eol + ArrayUtils.toString(persistentFieldClasses)
+ + eol + "Last exception message was: "
+ + eol + latestException.message, latestException.exception);
}
try
{
@@ -104,8 +111,8 @@
}
else
{
- handleException("Can't extract field value for field " + getName()
- + " from object " + (anObject != null ? anObject.getClass() : null), e);
+ handleException("Can't extract field value for field '" + getName()
+ + "' from object " + (anObject != null ? anObject.getClass() : null) + " using " + getCurrent().getClass(), e);
return get(anObject);
}
}
@@ -125,8 +132,8 @@
}
else
{
- handleException("Can't set value for field " + getName()
- + " to object " + (obj != null ? obj.getClass() : null), e);
+ handleException("Can't set value for field '" + getName()
+ + "' to object " + (obj != null ? obj.getClass() : null) + " using " + getCurrent().getClass(), e);
set(obj, value);
}
}
@@ -146,7 +153,7 @@
}
else
{
- handleException("Can't identify field type for field " + getName(), null);
+ handleException("Can't identify field type for field '" + getName() + "'", null);
return getType();
}
}
@@ -174,10 +181,9 @@
return (PersistentField) ClassHelper.newInstance(pfClass, types, args);
}
- private static class ExceptionWrapper implements Serializable
+ static class ExceptionWrapper implements Serializable
{
- private static final long serialVersionUID = 3979512977080088567L;
-
+ private static final long serialVersionUID = 3691042088451912249L;
Exception exception;
String message;
@@ -188,9 +194,9 @@
}
}
- private static class AutoDetectException extends MetadataException
+ static class AutoDetectException extends MetadataException
{
- private static final long serialVersionUID = -48339524725993565L;
+ private static final long serialVersionUID = 3257290223049585970L;
public AutoDetectException()
{
Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldBase.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldBase.java?rev=422231&r1=422230&r2=422231&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldBase.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldBase.java Sat Jul 15 07:20:25 2006
@@ -15,6 +15,10 @@
* limitations under the License.
*/
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@@ -22,13 +26,13 @@
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
-import org.apache.ojb.broker.core.proxy.ProxyFactory;
import org.apache.ojb.broker.metadata.MetadataException;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
/**
- * Abstract {@link PersistentField} base implementation class.
+ * Abstract base implementation class for {@link PersistentField} with
+ * some additional (protected) helper methods for real implementation classes.
*
* @version $Id$
*/
@@ -120,9 +124,51 @@
}
}
+ /**
+ * Helper method for bean based (setter/getter based) implementations: Get the
+ * {@link java.beans.PropertyDescriptor} of specified class and property.
+ *
+ * @param aClass The target class.
+ * @param aPropertyName The name of the property.
+ * @return The
+ */
+ protected PropertyDescriptor findPropertyDescriptor(Class aClass, String aPropertyName)
+ {
+ BeanInfo info;
+ PropertyDescriptor[] pd;
+ PropertyDescriptor descriptor = null;
+
+ try
+ {
+ // use stop class to find inherited properties
+ info = Introspector.getBeanInfo(aClass);
+ pd = info.getPropertyDescriptors();
+ for (int i = 0; i < pd.length; i++)
+ {
+ if (pd[i].getName().equals(aPropertyName))
+ {
+ descriptor = pd[i];
+ break;
+ }
+ }
+ if (descriptor == null)
+ {
+ throw new MetadataException("Can't find property '" + aPropertyName + "' in " + aClass);
+ }
+ return descriptor;
+ }
+ catch (IntrospectionException ex)
+ {
+ throw new MetadataException("Can't find property '" + aPropertyName + "' in " + aClass, ex);
+ }
+ }
+
+ /**
+ * Lookup the logging instance.
+ */
protected Logger getLog()
{
- return LoggerFactory.getLogger("PersistentField");
+ return LoggerFactory.getLogger(this.getClass());
}
public String toString()
@@ -134,7 +180,7 @@
}
/**
- * Build a String representation of given arguments.
+ * Helper method: Build set-error string for field access based implementations.
*/
protected String buildErrorSetMsg(Object obj, Object value, Field aField)
{
@@ -142,18 +188,18 @@
StringBuffer buf = new StringBuffer();
buf
.append(eol + "[try to set 'object value' in 'target object'")
- .append(eol + "target obj class: " + (obj != null ? obj.getClass().getName() : null))
- .append(eol + "target field name: " + (aField != null ? aField.getName() : null))
- .append(eol + "target field type: " + (aField != null ? aField.getType() : null))
- .append(eol + "target field declared in: " + (aField != null ? aField.getDeclaringClass().getName() : null))
- .append(eol + "object value class: " + (value != null ? value.getClass().getName() : null))
- .append(eol + "object value: " + (value != null ? value : null))
+ .append(eol + " target obj class: " + (obj != null ? obj.getClass().getName() : null))
+ .append(eol + " target field name: " + (aField != null ? aField.getName() : null))
+ .append(eol + " target field type: " + (aField != null ? aField.getType() : null))
+ .append(eol + " target field declared in: " + (aField != null ? aField.getDeclaringClass().getName() : null))
+ .append(eol + " object value class: " + (value != null ? value.getClass().getName() : null))
+ .append(eol + " object value: " + (value != null ? value : null))
.append(eol + "]");
return buf.toString();
}
/**
- * Build a String representation of given arguments.
+ * Helper method: Build get-error string for field access based implementations.
*/
protected String buildErrorGetMsg(Object obj, Field aField)
{
@@ -161,11 +207,68 @@
StringBuffer buf = new StringBuffer();
buf
.append(eol + "[try to read from source object")
- .append(eol + "source obj class: " + (obj != null ? obj.getClass().getName() : null))
- .append(eol + "target field name: " + (aField != null ? aField.getName() : null))
- .append(eol + "target field type: " + (aField != null ? aField.getType() : null))
- .append(eol + "target field declared in: " + (aField != null ? aField.getDeclaringClass().getName() : null))
+ .append(eol + " source obj class: " + (obj != null ? obj.getClass().getName() : null))
+ .append(eol + " target field name: " + (aField != null ? aField.getName() : null))
+ .append(eol + " target field type: " + (aField != null ? aField.getType() : null))
+ .append(eol + " target field declared in: " + (aField != null ? aField.getDeclaringClass().getName() : null))
.append(eol + "]");
+ return buf.toString();
+ }
+
+ /**
+ * Helper method: Build getting-error string for setter/getter based implementations based on given arguments.
+ */
+ protected String buildGetterErrorMsg(Class returnType, Object source, String msg)
+ {
+ return buildPropertyErrorMsg(returnType, source, null, msg, false);
+}
+
+ /**
+ * Helper method: Build setting-error string for setter/getter based implementations based on given arguments.
+ */
+ protected String buildSetterErrorMsg(Class setterArgType, Object target, Object aValue, String msg)
+ {
+ return buildPropertyErrorMsg(setterArgType, target, aValue, msg, true);
+ }
+
+ /**
+ * Build error string for setter/getter based implementations based on given arguments.
+ */
+ private String buildPropertyErrorMsg(Class returnOrArgumentType, Object anObject, Object aValue, String msg, boolean isSetter)
+ {
+ String eol = SystemUtils.LINE_SEPARATOR;
+ StringBuffer buf = new StringBuffer();
+ String type = (isSetter ? "setter" : "getter");
+ buf
+ .append(eol + "["
+ + (msg == null ? "try to handle " + type + " property call" : type + " property call: " + msg))
+ .append(eol + " Declaring class [" + getDeclaringClass().getName() + "]")
+ .append(eol + " Property Name [" + getName() + "]")
+ .append(eol + " Property Type ["
+ + (returnOrArgumentType != null ? returnOrArgumentType.getName() : "not available") + "]");
+
+ if (anObject != null)
+ {
+ //buf.append("the " + (isSetter ? "target" : "source") + " object was [" + anObject + "]");
+ buf.append(eol + " the "
+ + (isSetter ? "target" : "source") + " object type was [" + anObject.getClass().getName() + "]");
+ }
+ else
+ {
+ buf.append(eol + " the " + (isSetter ? "target" : "source") + " object was 'null'");
+ }
+ if(isSetter)
+ {
+ if (aValue != null)
+ {
+ buf.append(eol + " the value was [" + aValue + "]");
+ buf.append(eol + " the value type was [" + aValue.getClass().getName() + "]");
+ }
+ else
+ {
+ buf.append(eol + " the value was 'null'");
+ }
+ }
return buf.toString();
}
}
Modified: db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldDirectImpl.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldDirectImpl.java?rev=422231&r1=422230&r2=422231&view=diff
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldDirectImpl.java (original)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldDirectImpl.java Sat Jul 15 07:20:25 2006
@@ -22,7 +22,7 @@
import org.apache.ojb.broker.util.ClassHelper;
/**
- * This {@link PersistentField} implementation
+ * This {@link org.apache.ojb.broker.metadata.fieldaccess.PersistentField} implementation
* is the high-speed version of the access strategies.
* <br/>
* It does not cooperate with an AccessController (like {@link PersistentFieldPrivilegedImpl}),
---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org