You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2006/04/26 07:42:37 UTC

svn commit: r397093 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/aspect/org/apache/tapestry/internal/aspects/ main/java/org/apache/tapestry/internal/transform/ main/java/org/apache/tapestry/util/ test/java/org/apache/tapestry/internal/aspects...

Author: hlship
Date: Tue Apr 25 22:42:35 2006
New Revision: 397093

URL: http://svn.apache.org/viewcvs?rev=397093&view=rev
Log:
Complete the Synchronization aspect
Begin work on supporting class hierarchies during component class transformation

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ConstructorArg.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchTargetWrapper.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/aspect/org/apache/tapestry/internal/aspects/Synchronization.aj
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformation.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformationImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/CollectionFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchronizationAspectTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchronizationTarget.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/util/CollectionFactoryTest.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/aspect/org/apache/tapestry/internal/aspects/Synchronization.aj
URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/aspect/org/apache/tapestry/internal/aspects/Synchronization.aj?rev=397093&r1=397092&r2=397093&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/aspect/org/apache/tapestry/internal/aspects/Synchronization.aj (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/aspect/org/apache/tapestry/internal/aspects/Synchronization.aj Tue Apr 25 22:42:35 2006
@@ -4,6 +4,7 @@
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import org.apache.tapestry.internal.annotations.Synchronized;
+import org.aspectj.lang.JoinPoint;
 
 /**
  * Manages multi-threaded access to methods of an object instance using a
@@ -11,14 +12,16 @@
  * {@link Synchronized.Read} and {@link Synchronized.Write} annotations. Methods that have the
  * Synchronized.Read annotation witll be advised to obtain and release the read lock around their
  * execution. Methods with the Synchronized.Write annotation will obtain and release the write lock
- * around their execution. Methods with Synchronized.Read that call a Synchronized.Write-ed method
- * (within the same instance) will release the read lock before invoking the Synchronized.Write-ed
+ * around their execution. Methods annotated with Synchronized.Read that call a method annotated
+ * with Synchronized.Write (within the same instance) will release the read lock before invoking the
  * method.
  * <p>
+ * This implementation makes use of a ThreadLocal to determine if the current thread has acquired
+ * the read lock (or not). This prevents the read lock from being re-locked, and allows the aspect
+ * to release the read lock temporarily when acquiring the write lock.
+ * <p>
  * This aspect also enforces that the annotations are only applied to instance (not static) methods,
  * and that a method may be either read or write, but not both.
- * <p>
- * TODO: read-into-write not yet implemented!
  * 
  * @author Howard M. Lewis Ship
  * @see org.apache.tapestry.internal.annotations.Synchronized
@@ -28,6 +31,21 @@
 {
     private final ReadWriteLock _lock = new ReentrantReadWriteLock();
 
+    private final ThreadLocal<Boolean> _threadHasReadLock = new ThreadLocal<Boolean>()
+    {
+        @Override
+        protected Boolean initialValue()
+        {
+            return false;
+        }
+    };
+
+    private void log(JoinPoint jp, String message)
+    {
+        if (false)
+            System.out.println(String.format("%s\n    %s:\n    %s\n", jp, _lock, message));
+    }
+
     pointcut annotatedClasses() :
          targetClasses() && within(@Synchronized *);
 
@@ -47,39 +65,76 @@
         execution(@Synchronized.Read @Synchronized.Write * *(..)) :
             "A method may be annotated with Synchronized.Read or with Synchronized.Write but not both.";
 
-    pointcut readMethods() :
-         execution(@Synchronized.Read * *(..)) && annotatedClasses();
-
     pointcut writeMethods() :
-        execution(@Synchronized.Write * *(..)) && annotatedClasses();
+            execution(@Synchronized.Write * *(..));
+
+    pointcut readMethods() :
+        execution(@Synchronized.Read * *(..));
 
-    /** Before read lock methods, acquire the read lock. */
-    before() : readMethods()
+    Object around() : readMethods()
     {
-        _lock.readLock().lock();
+        boolean locked = _threadHasReadLock.get();
+
+        if (!locked)
+        {
+            log(thisJoinPoint, "acquiring read lock");
+
+            _lock.readLock().lock();
+
+            _threadHasReadLock.set(true);
+        }
+
+        try
+        {
+            return proceed();
+        }
+        finally
+        {
+            if (!locked)
+            {
+                log(thisJoinPoint, "releasing read lock");
+
+                _lock.readLock().unlock();
+
+                _threadHasReadLock.set(false);
+            }
+        }
     }
 
-    /** After read lock methods (including thrown exceptions), release the read lock. */
-    after() : readMethods()
+    Object around() : writeMethods()
     {
-        _lock.readLock().unlock();
-    }
+        boolean locked = _threadHasReadLock.get();
 
-    /**
-     * Before write lock methods, acquire the write lock. Note that obtaining the write lock will
-     * block indefinately if the current thread has a read lock, but we handle that as a special
-     * case.
-     */
+        if (locked)
+        {
+            log(thisJoinPoint, "releasing read lock (for upgrade)");
+
+            _lock.readLock().unlock();
+
+            _threadHasReadLock.set(false);
+        }
+
+        log(thisJoinPoint, "acquiring write lock");
 
-    before(): writeMethods()
-    {
         _lock.writeLock().lock();
-    }
 
-    /** And release the write lock after the method completes (successfully, or with an exception). */
-    after() : writeMethods()
-    {
-        _lock.writeLock().unlock();
+        try
+        {
+            return proceed();
+        }
+        finally
+        {
+            log(thisJoinPoint, "releasing write lock");
+            _lock.writeLock().unlock();
+
+            if (locked)
+            {
+                log(thisJoinPoint, "acquiring read lock (for downgrade)");
+
+                _lock.readLock().lock();
+
+                _threadHasReadLock.set(true);
+            }
+        }
     }
-
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ConstructorArg.java
URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ConstructorArg.java?rev=397093&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ConstructorArg.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/ConstructorArg.java Tue Apr 25 22:42:35 2006
@@ -0,0 +1,45 @@
+package org.apache.tapestry.internal.transform;
+
+import javassist.CtClass;
+
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+
+import static org.apache.tapestry.util.Defense.notNull;
+
+/**
+ * Stores transformation type data about one argument to a class constructor.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+public class ConstructorArg
+{
+    final CtClass _type;
+
+    final Object _value;
+
+    /**
+     * Constructs new instance.
+     * 
+     * @param type
+     *            type of the parameter to be created (may not be null)
+     * @param value
+     *            value to be injected via the constructor (may be null)
+     */
+    @SuppressNullCheck
+    ConstructorArg(CtClass type, Object value)
+    {
+        _type = notNull(type, "type");
+        _value = value;
+    }
+
+    public CtClass getType()
+    {
+        return _type;
+    }
+
+    /** The value to be injected (may be null). */
+    public Object getValue()
+    {
+        return _value;
+    }
+}
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformation.java
URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformation.java?rev=397093&r1=397092&r2=397093&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformation.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformation.java Tue Apr 25 22:42:35 2006
@@ -14,8 +14,11 @@
 
 package org.apache.tapestry.internal.transform;
 
