From 2f7be5df3d4bd57b0ffbb89b32919527e64e9e2d Mon Sep 17 00:00:00 2001 From: Valentin Iancu Date: Tue, 27 Jun 2017 15:22:50 +0300 Subject: [PATCH] Bug 463737 - ClassDescriptor clone&synchronize ManagedTypeImpl.init For ManagedTypeImpl, if many threads are creating entity managers, ConcurrentModificationException related to members appears (the second thread reaches the if (null != this.members) and exits - it will access members that are in construction, leading to exceptions and invalid results. All the tenant client session will clone the classdescriptor from the client session (same client session). If there are multiple tenant client sessions started, the notifyReferencingDescriptorsOfIsolation is called for other entity manager, while referencedDescriptors are added from the initialize method. The tests were done with a multitenant-rich data model with 100 parallel threads doing create an EntityManager (with tenant id), start a transaction, commit the transaction. PS: all AccessController.doPrivileged lines are no-op, probably new line change --- .../persistence/descriptors/ClassDescriptor.java | 91 ++++++++++++---------- .../internal/jpa/metamodel/ManagedTypeImpl.java | 15 ++-- 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/descriptors/ClassDescriptor.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/descriptors/ClassDescriptor.java index 28d38fb..2b18468 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/descriptors/ClassDescriptor.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/descriptors/ClassDescriptor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 1998, 2015 Oracle and/or its affiliates, IBM Corporation. All rights reserved. + * Copyright (c) 1998, 2015 Oracle and/or its affiliates, IBM Corporation. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. @@ -32,6 +32,8 @@ * - 397772: JPA 2.1 Entity Graph Support * 06/25/2014-2.5.2 Rick Curtis * - 438177: Support M2M map with jointable + * 06/23/2017- 2.6 Valentin Iancu + * - 463737(ConcurrentModificationException part or 461873): Clone referencingClasses (from server session to client session) & use a method - addDescriptor ******************************************************************************/ package org.eclipse.persistence.descriptors; @@ -580,7 +582,7 @@ public class ClassDescriptor extends CoreDescriptor(referencingClasses); Vector mappingsVector = NonSynchronizedVector.newInstance(); @@ -1394,7 +1396,7 @@ public class ClassDescriptor extends CoreDescriptor * * When the shouldAlwaysRefreshCache argument passed into this method is false, this method - * ensures that a ClassDescriptor is not configured to always refresh the cache if data is received from the database by any query. + * ensures that a ClassDescriptor is not configured to always refresh the cache if data is received from the database by any query. * * @see #alwaysRefreshCache * @see #dontAlwaysRefreshCache @@ -6337,7 +6339,7 @@ public class ClassDescriptor extends CoreDescriptor getAttributeGroups() { return super.getAttributeGroups(); } + private void addDescriptor(ClassDescriptor cd) { + referencingClasses.add(cd); + } } diff --git a/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel/ManagedTypeImpl.java b/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel/ManagedTypeImpl.java index d36d01f..7d69f48 100644 --- a/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel/ManagedTypeImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/org/eclipse/persistence/internal/jpa/metamodel/ManagedTypeImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011, 2015 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. @@ -49,6 +49,9 @@ * - do not attempt a reflective call on a superclass * - see design issue #25 * http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_25:_20090616:_Inherited_parameterized_generics_for_Element_Collections_.28Basic.29 + * 06/22/2017- 2.6 Valentin Iancu - 463737 (while testing Concurrent Exception Initializer) + * Ensure no race condition occurs if two threads are calling initialize - one will enter initialize and put in this.members, while the other + * will request from the members various attributes * ******************************************************************************/ package org.eclipse.persistence.internal.jpa.metamodel; @@ -1111,7 +1114,7 @@ public abstract class ManagedTypeImpl extends TypeImpl implements ManagedT * Initialization should occur after all types in the metamodel have been created already. * */ - protected void initialize() { // Future: Check all is*Policy() calls + protected synchronized void initialize() { // Future: Check all is*Policy() calls /* * Design Issue 37 and 58: * http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/metamodel_api#DI_37:_20090708:_CollectionAttribute_acts_as_a_peer_of_Map.2C_Set.2C_List_but_should_be_a_super_interface @@ -1232,7 +1235,7 @@ public abstract class ManagedTypeImpl extends TypeImpl implements ManagedT Field field = null; if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ try { - field = AccessController.doPrivileged(new PrivilegedGetDeclaredField( + field = AccessController.doPrivileged(new PrivilegedGetDeclaredField( this.getJavaType(), colMapping.getAttributeName(), false)); } catch (PrivilegedActionException exception) { member = initializePluralAttributeTypeNotFound(this, colMapping, true); @@ -1266,7 +1269,7 @@ public abstract class ManagedTypeImpl extends TypeImpl implements ManagedT try { Method aMethod = null; if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { - aMethod = AccessController.doPrivileged(new PrivilegedGetDeclaredMethod( + aMethod = AccessController.doPrivileged(new PrivilegedGetDeclaredMethod( this.getJavaType(), getMethodName, null)); } else { aMethod = PrivilegedAccessHelper.getDeclaredMethod( @@ -1359,7 +1362,7 @@ public abstract class ManagedTypeImpl extends TypeImpl implements ManagedT // Check declaredFields in the case where we have no getMethod or getMethodName try { if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ - aField = AccessController.doPrivileged(new PrivilegedGetDeclaredField( + aField = AccessController.doPrivileged(new PrivilegedGetDeclaredField( this.getJavaType(), mapping.getAttributeName(), false)); } else { aField = PrivilegedAccessHelper.getDeclaredField( @@ -1381,7 +1384,7 @@ public abstract class ManagedTypeImpl extends TypeImpl implements ManagedT Method aMethod = null; try { if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { - aMethod = AccessController.doPrivileged(new PrivilegedGetDeclaredMethod( + aMethod = AccessController.doPrivileged(new PrivilegedGetDeclaredMethod( this.getJavaType(), getMethodName, null)); } else { aMethod = PrivilegedAccessHelper.getDeclaredMethod( -- 2.10.1.windows.1