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() {

Reply via email to