+import java.util.List;
+
 import org.apache.tapestry.transform.ClassTransformWorker;
 import org.apache.tapestry.transform.ClassTransformation;
+import org.apache.tapestry.util.IdAllocator;
 
 /**
  * Extends {@link org.apache.tapestry.transform.ClassTransformation} with additional methods that
@@ -46,4 +49,15 @@
      * @return the component's instantiator
      */
     Instantiator createInstantiator(Class componentClass);
+
+    /**
+     * Returns a copy of the transformation's IdAllocator. Used when creating a child class
+     * transformation. May only be invoked on a frozen transformation.
+     */
+    IdAllocator getIdAllocator();
+
+    /**
+     * Returns a copy of the list of constructor arguments for this class.
+     */
+    List<ConstructorArg> getConstructorArgs();
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformationImpl.java
URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformationImpl.java?rev=397093&r1=397092&r2=397093&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformationImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/transform/InternalClassTransformationImpl.java Tue Apr 25 22:42:35 2006
@@ -31,14 +31,13 @@
 import javassist.CtNewConstructor;
 import javassist.CtNewMethod;
 import javassist.NotFoundException;
-import javassist.expr.ExprEditor;
-import javassist.expr.FieldAccess;
 
 import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.events.ComponentLifecycle;
 import org.apache.tapestry.internal.InternalComponentResources;
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.transform.MethodSignature;
+import org.apache.tapestry.util.CollectionFactory;
 import org.apache.tapestry.util.IdAllocator;
 
 import static java.lang.String.format;
