You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/05/31 00:19:42 UTC

incubator-juneau git commit: Fix race condition in BeanContext/ClassMeta

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 <ja...@apache.org>
Authored: Tue May 30 20:19:40 2017 -0400
Committer: JamesBognar <ja...@apache.org>
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() {