Repository: incubator-juneau Updated Branches: refs/heads/master c4952d2cf -> ce2d7fbe1
Fix race condition in BeanContext/ClassMeta Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/ce2d7fbe Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/ce2d7fbe Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/ce2d7fbe Branch: refs/heads/master Commit: ce2d7fbe1e53d4e9f2210e19101e5cc78b0c82ea Parents: c4952d2 Author: JamesBognar <[email protected]> Authored: Tue May 30 20:19:40 2017 -0400 Committer: JamesBognar <[email protected]> Committed: Tue May 30 20:19:40 2017 -0400 ---------------------------------------------------------------------- .../java/org/apache/juneau/BeanContext.java | 15 +++ .../org/apache/juneau/BeanPropertyValue.java | 9 ++ .../main/java/org/apache/juneau/ClassMeta.java | 105 +++++++++++-------- 3 files changed, 85 insertions(+), 44 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ce2d7fbe/juneau-core/src/main/java/org/apache/juneau/BeanContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java index 5b1f166..69fd415 100644 --- a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java +++ b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java @@ -1048,6 +1048,19 @@ public class BeanContext extends Context { * Otherwise, returns a new {@link ClassMeta} object every time.<br> */ public final <T> ClassMeta<T> getClassMeta(Class<T> type) { + return getClassMeta(type, true); + } + + /** + * Construct a {@code ClassMeta} wrapper around a {@link Class} object. + * + * @param <T> The class type being wrapped. + * @param type The class to resolve. + * @param waitForInit If <jk>true</jk>, wait for the ClassMeta constructor to finish before returning. + * @return If the class is not an array, returns a cached {@link ClassMeta} object. + * Otherwise, returns a new {@link ClassMeta} object every time.<br> + */ + final <T> ClassMeta<T> getClassMeta(Class<T> type, boolean waitForInit) { // If this is an array, then we want it wrapped in an uncached ClassMeta object. // Note that if it has a pojo swap, we still want to cache it so that @@ -1069,6 +1082,8 @@ public class BeanContext extends Context { cm = new ClassMeta<T>(type, this, findImplClass(type), findBeanFilter(type), findPojoSwap(type), findChildPojoSwaps(type)); } } + if (waitForInit) + cm.waitForInit(); return cm; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ce2d7fbe/juneau-core/src/main/java/org/apache/juneau/BeanPropertyValue.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyValue.java b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyValue.java index 68abb37..856968c 100644 --- a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyValue.java +++ b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyValue.java @@ -82,4 +82,13 @@ public class BeanPropertyValue implements Comparable<BeanPropertyValue> { public int compareTo(BeanPropertyValue o) { return name.compareTo(o.name); } + + @Override /* Object */ + public String toString() { + return new ObjectMap() + .append("name", name) + .append("value", value) + .append("type", pMeta.getClassMeta().getInnerClass().getSimpleName()) + .toString(); + } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ce2d7fbe/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java index 4c1a333..e560080 100644 --- a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java @@ -22,6 +22,7 @@ import java.net.*; import java.net.URI; import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.locks.*; import org.apache.juneau.annotation.*; import org.apache.juneau.internal.*; @@ -117,6 +118,9 @@ public final class ClassMeta<T> implements Type { private static final Double DOUBLE_DEFAULT = 0d; private static final Byte BYTE_DEFAULT = (byte)0; + private ReadWriteLock lock = new ReentrantReadWriteLock(false); + private Lock rLock = lock.readLock(), wLock = lock.writeLock(); + /** * Construct a new {@code ClassMeta} based on the specified {@link Class}. * @@ -139,49 +143,62 @@ public final class ClassMeta<T> implements Type { this.innerClass = innerClass; this.beanContext = beanContext; - // We always immediately add this class meta to the bean context cache so that we can resolve recursive references. - if (beanContext != null && beanContext.cmCache != null) - beanContext.cmCache.put(innerClass, this); - - ClassMetaBuilder<T> builder = new ClassMetaBuilder(innerClass, beanContext, implClass, beanFilter, pojoSwap, childPojoSwaps); - - this.cc = builder.cc; - this.isDelegate = builder.isDelegate; - this.fromStringMethod = builder.fromStringMethod; - this.swapMethod = builder.swapMethod; - this.unswapMethod = builder.unswapMethod; - this.swapMethodType = builder.swapMethodType; - this.parentPropertyMethod = builder.parentPropertyMethod; - this.namePropertyMethod = builder.namePropertyMethod; - this.noArgConstructor = builder.noArgConstructor; - this.stringConstructor = builder.stringConstructor; - this.swapConstructor = builder.swapConstructor; - this.numberConstructor = builder.numberConstructor; - this.numberConstructorType = builder.numberConstructorType; - this.primitiveDefault = builder.primitiveDefault; - this.publicMethods = builder.publicMethods; - this.remoteableMethods = builder.remoteableMethods; - this.beanFilter = beanFilter; - this.pojoSwap = builder.pojoSwap; - this.extMeta = new MetadataMap(); - this.keyType = builder.keyType; - this.valueType = builder.valueType; - this.elementType = builder.elementType; - this.notABeanReason = builder.notABeanReason; - this.beanMeta = builder.beanMeta; - this.initException = builder.initException; - this.typePropertyName = builder.typePropertyName; - this.dictionaryName = builder.dictionaryName; - this.serializedClassMeta = builder.serializedClassMeta; - this.invocationHandler = builder.invocationHandler; - this.beanRegistry = builder.beanRegistry; - this.isMemberClass = builder.isMemberClass; - this.isAbstract = builder.isAbstract; - this.implClass = builder.implClass; - this.childUnswapMap = builder.childUnswapMap; - this.childSwapMap = builder.childSwapMap; - this.childPojoSwaps = builder.childPojoSwaps; - this.args = null; + wLock.lock(); + try { + // We always immediately add this class meta to the bean context cache so that we can resolve recursive references. + if (beanContext != null && beanContext.cmCache != null) + beanContext.cmCache.put(innerClass, this); + + ClassMetaBuilder<T> builder = new ClassMetaBuilder(innerClass, beanContext, implClass, beanFilter, pojoSwap, childPojoSwaps); + + this.cc = builder.cc; + this.isDelegate = builder.isDelegate; + this.fromStringMethod = builder.fromStringMethod; + this.swapMethod = builder.swapMethod; + this.unswapMethod = builder.unswapMethod; + this.swapMethodType = builder.swapMethodType; + this.parentPropertyMethod = builder.parentPropertyMethod; + this.namePropertyMethod = builder.namePropertyMethod; + this.noArgConstructor = builder.noArgConstructor; + this.stringConstructor = builder.stringConstructor; + this.swapConstructor = builder.swapConstructor; + this.numberConstructor = builder.numberConstructor; + this.numberConstructorType = builder.numberConstructorType; + this.primitiveDefault = builder.primitiveDefault; + this.publicMethods = builder.publicMethods; + this.remoteableMethods = builder.remoteableMethods; + this.beanFilter = beanFilter; + this.pojoSwap = builder.pojoSwap; + this.extMeta = new MetadataMap(); + this.keyType = builder.keyType; + this.valueType = builder.valueType; + this.elementType = builder.elementType; + this.notABeanReason = builder.notABeanReason; + this.beanMeta = builder.beanMeta; + this.initException = builder.initException; + this.typePropertyName = builder.typePropertyName; + this.dictionaryName = builder.dictionaryName; + this.serializedClassMeta = builder.serializedClassMeta; + this.invocationHandler = builder.invocationHandler; + this.beanRegistry = builder.beanRegistry; + this.isMemberClass = builder.isMemberClass; + this.isAbstract = builder.isAbstract; + this.implClass = builder.implClass; + this.childUnswapMap = builder.childUnswapMap; + this.childSwapMap = builder.childSwapMap; + this.childPojoSwaps = builder.childPojoSwaps; + this.args = null; + } finally { + wLock.unlock(); + } + } + + /** + * Causes thread to wait until constructor has exited. + */ + final void waitForInit() { + rLock.lock(); + rLock.unlock(); } /** @@ -687,7 +704,7 @@ public final class ClassMeta<T> implements Type { } private ClassMeta<?> findClassMeta(Class<?> c) { - return beanContext.getClassMeta(c); + return beanContext.getClassMeta(c, false); } private ClassMeta<?>[] findParameters() {