@@ -62,11 +61,11 @@
 
     private final ClassPool _classPool;
 
-    private Set<CtMethod> _addedMethods = newSet();
+    // private Set<CtMethod> _addedMethods = newSet();
 
-    private final IdAllocator _idAllocator = new IdAllocator();
+    private final IdAllocator _idAllocator;
 
-    private final Map<CtField, FieldEncapsulation> _encapsulations = newMap();
+    // private final Map<CtField, FieldEncapsulation> _encapsulations = newMap();
 
     /** Map from a field to the annotation objects for that field. */
     private Map<String, Object[]> _fieldAnnotations = newMap();
@@ -80,15 +79,10 @@
 
     private Object[] _classAnnotations;
 
-    /** Contains the assembled Javassist code for the class' constructor. */
+    /** Contains the assembled Javassist code for the class' default constructor. */
     private StringBuffer _constructor = new StringBuffer();
 
-    // Argument #0 is always "this". We always add argument #1 (i.e., $1) to be the
-    // InternalComponentResources. From there it all about injections.
-
-    private int _constructorArgIndex = 0;
-
-    private final List<ConstructorArg> _constructorArgs = newList();
+    private final List<ConstructorArg> _constructorArgs;
 
     // All field/method names will have this value as a prefix
 
@@ -96,37 +90,20 @@
 
     private final String _resourcesFieldName;
 
-    private static class ConstructorArg
-    {
-        private final CtClass _type;
-
-        private final Object _value;
-
-        @SuppressNullCheck
-        ConstructorArg(CtClass type, Object value)
-        {
-            _type = type;
-            _value = value;
-        }
-
-        CtClass getType()
-        {
-            return _type;
-        }
-
-        Object getValue()
-        {
-            return _value;
-        }
-    }
-
+    /**
+     * This is a constructor for the root class, the class that directly contains the ComponentClass
+     * annotation.
+     */
     public InternalClassTransformationImpl(CtClass ctClass)
     {
         _ctClass = ctClass;
         _classPool = _ctClass.getClassPool();
 
+        _idAllocator = new IdAllocator();
+
         preloadMemberNames();
 
+        _constructorArgs = newList();
         _constructor.append("{\n");
 
         addImplementedInterface(ComponentLifecycle.class);
@@ -141,6 +118,40 @@
         extendMethod(sig, "return " + _resourcesFieldName + ";");
     }
 
