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 2008/09/08 04:25:12 UTC

svn commit: r692984 - in /tapestry/tapestry5/trunk: support/ tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ tapestry-core/src/test/conf/ tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ tapestry-ioc/src/main/java/org/a...

Author: hlship
Date: Sun Sep  7 19:25:11 2008
New Revision: 692984

URL: http://svn.apache.org/viewvc?rev=692984&view=rev
Log:
TAPESTRY-2561: Rapidly refreshing a page, even the same page, can cause a deadlock related to class loading

Added:
    tapestry/tapestry5/trunk/support/heavy-load.jmx
Removed:
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/InternalConstants.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/conf/testng.xml
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/CtClassSourceImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImpl.java

Added: tapestry/tapestry5/trunk/support/heavy-load.jmx
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/support/heavy-load.jmx?rev=692984&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/support/heavy-load.jmx (added)
+++ tapestry/tapestry5/trunk/support/heavy-load.jmx Sun Sep  7 19:25:11 2008
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<jmeterTestPlan version="1.2" properties="2.1">
+  <hashTree>
+    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
+      <stringProp name="TestPlan.comments"></stringProp>
+      <boolProp name="TestPlan.functional_mode">false</boolProp>
+      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
+      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
+        <collectionProp name="Arguments.arguments"/>
+      </elementProp>
+      <stringProp name="TestPlan.user_define_classpath"></stringProp>
+    </TestPlan>
+    <hashTree>
+      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
+        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
+          <boolProp name="LoopController.continue_forever">false</boolProp>
+          <stringProp name="LoopController.loops">5</stringProp>
+        </elementProp>
+        <stringProp name="ThreadGroup.num_threads">20</stringProp>
+        <stringProp name="ThreadGroup.ramp_time">0</stringProp>
+        <longProp name="ThreadGroup.start_time">1220812823000</longProp>
+        <longProp name="ThreadGroup.end_time">1220812823000</longProp>
+        <boolProp name="ThreadGroup.scheduler">false</boolProp>
+        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
+        <stringProp name="ThreadGroup.duration"></stringProp>
+        <stringProp name="ThreadGroup.delay"></stringProp>
+      </ThreadGroup>
+      <hashTree>
+        <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP Request Defaults" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain">localhost</stringProp>
+          <stringProp name="HTTPSampler.port">8080</stringProp>
+          <stringProp name="HTTPSampler.protocol">http</stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">/</stringProp>
+          <boolProp name="HTTPSampler.image_parser">true</boolProp>
+        </ConfigTestElement>
+        <hashTree/>
+        <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true">
+          <collectionProp name="CookieManager.cookies"/>
+          <boolProp name="CookieManager.clearEachIteration">true</boolProp>
+          <stringProp name="CookieManager.policy">rfc2109</stringProp>
+        </CookieManager>
+        <hashTree/>
+        <HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="HTTP Request" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain">localhost</stringProp>
+          <stringProp name="HTTPSampler.port">8080</stringProp>
+          <stringProp name="HTTPSampler.protocol">http</stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">/</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.FILE_NAME"></stringProp>
+          <stringProp name="HTTPSampler.FILE_FIELD"></stringProp>
+          <stringProp name="HTTPSampler.mimetype"></stringProp>
+          <boolProp name="HTTPSampler.image_parser">true</boolProp>
+          <stringProp name="HTTPSampler.monitor">false</stringProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+        </HTTPSampler>
+        <hashTree/>
+        <HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="HTTP Request" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">/ValidBeanEditorDemo</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.FILE_NAME"></stringProp>
+          <stringProp name="HTTPSampler.FILE_FIELD"></stringProp>
+          <stringProp name="HTTPSampler.mimetype"></stringProp>
+          <stringProp name="HTTPSampler.monitor">false</stringProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+        </HTTPSampler>
+        <hashTree/>
+        <HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="HTTP Request" enabled="true">
+          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
+            <collectionProp name="Arguments.arguments"/>
+          </elementProp>
+          <stringProp name="HTTPSampler.domain"></stringProp>
+          <stringProp name="HTTPSampler.port"></stringProp>
+          <stringProp name="HTTPSampler.protocol"></stringProp>
+          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+          <stringProp name="HTTPSampler.path">/griddemo</stringProp>
+          <stringProp name="HTTPSampler.method">GET</stringProp>
+          <boolProp name="HTTPSampler.follow_redirects">false</boolProp>
+          <boolProp name="HTTPSampler.auto_redirects">true</boolProp>
+          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+          <stringProp name="HTTPSampler.FILE_NAME"></stringProp>
+          <stringProp name="HTTPSampler.FILE_FIELD"></stringProp>
+          <stringProp name="HTTPSampler.mimetype"></stringProp>
+          <stringProp name="HTTPSampler.monitor">false</stringProp>
+          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+        </HTTPSampler>
+        <hashTree/>
+        <ResultCollector guiclass="GraphVisualizer" testclass="ResultCollector" testname="Graph Results" enabled="true">
+          <boolProp name="ResultCollector.error_logging">false</boolProp>
+          <objProp>
+            <name>saveConfig</name>
+            <value class="SampleSaveConfiguration">
+              <time>true</time>
+              <latency>true</latency>
+              <timestamp>true</timestamp>
+              <success>true</success>
+              <label>true</label>
+              <code>true</code>
+              <message>true</message>
+              <threadName>true</threadName>
+              <dataType>true</dataType>
+              <encoding>false</encoding>
+              <assertions>true</assertions>
+              <subresults>true</subresults>
+              <responseData>false</responseData>
+              <samplerData>false</samplerData>
+              <xml>true</xml>
+              <fieldNames>false</fieldNames>
+              <responseHeaders>false</responseHeaders>
+              <requestHeaders>false</requestHeaders>
+              <responseDataOnError>false</responseDataOnError>
+              <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
+              <assertionsResultsToSave>0</assertionsResultsToSave>
+              <bytes>true</bytes>
+            </value>
+          </objProp>
+          <stringProp name="filename"></stringProp>
+        </ResultCollector>
+        <hashTree/>
+        <ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true">
+          <boolProp name="ResultCollector.error_logging">false</boolProp>
+          <objProp>
+            <name>saveConfig</name>
+            <value class="SampleSaveConfiguration">
+              <time>true</time>
+              <latency>true</latency>
+              <timestamp>true</timestamp>
+              <success>true</success>
+              <label>true</label>
+              <code>true</code>
+              <message>true</message>
+              <threadName>true</threadName>
+              <dataType>true</dataType>
+              <encoding>false</encoding>
+              <assertions>true</assertions>
+              <subresults>true</subresults>
+              <responseData>false</responseData>
+              <samplerData>false</samplerData>
+              <xml>true</xml>
+              <fieldNames>false</fieldNames>
+              <responseHeaders>false</responseHeaders>
+              <requestHeaders>false</requestHeaders>
+              <responseDataOnError>false</responseDataOnError>
+              <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
+              <assertionsResultsToSave>0</assertionsResultsToSave>
+              <bytes>true</bytes>
+            </value>
+          </objProp>
+          <stringProp name="filename"></stringProp>
+        </ResultCollector>
+        <hashTree/>
+      </hashTree>
+    </hashTree>
+  </hashTree>
+</jmeterTestPlan>

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java?rev=692984&r1=692983&r2=692984&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java Sun Sep  7 19:25:11 2008
@@ -18,7 +18,6 @@
 import org.apache.tapestry5.internal.event.InvalidationEventHubImpl;
 import org.apache.tapestry5.internal.events.UpdateListener;
 import org.apache.tapestry5.internal.util.URLChangeTracker;
