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