+    public InternalClassTransformationImpl(CtClass ctClass,
+            InternalClassTransformation parentTransformation)
+    {
+        _ctClass = ctClass;
+        _classPool = _ctClass.getClassPool();
+        _resourcesFieldName = parentTransformation.getResourcesFieldName();
+
+        _idAllocator = parentTransformation.getIdAllocator();
+
+        preloadMemberNames();
+
+        _constructorArgs = parentTransformation.getConstructorArgs();
+
+        int count = _constructorArgs.size();
+
+        // Build the call to the super-constructor.
+
+        _constructor.append("{ super(");
+
+        for (int i = 1; i <= count; i++)
+        {
+            if (i > 1)
+                _constructor.append(", ");
+
+            // $0 is implicitly self, so the 0-index ConstructorArg will be Javassisst
+            // pseudeo-variable $1, and so forth.
+
+            _constructor.append("$" + i);
+        }
+
+        _constructor.append(");\n");
+
+    }
+
     public String getResourcesFieldName()
     {
         return _resourcesFieldName;
@@ -261,7 +272,7 @@
 
     public String newMemberName(String suggested)
     {
-        checkFrozen();
+        failIfFrozen();
 
         String memberName = createMemberName(notBlank(suggested, "suggested"));
 
@@ -271,7 +282,7 @@
     /** Strips leading characters defined by {@link #NAME_PREFIX}. */
     private String createMemberName(String memberName)
     {
-        checkFrozen();
+        failIfFrozen();
 
         StringBuilder builder = new StringBuilder(memberName);
 
@@ -296,62 +307,63 @@
         return builder.toString();
     }
 
-    public void addFieldEncapsulation(CtField field, String readMethodName, String writeMethodName)
-    {
-        checkFrozen();
-
-        // TODO: Check if field already encapsulated
-
-        FieldEncapsulation fe = new FieldEncapsulation(readMethodName, writeMethodName);
-
-        _encapsulations.put(field, fe);
-    }
-
-    public void applyFieldEncapsulations()
-    {
-        checkFrozen();
-
-        ExprEditor editor = new ExprEditor()
-        {
-
-            @Override
-            public void edit(FieldAccess f) throws CannotCompileException
-            {
-                // Methods added as part of enhancement are NOT subject
-                // to field encapsulation.
-
-                if (_addedMethods.contains(f.where()))
-                    return;
-
-                try
-                {
-                    CtField field = f.getField();
-                    FieldEncapsulation fe = _encapsulations.get(field);
-
-                    if (fe != null)
-                        fe.encapsulate(f);
-                }
-                catch (NotFoundException ex)
-                {
-                    throw new RuntimeException(ex);
-                }
-            }
-
-        };
-
-        try
-        {
-            _ctClass.instrument(editor);
-        }
-        catch (CannotCompileException ex)
-        {
-            throw new RuntimeException(ex);
-        }
-    }
+    // public void addFieldEncapsulation(CtField field, String readMethodName, String
+    // writeMethodName)
+    // {
+    // failIfFrozen();
+    //
+    // // TODO: Check if field already encapsulated
+    //
+    // FieldEncapsulation fe = new FieldEncapsulation(readMethodName, writeMethodName);
+    //
+    // _encapsulations.put(field, fe);
+    // }
+    //
+    // public void applyFieldEncapsulations()
+    // {
+    // failIfFrozen();
+    //
+    // ExprEditor editor = new ExprEditor()
+    // {
+    //
+    // @Override
+    // public void edit(FieldAccess f) throws CannotCompileException
+    // {
+    // // Methods added as part of enhancement are NOT subject
+    // // to field encapsulation.
+    //
+    // if (_addedMethods.contains(f.where()))
+    // return;
+    //
+    // try
+    // {
+    // CtField field = f.getField();
+    // FieldEncapsulation fe = _encapsulations.get(field);
+    //
+    // if (fe != null)
+    // fe.encapsulate(f);
+    // }
+    // catch (NotFoundException ex)
+    // {
+    // throw new RuntimeException(ex);
+    // }
+    // }
+    //
+    // };
+    //
+    // try
+    // {
+    // _ctClass.instrument(editor);
+    // }
+    // catch (CannotCompileException ex)
+    // {
+    // throw new RuntimeException(ex);
+    // }
+    // }
 
     public void addImplementedInterface(Class interfaceClass)
     {
-        checkFrozen();
+        failIfFrozen();
 
         String interfaceName = interfaceClass.getName();
 
@@ -460,7 +472,7 @@
     {
         notBlank(fieldName, "fieldName");
 
-        checkFrozen();
+        failIfFrozen();
 
         Object existing = _claimedFields.get(fieldName);
 
@@ -482,7 +494,7 @@
 
     public void extendMethod(MethodSignature methodSignature, String methodBody)
     {
-        checkFrozen();
+        failIfFrozen();
 
         CtMethod method = findMethod(methodSignature);
 
@@ -574,7 +586,7 @@
 
     public String[] findUnclaimedFields()
     {
-        checkFrozen();
+        failIfFrozen();
 
         List<String> names = newList();
 
@@ -623,7 +635,7 @@
 
     public String addField(int modifiers, String type, String suggestedName)
     {
-        checkFrozen();
+        failIfFrozen();
 
         String fieldName = newMemberName(suggestedName);
 
@@ -655,7 +667,7 @@
     {
         notNull(type, "type");
 
-        checkFrozen();
+        failIfFrozen();
 
         // TODO: Probably doesn't handle arrays and primitives.
 
@@ -672,10 +684,10 @@
 
         String fieldName = addField(Modifier.PROTECTED, type.getName(), suggestedName);
 
-        _constructor.append(format("%s = $%d;\n", fieldName, ++_constructorArgIndex));
-
         _constructorArgs.add(new ConstructorArg(ctType, value));
 
+        _constructor.append(format("%s = $%d;\n", fieldName, _constructorArgs.size()));
+
         return fieldName;
     }
 
@@ -686,7 +698,7 @@
 
     public void finish()
     {
-        checkFrozen();
+        failIfFrozen();
 
         int count = _constructorArgs.size();
 
@@ -719,7 +731,7 @@
 
         // Free up stuff we don't need after freezing.
 
-        _addedMethods = null;
+        // _addedMethods = null;
         _claimedFields = null;
         _addedFieldNames = null;
         _fieldAnnotations = null;
@@ -750,11 +762,33 @@
         return new ReflectiveInstantiator(componentClass, parameters);
     }
 
-    private void checkFrozen()
+    private void failIfFrozen()
     {
         if (_frozen)
             throw new IllegalStateException("The ClassTransformation instance (for "
                     + _ctClass.getName()
                     + ") has completed all transformations and may not be further modified.");
     }
+
+    private void failIfNotFrozen()
+    {
+        if (!_frozen)
+            throw new IllegalStateException("The ClassTransformation instance (for "
+                    + _ctClass.getName() + ") has not yet completed all transformations.");
+    }
+
+    public IdAllocator getIdAllocator()
+    {
+        failIfNotFrozen();
+
+        return _idAllocator;
+    }
+
+    public List<ConstructorArg> getConstructorArgs()
+    {
+        failIfNotFrozen();
+
+        return CollectionFactory.newList(_constructorArgs);
+    }
+
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/CollectionFactory.java
URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/CollectionFactory.java?rev=397093&r1=397092&r2=397093&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/CollectionFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/util/CollectionFactory.java Tue Apr 25 22:42:35 2006
@@ -27,7 +27,7 @@
 /**
  * Static factory methods to ease the creation of new collection types (when using generics). Most
  * of these method leverage the compiler's ability to match generic types by return value. Typical
- * usage:
+ * usage (with a static import):
  * 
  * <pre>
  * Map&lt;Foo, Bar&gt; map = newMap();
@@ -70,6 +70,12 @@
     public static <T> List<T> newList()
     {
         return new ArrayList<T>();
+    }
+
+    /** Constructs and returns a new {@link ArrayList} as a copy of the provided list. */
+    public static <T> List<T> newList(List<T> list)
+    {
+        return new ArrayList<T>(list);
     }
 
     /** Easy way to convert an array of like-typed values into a list. */

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchTargetWrapper.java
URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchTargetWrapper.java?rev=397093&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchTargetWrapper.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchTargetWrapper.java Tue Apr 25 22:42:35 2006
@@ -0,0 +1,25 @@
+package org.apache.tapestry.internal.aspects;
+
+import org.apache.tapestry.internal.annotations.Synchronized;
+
+/**
+ * Class used to test the {@link Synchronization} aspect.
+ * 
+ * @author Howard M. Lewis Ship
+ */
+@Synchronized
+public class SynchTargetWrapper implements Runnable
+{
+    private final SynchronizationTarget _target;
+
+    public SynchTargetWrapper(SynchronizationTarget target)
+    {
+        _target = target;
+    }
+
+    @Synchronized.Read
+    public void run()
+    {
+        _target.incrementCounter();
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchronizationAspectTest.java
URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchronizationAspectTest.java?rev=397093&r1=397092&r2=397093&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchronizationAspectTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchronizationAspectTest.java Tue Apr 25 22:42:35 2006
@@ -8,7 +8,6 @@
 
 import static org.apache.tapestry.util.CollectionFactory.newList;
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
 
 /** @author Howard M. Lewis Ship */
 public class SynchronizationAspectTest extends TestBase
@@ -21,9 +20,9 @@
         _target = new SynchronizationTarget();
     }
 
-    private static final int THREAD_COUNT = 1000;
+    private static final int THREAD_COUNT = 100;
 
-    private static final int THREAD_BLOCK_SIZE = 50;
+    private static final int THREAD_BLOCK_SIZE = 5;
 
     @Test
     public void readLockThenWriteLock() throws Exception
@@ -32,12 +31,7 @@
         {
             public void run()
             {
-
-                int start = _target.getCounter();
-
                 _target.incrementCounter();
-
-                assertTrue(_target.getCounter() > start);
             }
         };
 
@@ -51,25 +45,63 @@
         {
             public void run()
             {
+                // Gets a write lock, then a read lock.
+                _target.incrementCounterHard();
+            }
+        };
 
-                int start = _target.getCounter();
+        runOperation(operation);
+    }
 
-                // Gets a write lock, then a read lock.
+    @Test(enabled = true)
+    public void writeLockInsideReadLock() throws Exception
+    {
+        Runnable operation = new Runnable()
+        {
+            public void run()
+            {
+                // A read lock method that upgrades to a write lock
 
-                _target.incrementCounterHard();
+                _target.incrementIfNonNegative();
+            }
+        };
 
-                // If we tried _target.incrementIfNonNegative() we would
-                // hang!
+        runOperation(operation);
+    }
+
+    @Test(enabled = true)
+    public void testIndirection() throws Exception
+    {
+        Runnable operation = new Runnable()
+        {
+            public void run()
+            {
+
+                // Read lock method invokes other class, that invokes write method.
 
-                assertTrue(_target.getCounter() > start);
+                _target.incrementViaRunnable();
             }
         };
 
         runOperation(operation);
     }
 
+    /**
+     * Test that locking, especially read lock upgrade and downgrade, work properly when there's
+     * more than one object involved.
+     */
+    @Test
+    public void testMultipleObjects() throws Exception
+    {
+        Runnable operation = new SynchTargetWrapper(_target);
+
+        runOperation(operation);
+    }
+
     private void runOperation(Runnable operation) throws InterruptedException
     {
+        // System.out.println("** Start synchronization");
+
         List<Thread> threads = newList();
         List<Thread> running = newList();
 
@@ -92,6 +124,8 @@
             t.join();
 
         assertEquals(_target.getCounter(), THREAD_COUNT);
+
+        // System.out.println("** End synchronization");
     }
 
     private void startThreads(List<Thread> threads, List<Thread> running)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchronizationTarget.java
URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchronizationTarget.java?rev=397093&r1=397092&r2=397093&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchronizationTarget.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/aspects/SynchronizationTarget.java Tue Apr 25 22:42:35 2006
@@ -3,7 +3,7 @@
 import org.apache.tapestry.internal.annotations.Synchronized;
 
 /**
- * Class used to test the {@link Synchorization} aspect.
+ * Class used to test the {@link Synchronization} aspect.
  * 
  * @author Howard M. Lewis Ship
  */
@@ -12,6 +12,13 @@
 {
     private int _counter;
 
+    // Used to check if read locks accumulate when @Read calls @Read
+    @Synchronized.Read
+    public int readCounter()
+    {
+        return getCounter();
+    }
+    
     @Synchronized.Read
     public int getCounter()
     {
@@ -35,6 +42,20 @@
     {
         if (_counter >= 0)
             incrementCounter();
+    }
+
+    @Synchronized.Read
+    public void incrementViaRunnable()
+    {
+        Runnable r = new Runnable()
+        {
+            public void run()
+            {
+                incrementCounter();
+            }
+        };
+
+        r.run();
     }
 
     @Synchronized.Write

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/util/CollectionFactoryTest.java
URL: http://svn.apache.org/viewcvs/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/util/CollectionFactoryTest.java?rev=397093&r1=397092&r2=397093&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/util/CollectionFactoryTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/util/CollectionFactoryTest.java Tue Apr 25 22:42:35 2006
@@ -22,6 +22,7 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import static org.apache.tapestry.util.CollectionFactory.newList;
@@ -29,6 +30,7 @@
 import static org.apache.tapestry.util.CollectionFactory.newSet;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotSame;
 import static org.testng.Assert.assertTrue;
 
 /**
@@ -84,5 +86,15 @@
 
         assertEquals(actual, Arrays.asList(new String[]
         { "Fred", "Barney", "Wilma" }));
+    }
+
+    @Test
+    public void newListCopy()
+    {
+        List<String> start = newList("Fred", "Barney", "Wilma");
+        List<String> copy = newList(start);
+
+        assertNotSame(copy, start);
+        assertEquals(copy, start);
     }
 }



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