-import org.apache.tapestry5.ioc.internal.InternalConstants;
 import org.apache.tapestry5.ioc.internal.services.ClassFactoryClassPool;
 import org.apache.tapestry5.ioc.internal.services.ClassFactoryImpl;
 import org.apache.tapestry5.ioc.internal.services.CtClassSource;
@@ -73,6 +72,21 @@
             super(parent, classPool);
         }
 
+
+        /**
+         * Synchronizes on the parent class loader before continuing, which is necessary to prevent thread deadlocks. Any classes
+         * loaded, or transformed, by this class loader will do so with the parent (context) class loader locked.
+         * The required order is always that the context class loader be locked, then the child class loader.  Painful.
+         */
+        @Override
+        protected Class loadClass(String name, boolean resolve) throws ClassFormatError, ClassNotFoundException
+        {
+            synchronized (getParent())
+            {
+                return super.loadClass(name, resolve);
+            }
+        }
+
         /**
          * Determines if the class name represents a component class from a controlled package.  If so,
          * super.findClass() will load it and transform it. Returns null if not in a controlled package, allowing the
@@ -100,7 +114,6 @@
 
             return null;
         }
-
     }
 
     public ComponentInstantiatorSourceImpl(Logger logger, ClassLoader parent, ComponentClassTransformer transformer,
@@ -160,7 +173,9 @@
         classFactory = new ClassFactoryImpl(loader, classPool, classSource, logger);
     }
 
-    // This is called from well within a synchronized block.
+    // This is called from well within a synchronized block.    The component layer class loader,
+    // and the context class loader, should each be locked.
+
     public void onLoad(ClassPool pool, String classname) throws NotFoundException, CannotCompileException
     {
         logger.debug("BEGIN onLoad " + classname);
@@ -179,20 +194,17 @@
 
         try
         {
-            synchronized (InternalConstants.GLOBAL_CLASS_CREATION_MUTEX)
-            {
-                CtClass ctClass = pool.get(classname);
+            CtClass ctClass = pool.get(classname);
 
-                // Force the creation of the super-class before the target class.
+            // Force the creation of the super-class before the target class.
 
-                forceSuperclassTransform(ctClass);
+            forceSuperclassTransform(ctClass);
 
-                // Do the transformations here
+            // Do the transformations here
 
-                transformer.transformComponentClass(ctClass, loader);
+            transformer.transformComponentClass(ctClass, loader);
 
-                writeClassToFileSystemForHardCoreDebuggingPurposesOnly(ctClass);
-            }
+            writeClassToFileSystemForHardCoreDebuggingPurposesOnly(ctClass);
 
             diag = "END";
         }
@@ -218,7 +230,6 @@
             ctClass.writeFile(JAVASSIST_WRITE_DIR);
             ctClass.defrost();
             ctClass.stopPruning(p);
-
         }
         catch (Exception ex)
         {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/conf/testng.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/conf/testng.xml?rev=692984&r1=692983&r2=692984&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/conf/testng.xml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/conf/testng.xml Sun Sep  7 19:25:11 2008
@@ -15,7 +15,9 @@
    limitations under the License.
 -->
 
-<suite name="Tapestry Core" thread-count="10" annotations="1.5" verbose="2" parallel="tests">
+<!-- The suite may no longer be run in parallel, because of some tricky issues related to locking of class loaders. Running in parallel causes thread deadlocks, when unmanaged
+tests run at the same time as Selenium-based integration tests.  See TAPESTRY-2561. -->
+<suite name="Tapestry Core" annotations="1.5" verbose="2">
     <test name="Integration Tests">
         <packages>
             <package name="org.apache.tapestry5.integration"/>

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java?rev=692984&r1=692983&r2=692984&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java Sun Sep  7 19:25:11 2008
@@ -20,9 +20,7 @@
 import org.apache.tapestry5.ioc.def.ModuleDef;
 import org.apache.tapestry5.ioc.def.ServiceDef;
 import org.apache.tapestry5.ioc.internal.services.JustInTimeObjectCreator;
-import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.ioc.internal.util.Defense;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.internal.util.*;
 import org.apache.tapestry5.ioc.services.*;
 import org.slf4j.Logger;
 
@@ -46,12 +44,11 @@
 
     private final Logger logger;
 
-    // Guarded by InternalConstants.GLOBAL_CLASS_CREATION_MUTEX
     private Object moduleBuilder;
 
     // Set to true when invoking the module constructor. Used to
     // detect endless loops caused by irresponsible dependencies in
-    // the constructor. Guarded by InternalConstants.GLOBAL_CLASS_CREATION_MUTEX.
+    // the constructor.
     private boolean insideConstructor;
 
     /**
@@ -59,6 +56,8 @@
      */
     private final Map<String, Object> services = CollectionFactory.newCaseInsensitiveMap();
 
