Skip to content
Browse files

TAPESTRY-2561: Rapidly refreshing a page, even the same page, can cau…

…se a deadlock related to class loading

git-svn-id: https://svn.apache.org/repos/asf/tapestry/tapestry5/trunk@692984 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
1 parent 1b6ca8f commit 571819f7b5d396d264d9d3b1c82d0676b0dc87af @hlship hlship committed Sep 8, 2008
View
178 support/heavy-load.jmx
@@ -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>
View
37 ...src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java
@@ -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 @@ public PackageAwareLoader(ClassLoader parent, ClassPool classPool)
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 @@ protected Class findClass(String className) throws ClassNotFoundException
return null;
}
-
}
public ComponentInstantiatorSourceImpl(Logger logger, ClassLoader parent, ComponentClassTransformer transformer,
@@ -160,7 +173,9 @@ private void initializeService()
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 @@ public void onLoad(ClassPool pool, String classname) throws NotFoundException, C
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 @@ private void writeClassToFileSystemForHardCoreDebuggingPurposesOnly(CtClass ctCl
ctClass.writeFile(JAVASSIST_WRITE_DIR);
ctClass.defrost();
ctClass.stopPruning(p);
-
}
catch (Exception ex)
{
View
4 tapestry-core/src/test/conf/testng.xml
@@ -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"/>
View
25 tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/InternalConstants.java
@@ -1,25 +0,0 @@
-// Copyright 2008 The Apache Software Foundation
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package org.apache.tapestry5.ioc.internal;
-
-public class InternalConstants
-{
- /**
- * Mutex used to ensure that there are no thread conflicts when creating new classes using Javassist.
- * <p/>
- * See TAPESTRY-2561.
- */
- public static final Object GLOBAL_CLASS_CREATION_MUTEX = new Object();
-}
View
87 tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
@@ -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,19 +44,20 @@
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;
/**
* Keyed on fully qualified service id; values are instantiated services (proxies).
*/
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 @@ public ModuleImpl(InternalRegistry registry, ServiceActivityTracker tracker, Mod
/**
* 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 @@ private Object create(ServiceDef def, Collection<EagerLoadServiceProxy> eagerLoa
}
}
- 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 @@ private Object createProxyInstance(ObjectCreator creator, String serviceId, Clas
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 @@ private Object createProxyInstance(ObjectCreator creator, String serviceId, Clas
// 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 @@ public String getLoggerName()
{
return moduleDef.getLoggerName();
}
-
}
View
14 tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/CtClassSourceImpl.java
@@ -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 @@ public CtClassSourceImpl(ClassFactoryClassPool pool, ClassLoader loader)
this.loader = loader;
}
- public CtClass toCtClass(Class searchClass)
+ public synchronized CtClass toCtClass(Class searchClass)
{
ClassLoader loader = searchClass.getClassLoader();
@@ -75,7 +74,7 @@ public CtClass toCtClass(String name)
}
}
- public synchronized CtClass newClass(String name, Class superClass)
+ public CtClass newClass(String name, Class superClass)
{
CtClass ctSuperClass = toCtClass(superClass);
@@ -84,17 +83,20 @@ public synchronized CtClass newClass(String name, Class superClass)
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;
}
View
6 ...stry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImpl.java
@@ -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 @@ public ClassPropertyAdapter getAdapter(Class forClass)
* 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 @@ private ClassPropertyAdapter buildAdapter(Class forClass)
if (forClass.isInterface()) addPropertiesFromExtendedInterfaces(forClass, descriptors);
return new ClassPropertyAdapterImpl(forClass, descriptors);
- }
}
catch (Throwable ex)
{

0 comments on commit 571819f

Please sign in to comment.
Something went wrong with that request. Please try again.