+    private final ConcurrentBarrier barrier = new ConcurrentBarrier();
+
     public ModuleImpl(InternalRegistry registry, ServiceActivityTracker tracker, ModuleDef moduleDef,
                       ClassFactory classFactory, Logger logger)
     {
@@ -135,47 +134,58 @@
 
     /**
      * Locates the service proxy for a particular service (from the service definition).
-     * <p/>
-     * Access is synchronized via {@link InternalConstants#GLOBAL_CLASS_CREATION_MUTEX}.
      *
      * @param def              defines the service
      * @param eagerLoadProxies collection into which proxies for eager loaded services are added (or null)
      * @return the service proxy
      */
-    private Object findOrCreate(ServiceDef def, Collection<EagerLoadServiceProxy> eagerLoadProxies)
+    private synchronized Object findOrCreate(final ServiceDef def,
+                                             final Collection<EagerLoadServiceProxy> eagerLoadProxies)
     {
-        synchronized (InternalConstants.GLOBAL_CLASS_CREATION_MUTEX)
+        final String key = def.getServiceId();
+
+        final Invokable create = new Invokable()
         {
-            String key = def.getServiceId();
+            public Object invoke()
+            {
+                Object result = create(def, eagerLoadProxies);
 
-            Object result = services.get(key);
+                services.put(key, result);
 
-            if (result == null)
+                return result;
+            }
+        };
+
+        Invokable find = new Invokable()
+        {
+            public Object invoke()
             {
-                result = create(def, eagerLoadProxies);
-                services.put(key, result);
+                Object result = services.get(key);
+
+                if (result == null)
+                {
+                    result = barrier.withWrite(create);
+                }
+
+                return result;
             }
+        };
 
-            return result;
-        }
+        return barrier.withRead(find);
     }
 
     public void collectEagerLoadServices(Collection<EagerLoadServiceProxy> proxies)
     {
-        synchronized (InternalConstants.GLOBAL_CLASS_CREATION_MUTEX)
+        for (String serviceId : moduleDef.getServiceIds())
         {
-            for (String serviceId : moduleDef.getServiceIds())
-            {
-                ServiceDef def = moduleDef.getServiceDef(serviceId);
+            ServiceDef def = moduleDef.getServiceDef(serviceId);
 
-                if (def.isEagerLoad()) findOrCreate(def, proxies);
-            }
+            if (def.isEagerLoad()) findOrCreate(def, proxies);
         }
     }
 
     /**
-     * Creates the service and updates the cache of created services. Access is synchronized via {@link
-     * InternalConstants#GLOBAL_CLASS_CREATION_MUTEX}.
+     * Creates the service and updates the cache of created services.
      *
      * @param eagerLoadProxies a list into which any eager loaded proxies should be added
      */
@@ -239,20 +249,30 @@
         }
     }
 
-    public Object getModuleBuilder()
+    private final Runnable instantiateModuleBuilder = new Runnable()
+    {
+        public void run()
+        {
+            moduleBuilder = constructModuleBuilder();
+        }
+    };
+
+    private final Invokable provideModuleBuilder = new Invokable<Object>()
     {
-        synchronized (InternalConstants.GLOBAL_CLASS_CREATION_MUTEX)
+        public Object invoke()
         {
-            if (moduleBuilder == null) moduleBuilder = instantiateModuleBuilder();
+            if (moduleBuilder == null) barrier.withWrite(instantiateModuleBuilder);
 
             return moduleBuilder;
         }
+    };
+
+    public Object getModuleBuilder()
+    {
+        return barrier.withRead(provideModuleBuilder);
     }
 
-    /**
-     * Access synchronized by MUTEX.
-     */
-    private Object instantiateModuleBuilder()
+    private Object constructModuleBuilder()
     {
         Class builderClass = moduleDef.getBuilderClass();
 
@@ -338,7 +358,7 @@
         classFab.addField("creator", Modifier.PRIVATE | Modifier.FINAL, ObjectCreator.class);
         classFab.addField("token", Modifier.PRIVATE | Modifier.FINAL, ServiceProxyToken.class);
 
-        classFab.addConstructor(new Class[]{ObjectCreator.class, ServiceProxyToken.class}, null,
+        classFab.addConstructor(new Class[] {ObjectCreator.class, ServiceProxyToken.class}, null,
                                 "{ creator = $1; token = $2; }");
 
         // Make proxies serializable by writing the token to the stream.
@@ -348,7 +368,7 @@
         // This is the "magic" signature that allows an object to substitute some other
         // object for itself.
         MethodSignature writeReplaceSig = new MethodSignature(Object.class, "writeReplace", null,
-                                                              new Class[]{ObjectStreamException.class});
+                                                              new Class[] {ObjectStreamException.class});
 
         classFab.addMethod(Modifier.PRIVATE, writeReplaceSig, "return token;");
 
@@ -397,5 +417,4 @@
     {
         return moduleDef.getLoggerName();
     }
-
 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/CtClassSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/CtClassSourceImpl.java?rev=692984&r1=692983&r2=692984&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/CtClassSourceImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/CtClassSourceImpl.java Sun Sep  7 19:25:11 2008
@@ -16,7 +16,6 @@
 
 import javassist.CtClass;
 import javassist.NotFoundException;
-import org.apache.tapestry5.ioc.internal.InternalConstants;
 import org.apache.tapestry5.ioc.services.ClassFabUtils;
 
 import java.security.ProtectionDomain;
@@ -49,7 +48,7 @@
         this.loader = loader;
     }
 
-    public CtClass toCtClass(Class searchClass)
+    public synchronized CtClass toCtClass(Class searchClass)
     {
         ClassLoader loader = searchClass.getClassLoader();
 
@@ -75,7 +74,7 @@
         }
     }
 
-    public synchronized CtClass newClass(String name, Class superClass)
+    public CtClass newClass(String name, Class superClass)
     {
         CtClass ctSuperClass = toCtClass(superClass);
 
@@ -84,17 +83,20 @@
 
     private static final String WRITE_DIR = System.getProperty("javassist-write-dir");
 
-    public synchronized Class createClass(CtClass ctClass)
+    public Class createClass(CtClass ctClass)
     {
         if (WRITE_DIR != null) writeClass(ctClass);
 
-        synchronized (InternalConstants.GLOBAL_CLASS_CREATION_MUTEX)
+        synchronized (loader)
         {
             try
             {
                 Class result = pool.toClass(ctClass, loader, domain);
 
-                createdClassCount++;
+                synchronized (this)
+                {
+                    createdClassCount++;
+                }
 
                 return result;
             }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImpl.java?rev=692984&r1=692983&r2=692984&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImpl.java Sun Sep  7 19:25:11 2008
@@ -14,7 +14,6 @@
 
 package org.apache.tapestry5.ioc.internal.services;
 
-import org.apache.tapestry5.ioc.internal.InternalConstants;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.services.ClassPropertyAdapter;
 import org.apache.tapestry5.ioc.services.PropertyAccess;
@@ -75,15 +74,13 @@
      * serializes access to the Java Beans Introspector, which is not thread safe. In addition, handles the case where
      * the class in question is an interface, accumulating properties inherited from super-classes.
      */
-    private ClassPropertyAdapter buildAdapter(Class forClass)
+    private synchronized ClassPropertyAdapter buildAdapter(Class forClass)
     {
         // In some race conditions, we may hit this method for the same class multiple times.
         // We just let it happen, replacing the old ClassPropertyAdapter with a new one.
 
         try
         {
-            synchronized (InternalConstants.GLOBAL_CLASS_CREATION_MUTEX)
-            {
                 BeanInfo info = Introspector.getBeanInfo(forClass);
 
                 List<PropertyDescriptor> descriptors = CollectionFactory.newList();
@@ -93,7 +90,6 @@
                 if (forClass.isInterface()) addPropertiesFromExtendedInterfaces(forClass, descriptors);
 
                 return new ClassPropertyAdapterImpl(forClass, descriptors);
-            }
         }
         catch (Throwable ex)
         {