diff --git a/etc/jenkins/pr_verify.groovy b/etc/jenkins/pr_verify.groovy index eb76edd8921..2a7a15df6e3 100644 --- a/etc/jenkins/pr_verify.groovy +++ b/etc/jenkins/pr_verify.groovy @@ -1,5 +1,5 @@ // -// Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2021, 2024 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 v. 2.0 which is available at @@ -194,7 +194,7 @@ spec: container('el-build') { sh """ mvn -B -V clean install -pl :eclipselink -P staging - mvn -B -V verify -pl :org.eclipse.persistence.jpa.modelgen.processor,:org.eclipse.persistence.jpa.jse.test,:org.eclipse.persistence.extension,:org.eclipse.persistence.jpa.jpql,:org.eclipse.persistence.jpa.wdf.test,:org.eclipse.persistence.jpars,:org.eclipse.persistence.dbws,:org.eclipse.persistence.dbws.builder,:eclipselink,:org.eclipse.persistence.distribution.tests -P staging,mysql; + mvn -B -V verify -pl :org.eclipse.persistence.jpa.modelgen.processor,:org.eclipse.persistence.jpa.jse.test,:org.eclipse.persistence.jpa.helidon.test,:org.eclipse.persistence.extension,:org.eclipse.persistence.jpa.jpql,:org.eclipse.persistence.jpa.wdf.test,:org.eclipse.persistence.jpars,:org.eclipse.persistence.dbws,:org.eclipse.persistence.dbws.builder,:eclipselink,:org.eclipse.persistence.distribution.tests -P staging,mysql; """ } } diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectList.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectList.java index 74b8cf59246..bcfadd3ca08 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectList.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024 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 v. 2.0 which is available at @@ -24,6 +24,8 @@ import java.util.ListIterator; import java.util.Spliterator; import java.util.Vector; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator; @@ -98,6 +100,8 @@ public class IndirectList extends Vector implements CollectionChangeTracke */ private boolean useLazyInstantiation = true; + private final Lock instanceLock = new ReentrantLock(); + /** * PUBLIC: * Construct an empty IndirectList so that its internal data array @@ -350,13 +354,18 @@ public void clearDeferredChanges(){ before merging collections (again, "un-instantiated" collections are not merged). */ @Override - public synchronized Object clone() { - IndirectList result = (IndirectList)super.clone(); - result.delegate = (Vector)this.getDelegate().clone(); - result.valueHolder = new ValueHolder<>(result.delegate); - result.attributeName = null; - result.changeListener = null; - return result; + public Object clone() { + instanceLock.lock(); + try { + IndirectList result = (IndirectList)super.clone(); + result.delegate = (Vector)this.getDelegate().clone(); + result.valueHolder = new ValueHolder<>(result.delegate); + result.attributeName = null; + result.changeListener = null; + return result; + } finally { + instanceLock.unlock(); + } } /** @@ -391,8 +400,13 @@ public boolean containsAll(Collection c) { * @see java.util.Vector#copyInto(java.lang.Object[]) */ @Override - public synchronized void copyInto(Object[] anArray) { - getDelegate().copyInto(anArray); + public void copyInto(Object[] anArray) { + instanceLock.lock(); + try { + getDelegate().copyInto(anArray); + } finally { + instanceLock.unlock(); + } } /** @@ -452,11 +466,14 @@ public E get(int index) { protected Vector getDelegate() { Vector v = this.delegate; if (v == null) { - synchronized(this){ + instanceLock.lock(); + try { v = this.delegate; if (v == null) { this.delegate = v = this.buildDelegate(); } + } finally { + instanceLock.unlock(); } } return v; @@ -482,11 +499,14 @@ public ValueHolderInterface> getValueHolder() { ValueHolderInterface> vh = this.valueHolder; // PERF: lazy initialize value holder and vector as are normally set after creation. if (vh == null) { - synchronized(this) { + instanceLock.lock(); + try { vh = this.valueHolder; if (vh == null) { this.valueHolder = vh = new ValueHolder<>(new Vector<>(this.initialCapacity, this.capacityIncrement)); } + } finally { + instanceLock.unlock(); } } return vh; @@ -877,33 +897,43 @@ public Spliterator spliterator() { } @Override - public synchronized void replaceAll(UnaryOperator operator) { - // Must trigger remove/add events if tracked or uow. - if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { - List del = getDelegate(); - for (int i = 0; i < del.size(); i++) { - set(i, operator.apply(del.get(i))); + public void replaceAll(UnaryOperator operator) { + instanceLock.lock(); + try { + // Must trigger remove/add events if tracked or uow. + if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { + List del = getDelegate(); + for (int i = 0; i < del.size(); i++) { + set(i, operator.apply(del.get(i))); + } + } else { + getDelegate().replaceAll(operator); } - } else { - getDelegate().replaceAll(operator); + } finally { + instanceLock.unlock(); } } @Override - public synchronized boolean removeIf(Predicate filter) { - // Must trigger remove events if tracked or uow. - if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { - boolean hasChanged = false; - Iterator objects = iterator(); - while (objects.hasNext()) { - if (filter.test(objects.next())) { - objects.remove(); - hasChanged |= true; + public boolean removeIf(Predicate filter) { + instanceLock.lock(); + try { + // Must trigger remove events if tracked or uow. + if (hasBeenRegistered() || hasTrackedPropertyChangeListener()) { + boolean hasChanged = false; + Iterator objects = iterator(); + while (objects.hasNext()) { + if (filter.test(objects.next())) { + objects.remove(); + hasChanged |= true; + } } + return hasChanged; } - return hasChanged; + return getDelegate().removeIf(filter); + } finally { + instanceLock.unlock(); } - return getDelegate().removeIf(filter); } @Override diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectMap.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectMap.java index f44bbf83111..f5bb5845594 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectMap.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024 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 v. 2.0 which is available at @@ -23,6 +23,8 @@ import java.util.Objects; import java.util.Set; import java.util.Spliterator; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -74,6 +76,8 @@ public class IndirectMap extends Hashtable implements CollectionChan /** Store load factor for lazy init. */ protected float loadFactor = 0.75f; + private final Lock instanceLock = new ReentrantLock(); + /** * PUBLIC: * Construct a new, empty IndirectMap with a default @@ -137,15 +141,20 @@ protected Hashtable buildDelegate() { * @see java.util.Hashtable#clear() */ @Override - public synchronized void clear() { - if (hasTrackedPropertyChangeListener()) { - Iterator objects = this.keySet().iterator(); - while (objects.hasNext()) { - K o = objects.next(); - objects.remove(); + public void clear() { + instanceLock.lock(); + try { + if (hasTrackedPropertyChangeListener()) { + Iterator objects = this.keySet().iterator(); + while (objects.hasNext()) { + K o = objects.next(); + objects.remove(); + } + } else { + this.getDelegate().clear(); } - } else { - this.getDelegate().clear(); + } finally { + instanceLock.unlock(); } } @@ -176,29 +185,44 @@ public void clearDeferredChanges(){ before merging collections (again, "un-instantiated" collections are not merged). */ @Override - public synchronized Object clone() { - IndirectMap result = (IndirectMap)super.clone(); - result.delegate = (Hashtable)this.getDelegate().clone(); - result.valueHolder = new ValueHolder<>(result.delegate); - result.attributeName = null; - result.changeListener = null; - return result; + public Object clone() { + instanceLock.lock(); + try { + IndirectMap result = (IndirectMap)super.clone(); + result.delegate = (Hashtable)this.getDelegate().clone(); + result.valueHolder = new ValueHolder<>(result.delegate); + result.attributeName = null; + result.changeListener = null; + return result; + } finally { + instanceLock.unlock(); + } } /** * @see java.util.Hashtable#contains(java.lang.Object) */ @Override - public synchronized boolean contains(Object value) { - return this.getDelegate().contains(value); + public boolean contains(Object value) { + instanceLock.lock(); + try { + return this.getDelegate().contains(value); + } finally { + instanceLock.unlock(); + } } /** * @see java.util.Hashtable#containsKey(java.lang.Object) */ @Override - public synchronized boolean containsKey(Object key) { - return this.getDelegate().containsKey(key); + public boolean containsKey(Object key) { + instanceLock.lock(); + try { + return this.getDelegate().containsKey(key); + } finally { + instanceLock.unlock(); + } } /** @@ -213,8 +237,13 @@ public boolean containsValue(Object value) { * @see java.util.Hashtable#elements() */ @Override - public synchronized Enumeration elements() { - return this.getDelegate().elements(); + public Enumeration elements() { + instanceLock.lock(); + try { + return this.getDelegate().elements(); + } finally { + instanceLock.unlock(); + } } /** @@ -389,16 +418,26 @@ public Spliterator> spliterator() { * @see java.util.Hashtable#equals(java.lang.Object) */ @Override - public synchronized boolean equals(Object o) { - return this.getDelegate().equals(o); + public boolean equals(Object o) { + instanceLock.lock(); + try { + return this.getDelegate().equals(o); + } finally { + instanceLock.unlock(); + } } /** * @see java.util.Hashtable#get(java.lang.Object) */ @Override - public synchronized V get(Object key) { - return this.getDelegate().get(key); + public V get(Object key) { + instanceLock.lock(); + try { + return this.getDelegate().get(key); + } finally { + instanceLock.unlock(); + } } /** @@ -410,11 +449,14 @@ public synchronized V get(Object key) { protected Hashtable getDelegate() { Hashtable newDelegate = this.delegate; if (newDelegate == null) { - synchronized(this){ + instanceLock.lock(); + try { newDelegate = this.delegate; if (newDelegate == null) { this.delegate = newDelegate = this.buildDelegate(); } + } finally { + instanceLock.unlock(); } } return newDelegate; @@ -457,11 +499,14 @@ public ValueHolderInterface> getValueHolder() { ValueHolderInterface> vh = this.valueHolder; // PERF: lazy initialize value holder and vector as are normally set after creation. if (vh == null) { - synchronized(this){ + instanceLock.lock(); + try { vh = this.valueHolder; if (vh == null) { this.valueHolder = vh = new ValueHolder<>(new Hashtable<>(initialCapacity, loadFactor)); } + } finally { + instanceLock.unlock(); } } return vh; @@ -471,8 +516,13 @@ public ValueHolderInterface> getValueHolder() { * @see java.util.Hashtable#hashCode() */ @Override - public synchronized int hashCode() { - return this.getDelegate().hashCode(); + public int hashCode() { + instanceLock.lock(); + try { + return this.getDelegate().hashCode(); + } finally { + instanceLock.unlock(); + } } /** @@ -524,8 +574,13 @@ public boolean isInstantiated() { * @see java.util.Hashtable#keys() */ @Override - public synchronized Enumeration keys() { - return this.getDelegate().keys(); + public Enumeration keys() { + instanceLock.lock(); + try { + return this.getDelegate().keys(); + } finally { + instanceLock.unlock(); + } } /** @@ -693,13 +748,18 @@ public Spliterator spliterator() { * @see java.util.Hashtable#put(java.lang.Object, java.lang.Object) */ @Override - public synchronized V put(K key, V value) { - V oldValue = this.getDelegate().put(key, value); - if (oldValue != null){ - raiseRemoveChangeEvent(key, oldValue); + public V put(K key, V value) { + instanceLock.lock(); + try { + V oldValue = this.getDelegate().put(key, value); + if (oldValue != null){ + raiseRemoveChangeEvent(key, oldValue); + } + raiseAddChangeEvent(key, value); + return oldValue; + } finally { + instanceLock.unlock(); } - raiseAddChangeEvent(key, value); - return oldValue; } @@ -707,172 +767,232 @@ public synchronized V put(K key, V value) { * @see java.util.Hashtable#putAll(java.util.Map) */ @Override - public synchronized void putAll(Map t) { - // Must trigger add events if tracked or uow. - if (hasTrackedPropertyChangeListener()) { - t.entrySet().stream().forEach((newEntry) -> { - this.put(newEntry.getKey(), newEntry.getValue()); - }); - }else{ - this.getDelegate().putAll(t); + public void putAll(Map t) { + instanceLock.lock(); + try { + // Must trigger add events if tracked or uow. + if (hasTrackedPropertyChangeListener()) { + t.entrySet().stream().forEach((newEntry) -> { + this.put(newEntry.getKey(), newEntry.getValue()); + }); + } else { + this.getDelegate().putAll(t); + } + } finally { + instanceLock.unlock(); } } @Override - public synchronized V compute(K key, BiFunction remappingFunction) { - // Must trigger add events if tracked or uow. - if (hasTrackedPropertyChangeListener()) { - V oldValue = get(key); - V newValue = remappingFunction.apply(key, oldValue); - if (oldValue != null ) { - if (newValue != null) { - put(key, newValue); - return newValue; - } - remove(key); - } else { - if (newValue != null) { - put(key, newValue); - return newValue; - } + public V compute(K key, BiFunction remappingFunction) { + instanceLock.lock(); + try { + // Must trigger add events if tracked or uow. + if (hasTrackedPropertyChangeListener()) { + V oldValue = get(key); + V newValue = remappingFunction.apply(key, oldValue); + if (oldValue != null ) { + if (newValue != null) { + put(key, newValue); + return newValue; + } + remove(key); + } else { + if (newValue != null) { + put(key, newValue); + return newValue; + } + } + return null; } - return null; + return getDelegate().compute(key, remappingFunction); + } finally { + instanceLock.unlock(); } - return getDelegate().compute(key, remappingFunction); } @Override - public synchronized V computeIfAbsent(K key, Function mappingFunction) { - // Must trigger add events if tracked or uow. - if (hasTrackedPropertyChangeListener()) { - V oldValue = get(key); - if (oldValue == null) { - V newValue = mappingFunction.apply(key); - if (newValue != null) { - put(key, newValue); + public V computeIfAbsent(K key, Function mappingFunction) { + instanceLock.lock(); + try { + // Must trigger add events if tracked or uow. + if (hasTrackedPropertyChangeListener()) { + V oldValue = get(key); + if (oldValue == null) { + V newValue = mappingFunction.apply(key); + if (newValue != null) { + put(key, newValue); + } + return newValue; } - return newValue; + return oldValue; } - return oldValue; + return getDelegate().computeIfAbsent(key, mappingFunction); + } finally { + instanceLock.unlock(); } - return getDelegate().computeIfAbsent(key, mappingFunction); } @Override - public synchronized V computeIfPresent(K key, BiFunction remappingFunction) { - // Must trigger add events if tracked or uow. - if (hasTrackedPropertyChangeListener()) { - if (get(key) != null) { - V oldValue = get(key); - V newValue = remappingFunction.apply(key, oldValue); - if (newValue != null) { - put(key, newValue); - return newValue; + public V computeIfPresent(K key, BiFunction remappingFunction) { + instanceLock.lock(); + try { + // Must trigger add events if tracked or uow. + if (hasTrackedPropertyChangeListener()) { + if (get(key) != null) { + V oldValue = get(key); + V newValue = remappingFunction.apply(key, oldValue); + if (newValue != null) { + put(key, newValue); + return newValue; + } + remove(key); } - remove(key); + return null; } - return null; + return getDelegate().computeIfPresent(key, remappingFunction); + } finally { + instanceLock.unlock(); } - return getDelegate().computeIfPresent(key, remappingFunction); } @Override - public synchronized void forEach(BiConsumer action) { - getDelegate().forEach(action); + public void forEach(BiConsumer action) { + instanceLock.lock(); + try { + getDelegate().forEach(action); + } finally { + instanceLock.unlock(); + } } @Override - public synchronized V getOrDefault(Object key, V defaultValue) { - return getDelegate().getOrDefault(key, defaultValue); + public V getOrDefault(Object key, V defaultValue) { + instanceLock.lock(); + try { + return getDelegate().getOrDefault(key, defaultValue); + } finally { + instanceLock.unlock(); + } } @Override - public synchronized V merge(K key, V value, BiFunction remappingFunction) { - // Must trigger add events if tracked or uow. - if (hasTrackedPropertyChangeListener()) { - V oldValue = get(key); - V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value); - if (newValue == null) { - remove(key); - } else { - put(key, newValue); + public V merge(K key, V value, BiFunction remappingFunction) { + instanceLock.lock(); + try { + // Must trigger add events if tracked or uow. + if (hasTrackedPropertyChangeListener()) { + V oldValue = get(key); + V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value); + if (newValue == null) { + remove(key); + } else { + put(key, newValue); + } + return newValue; } - return newValue; + return getDelegate().merge(key, value, remappingFunction); + } finally { + instanceLock.unlock(); } - return getDelegate().merge(key, value, remappingFunction); } @Override - public synchronized V putIfAbsent(K key, V value) { - // Must trigger add events if tracked or uow. - if (hasTrackedPropertyChangeListener()) { - V current = getDelegate().get(key); - if (current == null) { - V v = getDelegate().put(key, value); - raiseAddChangeEvent(key, value); - return v; + public V putIfAbsent(K key, V value) { + instanceLock.lock(); + try { + // Must trigger add events if tracked or uow. + if (hasTrackedPropertyChangeListener()) { + V current = getDelegate().get(key); + if (current == null) { + V v = getDelegate().put(key, value); + raiseAddChangeEvent(key, value); + return v; + } + return current; } - return current; + return getDelegate().putIfAbsent(key, value); + } finally { + instanceLock.unlock(); } - return getDelegate().putIfAbsent(key, value); } @Override - public synchronized boolean remove(Object key, Object value) { - // Must trigger add events if tracked or uow. - if (hasTrackedPropertyChangeListener()) { - Map del = getDelegate(); - if (del.containsKey(key) && Objects.equals(del.get(key), value)) { - del.remove(key); - raiseRemoveChangeEvent(key, value); - return true; + public boolean remove(Object key, Object value) { + instanceLock.lock(); + try { + // Must trigger add events if tracked or uow. + if (hasTrackedPropertyChangeListener()) { + Map del = getDelegate(); + if (del.containsKey(key) && Objects.equals(del.get(key), value)) { + del.remove(key); + raiseRemoveChangeEvent(key, value); + return true; + } + return false; } - return false; + return getDelegate().remove(key, value); + } finally { + instanceLock.unlock(); } - return getDelegate().remove(key, value); } @Override - public synchronized V replace(K key, V value) { - // Must trigger add events if tracked or uow. - if (hasTrackedPropertyChangeListener()) { - Map del = getDelegate(); - if (del.containsKey(key)) { - return put(key, value); + public V replace(K key, V value) { + instanceLock.lock(); + try { + // Must trigger add events if tracked or uow. + if (hasTrackedPropertyChangeListener()) { + Map del = getDelegate(); + if (del.containsKey(key)) { + return put(key, value); + } + return null; } - return null; + return getDelegate().replace(key, value); + } finally { + instanceLock.unlock(); } - return getDelegate().replace(key, value); } @Override - public synchronized boolean replace(K key, V oldValue, V newValue) { - // Must trigger add events if tracked or uow. - if (hasTrackedPropertyChangeListener()) { - Map del = getDelegate(); - if (del.containsKey(key) && Objects.equals(del.get(key), oldValue)) { - put(key, newValue); - return true; + public boolean replace(K key, V oldValue, V newValue) { + instanceLock.lock(); + try { + // Must trigger add events if tracked or uow. + if (hasTrackedPropertyChangeListener()) { + Map del = getDelegate(); + if (del.containsKey(key) && Objects.equals(del.get(key), oldValue)) { + put(key, newValue); + return true; + } + return false; } - return false; + return getDelegate().replace(key, oldValue, newValue); + } finally { + instanceLock.unlock(); } - return getDelegate().replace(key, oldValue, newValue); } @Override - public synchronized void replaceAll(BiFunction function) { - // Must trigger add events if tracked or uow. - if (hasTrackedPropertyChangeListener()) { - getDelegate().entrySet().stream().forEach((entry) -> { - K key = entry.getKey(); - V oldValue = entry.getValue(); - entry.setValue(function.apply(key, entry.getValue())); - raiseRemoveChangeEvent(key, oldValue); - raiseAddChangeEvent(key, entry.getValue()); - }); - return; + public void replaceAll(BiFunction function) { + instanceLock.lock(); + try { + // Must trigger add events if tracked or uow. + if (hasTrackedPropertyChangeListener()) { + getDelegate().entrySet().stream().forEach((entry) -> { + K key = entry.getKey(); + V oldValue = entry.getValue(); + entry.setValue(function.apply(key, entry.getValue())); + raiseRemoveChangeEvent(key, oldValue); + raiseAddChangeEvent(key, entry.getValue()); + }); + return; + } + getDelegate().replaceAll(function); + } finally { + instanceLock.unlock(); } - getDelegate().replaceAll(function); } /** @@ -907,12 +1027,17 @@ protected void raiseRemoveChangeEvent(Object key, Object value) { * @see java.util.Hashtable#remove(java.lang.Object) */ @Override - public synchronized V remove(Object key) { - V value = this.getDelegate().remove(key); - if (value != null){ - raiseRemoveChangeEvent(key, value); + public V remove(Object key) { + instanceLock.lock(); + try { + V value = this.getDelegate().remove(key); + if (value != null){ + raiseRemoveChangeEvent(key, value); + } + return value; + } finally { + instanceLock.unlock(); } - return value; } /** diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectSet.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectSet.java index e2d58e32c34..517f7d7fe19 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectSet.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/indirection/IndirectSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024 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 v. 2.0 which is available at @@ -23,6 +23,8 @@ import java.util.Iterator; import java.util.Set; import java.util.Spliterator; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; @@ -118,6 +120,8 @@ public class IndirectSet implements CollectionChangeTracker, Set, Indirect */ private boolean useLazyInstantiation = false; + private final Lock instanceLock = new ReentrantLock(); + /** * Construct an empty IndirectSet. */ @@ -407,11 +411,14 @@ public void forEach(Consumer action) { protected Set getDelegate() { Set newDelegate = this.delegate; if (newDelegate == null) { - synchronized(this){ + instanceLock.lock(); + try { newDelegate = this.delegate; if (newDelegate == null) { this.delegate = newDelegate = this.buildDelegate(); } + } finally { + instanceLock.unlock(); } } return newDelegate; @@ -437,11 +444,14 @@ public ValueHolderInterface> getValueHolder() { ValueHolderInterface> vh = this.valueHolder; // PERF: lazy initialize value holder and vector as are normally set after creation. if (vh == null) { - synchronized(this){ + instanceLock.lock(); + try { vh = this.valueHolder; if (vh == null) { this.valueHolder = vh = new ValueHolder<>(new HashSet<>(initialCapacity, loadFactor)); } + } finally { + instanceLock.unlock(); } } return vh; diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourceAccessor.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourceAccessor.java index c58a1020e03..1892a53ae28 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourceAccessor.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourceAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020 IBM Corporation. All rights reserved. * * This program and the accompanying materials are made available under the @@ -29,6 +29,9 @@ import org.eclipse.persistence.sessions.SessionProfiler; import org.eclipse.persistence.sessions.server.ConnectionPool; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + /** * INTERNAL: * DatasourceAccessor is an abstract implementation @@ -152,6 +155,8 @@ public abstract class DatasourceAccessor implements Accessor { protected ConnectionPool pool; + private final Lock instanceLock = new ReentrantLock(); + /** * Default Constructor. */ @@ -289,19 +294,24 @@ public void beginTransaction(AbstractSession session) throws DatabaseException { * Used for load balancing and external pooling. */ @Override - public synchronized void decrementCallCount() { - int count = this.callCount; - // Avoid decrementing count if already zero, (failure before increment). - if (count <= 0) { - return; - } - this.callCount--; - if (this.usesExternalConnectionPooling && (!this.isInTransaction) && (currentSession == null || !currentSession.isExclusiveConnectionRequired()) && (count == 1)) { - try { - closeConnection(); - } catch (DatabaseException ignore) { - // Don't allow for errors to be masked by disconnect. + public void decrementCallCount() { + instanceLock.lock(); + try { + int count = this.callCount; + // Avoid decrementing count if already zero, (failure before increment). + if (count <= 0) { + return; } + this.callCount--; + if (this.usesExternalConnectionPooling && (!this.isInTransaction) && (currentSession == null || !currentSession.isExclusiveConnectionRequired()) && (count == 1)) { + try { + closeConnection(); + } catch (DatabaseException ignore) { + // Don't allow for errors to be masked by disconnect. + } + } + } finally { + instanceLock.unlock(); } } @@ -309,34 +319,39 @@ public synchronized void decrementCallCount() { * Used for load balancing and external pooling. */ @Override - public synchronized void incrementCallCount(AbstractSession session) { - this.callCount++; + public void incrementCallCount(AbstractSession session) { + instanceLock.lock(); + try { + this.callCount++; - if (this.callCount == 1) { - // If the login is null, then this accessor has never been connected. - if (this.login == null) { - throw DatabaseException.databaseAccessorNotConnected(); - } + if (this.callCount == 1) { + // If the login is null, then this accessor has never been connected. + if (this.login == null) { + throw DatabaseException.databaseAccessorNotConnected(); + } - // If the connection is no longer connected, it may have timed out. - if (this.datasourceConnection != null) { - if (shouldCheckConnection && !isConnected()) { - if (this.isInTransaction) { - throw DatabaseException.databaseAccessorNotConnected(); - } else { - reconnect(session); + // If the connection is no longer connected, it may have timed out. + if (this.datasourceConnection != null) { + if (shouldCheckConnection && !isConnected()) { + if (this.isInTransaction) { + throw DatabaseException.databaseAccessorNotConnected(); + } else { + reconnect(session); + } } - } - } else { - // If ExternalConnectionPooling is used, the connection can be re-established. - if (this.usesExternalConnectionPooling) { - reconnect(session); - session.postAcquireConnection(this); - currentSession = session; } else { - throw DatabaseException.databaseAccessorNotConnected(); + // If ExternalConnectionPooling is used, the connection can be re-established. + if (this.usesExternalConnectionPooling) { + reconnect(session); + session.postAcquireConnection(this); + currentSession = session; + } else { + throw DatabaseException.databaseAccessorNotConnected(); + } } } + } finally { + instanceLock.unlock(); } } diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/indirection/DatabaseValueHolder.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/indirection/DatabaseValueHolder.java index d1be9e51960..3adc720234b 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/indirection/DatabaseValueHolder.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/indirection/DatabaseValueHolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024 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 v. 2.0 which is available at @@ -25,6 +25,9 @@ import org.eclipse.persistence.internal.sessions.AbstractSession; import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + /** * DatabaseValueHolder wraps a database-stored object and implements * behavior to access it. The object is read only once from database @@ -56,6 +59,8 @@ public abstract class DatabaseValueHolder implements WeavedAttributeValueHold */ protected boolean isCoordinatedWithProperty = false; + private final Lock instanceLock = new ReentrantLock(); + /** * Default constructor. */ @@ -96,7 +101,8 @@ public ValueHolderInterface getWrappedValueHolder() { public T getValue() { boolean instantiated = this.isInstantiated; if (!instantiated) { - synchronized (this) { + instanceLock.lock(); + try { instantiated = this.isInstantiated; if (!instantiated) { // The value must be set directly because the setValue can also cause instantiation under UOW. @@ -105,6 +111,8 @@ public T getValue() { postInstantiate(); resetFields(); } + } finally { + instanceLock.unlock(); } } return value; diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/indirection/UnitOfWorkValueHolder.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/indirection/UnitOfWorkValueHolder.java index 05b33874297..2b4f1de376d 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/indirection/UnitOfWorkValueHolder.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/indirection/UnitOfWorkValueHolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024 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 v. 2.0 which is available at @@ -25,6 +25,9 @@ import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; import org.eclipse.persistence.logging.SessionLog; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + /** * A UnitOfWorkValueHolder is put in a clone object. * It wraps the value holder in the original object to delay @@ -59,6 +62,8 @@ public abstract class UnitOfWorkValueHolder extends DatabaseValueHolder im protected String sourceAttributeName; protected ObjID wrappedValueHolderRemoteID; + private final Lock wrappedValueHolderLock = new ReentrantLock(); + protected UnitOfWorkValueHolder() { super(); } @@ -146,7 +151,8 @@ protected T instantiateImpl() { Object value; // Bug 3835202 - Ensure access to valueholders is thread safe. Several of the methods // called below are not threadsafe alone. - synchronized (this.wrappedValueHolder) { + wrappedValueHolderLock.lock(); + try { if (this.wrappedValueHolder instanceof DatabaseValueHolder) { DatabaseValueHolder wrapped = (DatabaseValueHolder)this.wrappedValueHolder; UnitOfWorkImpl unitOfWork = getUnitOfWork(); @@ -174,6 +180,8 @@ protected T instantiateImpl() { } } value = this.wrappedValueHolder.getValue(); + } finally { + wrappedValueHolderLock.unlock(); } return buildCloneFor(value); } diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/sessions/server/ConnectionPool.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/sessions/server/ConnectionPool.java index 7ccae74bf4b..7df3794f891 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/sessions/server/ConnectionPool.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/sessions/server/ConnectionPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024 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 v. 2.0 which is available at @@ -14,13 +14,22 @@ // Oracle - initial API and implementation from Oracle TopLink package org.eclipse.persistence.sessions.server; -import java.util.*; - -import org.eclipse.persistence.internal.databaseaccess.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.eclipse.persistence.exceptions.ConcurrencyException; +import org.eclipse.persistence.exceptions.DatabaseException; +import org.eclipse.persistence.exceptions.QueryException; +import org.eclipse.persistence.internal.databaseaccess.Accessor; +import org.eclipse.persistence.internal.helper.Helper; +import org.eclipse.persistence.internal.localization.ToStringLocalization; import org.eclipse.persistence.sessions.Login; -import org.eclipse.persistence.internal.helper.*; -import org.eclipse.persistence.exceptions.*; -import org.eclipse.persistence.internal.localization.*; import org.eclipse.persistence.logging.SessionLog; /** @@ -53,6 +62,8 @@ public class ConnectionPool { public static final int INITIAL_CONNECTIONS = 1; public static final int WAIT_TIMEOUT = 180000; // 3 minutes. + private final Lock instanceLock = new ReentrantLock(); + private final Condition instanceLockCondition = instanceLock.newCondition(); /** * PUBLIC: * A connection pool is used to specify how connection should be pooled in a server session. @@ -125,88 +136,93 @@ public Accessor failover() { * INTERNAL: * Wait until a connection is available and allocate the connection for the client. */ - public synchronized Accessor acquireConnection() throws ConcurrencyException { - // Check for dead database and fail-over. - if (this.isDead) { - return failover(); - } - // PERF: Using direct variable access to minimize concurrency bottleneck. - while (this.connectionsAvailable.isEmpty()) { - if ((this.connectionsUsed.size() + this.connectionsAvailable.size()) < this.maxNumberOfConnections) { - Accessor connection = null; - try { - connection = buildConnection(); - } catch (RuntimeException failed) { - if (!this.failoverConnectionPools.isEmpty()) { - this.isDead = true; - this.timeOfDeath = System.currentTimeMillis(); - this.owner.logThrowable(SessionLog.WARNING, SessionLog.SQL, failed); - return acquireConnection(); - } else { - throw failed; + public Accessor acquireConnection() throws ConcurrencyException { + instanceLock.lock(); + try { + // Check for dead database and fail-over. + if (this.isDead) { + return failover(); + } + // PERF: Using direct variable access to minimize concurrency bottleneck. + while (this.connectionsAvailable.isEmpty()) { + if ((this.connectionsUsed.size() + this.connectionsAvailable.size()) < this.maxNumberOfConnections) { + Accessor connection = null; + try { + connection = buildConnection(); + } catch (RuntimeException failed) { + if (!this.failoverConnectionPools.isEmpty()) { + this.isDead = true; + this.timeOfDeath = System.currentTimeMillis(); + this.owner.logThrowable(SessionLog.WARNING, SessionLog.SQL, failed); + return acquireConnection(); + } else { + throw failed; + } } + this.connectionsUsed.add(connection); + if (this.owner.isInProfile()) { + this.owner.updateProfile(MONITOR_HEADER + this.name, this.connectionsUsed.size()); + } + if (this.owner.shouldLog(SessionLog.FINEST, SessionLog.CONNECTION)) { + Object[] args = new Object[1]; + args[0] = this.name; + this.owner.log(SessionLog.FINEST, SessionLog.CONNECTION, "acquire_connection", args, connection); + } + return connection; } - this.connectionsUsed.add(connection); - if (this.owner.isInProfile()) { - this.owner.updateProfile(MONITOR_HEADER + this.name, this.connectionsUsed.size()); - } - if (this.owner.shouldLog(SessionLog.FINEST, SessionLog.CONNECTION)) { - Object[] args = new Object[1]; - args[0] = this.name; - this.owner.log(SessionLog.FINEST, SessionLog.CONNECTION, "acquire_connection", args, connection); + try { + instanceLockCondition.await(this.waitTimeout, TimeUnit.MILLISECONDS);// Notify is called when connections are released. + } catch (InterruptedException exception) { + throw ConcurrencyException.waitFailureOnClientSession(exception); } - return connection; } - try { - wait(this.waitTimeout);// Notify is called when connections are released. - } catch (InterruptedException exception) { - throw ConcurrencyException.waitFailureOnClientSession(exception); - } - } - int connectionSize = this.connectionsAvailable.size(); - // Always used the last connection to avoid shift list and to use "hot" connection. - Accessor connection = this.connectionsAvailable.remove(connectionSize-1); - if (this.checkConnections) { - // EclipseLink has encountered a problem with a connection where the database no longer responded - // We need to now ensure that the failure was specific to that connection or we need to empty - // the pool of dead connections in the case of a database failover. - while (connectionSize >= 0) { - if (this.owner.getLogin().isConnectionHealthValidatedOnError() && this.owner.getServerPlatform().wasFailureCommunicationBased(null, connection, this.owner)) { - try { - //connection failed connect test - connection.closeConnection(); - } catch (Exception ex){ - //ignore - } finally { - connection.releaseCustomizer(); - } - if (this.connectionsAvailable.isEmpty()) { - this.checkConnections = false; - //we have emptied out all connections so let's have the connection pool build more - return acquireConnection(); + int connectionSize = this.connectionsAvailable.size(); + // Always used the last connection to avoid shift list and to use "hot" connection. + Accessor connection = this.connectionsAvailable.remove(connectionSize-1); + if (this.checkConnections) { + // EclipseLink has encountered a problem with a connection where the database no longer responded + // We need to now ensure that the failure was specific to that connection or we need to empty + // the pool of dead connections in the case of a database failover. + while (connectionSize >= 0) { + if (this.owner.getLogin().isConnectionHealthValidatedOnError() && this.owner.getServerPlatform().wasFailureCommunicationBased(null, connection, this.owner)) { + try { + //connection failed connect test + connection.closeConnection(); + } catch (Exception ex){ + //ignore + } finally { + connection.releaseCustomizer(); + } + if (this.connectionsAvailable.isEmpty()) { + this.checkConnections = false; + //we have emptied out all connections so let's have the connection pool build more + return acquireConnection(); + } else { + //test next connection + --connectionSize; + connection = this.connectionsAvailable.remove(connectionSize-1); + } } else { - //test next connection - --connectionSize; - connection = this.connectionsAvailable.remove(connectionSize-1); + //connection was good use it. And make sure we stop testing connections + this.checkConnections = false; + break; } - } else { - //connection was good use it. And make sure we stop testing connections - this.checkConnections = false; - break; } } + this.connectionsUsed.add(connection); + if (this.owner.isInProfile()) { + this.owner.updateProfile(MONITOR_HEADER + this.name, this.connectionsUsed.size()); + } + if (this.owner.shouldLog(SessionLog.FINEST, SessionLog.CONNECTION)) { + Object[] args = new Object[1]; + args[0] = this.name; + this.owner.log(SessionLog.FINEST, SessionLog.CONNECTION, "acquire_connection", args, connection); + } + return connection; + } finally { + instanceLock.unlock(); } - this.connectionsUsed.add(connection); - if (this.owner.isInProfile()) { - this.owner.updateProfile(MONITOR_HEADER + this.name, this.connectionsUsed.size()); - } - if (this.owner.shouldLog(SessionLog.FINEST, SessionLog.CONNECTION)) { - Object[] args = new Object[1]; - args[0] = this.name; - this.owner.log(SessionLog.FINEST, SessionLog.CONNECTION, "acquire_connection", args, connection); - } - return connection; } /** @@ -323,34 +339,39 @@ public boolean isThereConflictBetweenLoginAndType() { * INTERNAL: * Add the connection as single that a new connection is available. */ - public synchronized void releaseConnection(Accessor connection) throws DatabaseException { - if (this.owner.shouldLog(SessionLog.FINEST, SessionLog.CONNECTION)) { - Object[] args = new Object[1]; - args[0] = this.name; - this.owner.log(SessionLog.FINEST, SessionLog.CONNECTION, "release_connection", args, connection); - } - connection.reset(); + public void releaseConnection(Accessor connection) throws DatabaseException { + instanceLock.lock(); + try { + if (this.owner.shouldLog(SessionLog.FINEST, SessionLog.CONNECTION)) { + Object[] args = new Object[1]; + args[0] = this.name; + this.owner.log(SessionLog.FINEST, SessionLog.CONNECTION, "release_connection", args, connection); + } + connection.reset(); - this.connectionsUsed.remove(connection); + this.connectionsUsed.remove(connection); - if (!connection.isValid()) { - this.checkConnections = true; - try { - connection.disconnect(this.owner); - } catch (DatabaseException ex) { - //this is an invalid connection so expect an exception. - } - } else { - if ((this.connectionsUsed.size() + this.connectionsAvailable.size()) < this.minNumberOfConnections) { - this.connectionsAvailable.add(connection); + if (!connection.isValid()) { + this.checkConnections = true; + try { + connection.disconnect(this.owner); + } catch (DatabaseException ex) { + //this is an invalid connection so expect an exception. + } } else { - connection.disconnect(getOwner()); + if ((this.connectionsUsed.size() + this.connectionsAvailable.size()) < this.minNumberOfConnections) { + this.connectionsAvailable.add(connection); + } else { + connection.disconnect(getOwner()); + } } + if (this.owner.isInProfile()) { + this.owner.updateProfile(MONITOR_HEADER + this.name, this.connectionsUsed.size()); + } + instanceLockCondition.signal(); + } finally { + instanceLock.unlock(); } - if (this.owner.isInProfile()) { - this.owner.updateProfile(MONITOR_HEADER + this.name, this.connectionsUsed.size()); - } - notify(); } /** @@ -471,40 +492,50 @@ protected void setOwner(ServerSession owner) { * INTERNAL: * Disconnect all connections. */ - public synchronized void shutDown() { - setIsConnected(false); + public void shutDown() { + instanceLock.lock(); + try { + setIsConnected(false); - for (Iterator iterator = getConnectionsAvailable().iterator(); iterator.hasNext();) { - try { - iterator.next().disconnect(getOwner()); - } catch (DatabaseException exception) { - // Ignore. + for (Iterator iterator = getConnectionsAvailable().iterator(); iterator.hasNext();) { + try { + iterator.next().disconnect(getOwner()); + } catch (DatabaseException exception) { + // Ignore. + } } - } - for (Iterator iterator = getConnectionsUsed().iterator(); iterator.hasNext();) { - try { - iterator.next().disconnect(getOwner()); - } catch (DatabaseException exception) { - // Ignore. + for (Iterator iterator = getConnectionsUsed().iterator(); iterator.hasNext();) { + try { + iterator.next().disconnect(getOwner()); + } catch (DatabaseException exception) { + // Ignore. + } } + resetConnections(); + } finally { + instanceLock.unlock(); } - resetConnections(); } /** * INTERNAL: * Allocate the minimum connections. */ - public synchronized void startUp() { - if (isConnected()) { - return; - } - for (int index = getInitialNumberOfConnections(); index > 0; index--) { - getConnectionsAvailable().add(buildConnection()); - } + public void startUp() { + instanceLock.lock(); + try { + if (isConnected()) { + return; + } + for (int index = getInitialNumberOfConnections(); index > 0; index--) { + getConnectionsAvailable().add(buildConnection()); + } - setIsConnected(true); + setIsConnected(true); + } finally { + instanceLock.unlock(); + } } /** diff --git a/jpa/eclipselink.jpa.helidon.test/pom.xml b/jpa/eclipselink.jpa.helidon.test/pom.xml new file mode 100644 index 00000000000..bafead38bd1 --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/pom.xml @@ -0,0 +1,244 @@ + + + + + 4.0.0 + + EclipseLink JPA Helidon Test + org.eclipse.persistence + org.eclipse.persistence.jpa.helidon.test + jar + + + org.eclipse.persistence + org.eclipse.persistence.parent + 4.0.4-SNAPSHOT + ../../pom.xml + + + + org.eclipse.persistence.testing.helidon.Main + + + ${db.platform} + ${db.user} + ${db.pwd} + ${db.driver} + ${db.url} + + ${logging.level} + + ${skipTests} + + + + + + + org.eclipse.persistence + org.eclipse.persistence.jpa + + + + jakarta.transaction + jakarta.transaction-api + + + + io.helidon.microprofile.bundles + helidon-microprofile + + + io.helidon.jersey + helidon-jersey-client + + + + junit + junit + test + + + + ${db.driver.groupId} + ${db.driver.artifactId} + ${db.driver.version} + test + + + + + + + + src/main/resources + true + + + + + ${integration.test.resources.directory} + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + true + + + + default-compile + + true + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + attach-javadocs + + jar + + + true + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + org.carlspring.maven + derby-maven-plugin + + + start-derby + pre-integration-test + + start + + + + stop-derby + post-integration-test + + stop + + + + + + + + + + helidon-tests + + (21, + + + + + org.apache.maven.plugins + maven-compiler-plugin + + false + + + + default-compile + + false + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + ${project.build.directory}/test-run + + ${test.properties.file} + + + + test-jpa-helidon + + integration-test + + + ${test-skip-jpa-helidon} + + test-jpa-helidon + + org.eclipse.persistence.testing.helidon.TestMaster + + + + + verify-integration-tests + + verify + + + ${integration.test.skip.verify} + + + + + + + + + oracle + + + + org.eclipse.persistence + org.eclipse.persistence.oracle + test + + + + + test-jpa-helidon + + false + + + + diff --git a/jpa/eclipselink.jpa.helidon.test/src/it/java/org/eclipse/persistence/testing/helidon/TestMaster.java b/jpa/eclipselink.jpa.helidon.test/src/it/java/org/eclipse/persistence/testing/helidon/TestMaster.java new file mode 100644 index 00000000000..7373ac5b489 --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/it/java/org/eclipse/persistence/testing/helidon/TestMaster.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +package org.eclipse.persistence.testing.helidon; + +import java.util.List; + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.core.Response; + +import org.eclipse.persistence.testing.helidon.models.MasterEntity; + +import io.helidon.microprofile.server.Server; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class TestMaster { + + private static Server server; + + @BeforeClass + public static void startServer() throws Exception { + server = Main.startServer(); + } + + @AfterClass + public static void stopServer() throws Exception { + if (server != null) { + server.stop(); + } + } + + @Test + public void testMasterOneRest() throws Exception { + Client client = ClientBuilder.newClient(); + MasterEntity masterEntity = client + .target("http://localhost:8080/master/one") + .request("application/json") + .get(MasterEntity.class); + assertEquals(1L, masterEntity.getId()); + assertEquals("Master 1", masterEntity.getName()); + } + + @Test + public void testMasterAllRest() throws Exception { + Client client = ClientBuilder.newClient(); + List masterEntities = client + .target("http://localhost:8080/master/all") + .request("application/json") + .get(List.class); + assertTrue(masterEntities.size() >= 2); + } + + @Test + public void testMasterCreateRemoveRest() throws Exception { + Client client = ClientBuilder.newClient(); + Response response = client + .target("http://localhost:8080/master/create_remove") + .request("application/json") + .get(); + } +} diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/Main.java b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/Main.java new file mode 100644 index 00000000000..24f2b31dff2 --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/Main.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +package org.eclipse.persistence.testing.helidon; + +import io.helidon.microprofile.server.Server; + +public class Main { + + public static void main(String[] args) { + startServer(); + } + + static Server startServer() { + return Server.builder().retainDiscoveredApplications(true). + build(). + start(); + } +} diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/dao/BaseDao.java b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/dao/BaseDao.java new file mode 100644 index 00000000000..89c62643c06 --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/dao/BaseDao.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2024 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +package org.eclipse.persistence.testing.helidon.dao; + +import java.util.List; + +import jakarta.enterprise.context.Dependent; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Persistence; + +import org.eclipse.persistence.testing.helidon.models.BaseEntity; + +@Dependent +public class BaseDao { + + private EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("HelidonPu"); + + public void create(T entity) { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + try { + entityManager.getTransaction().begin(); + entityManager.persist(entity); + entityManager.getTransaction().commit(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(); + } finally { + if (entityManager.getTransaction().isActive()) { + entityManager.getTransaction().rollback(); + } + entityManager.close(); + } + } + + public void remove(T entity) { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + try { + entityManager.getTransaction().begin(); + T entity2 = entityManager.merge(entity);; + entityManager.remove(entity2); + entityManager.getTransaction().commit(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(); + } finally { + if (entityManager.getTransaction().isActive()) { + entityManager.getTransaction().rollback(); + } + entityManager.close(); + } + } + + public T update(T entity) { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + T result = null; + try { + entityManager.getTransaction().begin(); + result = entityManager.merge(entity); + entityManager.getTransaction().commit(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(); + } finally { + if (entityManager.getTransaction().isActive()) { + entityManager.getTransaction().rollback(); + } + entityManager.close(); + } + return result; + } + + public T find(Class clazz, long id) { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + try { + return entityManager.find(clazz, id); + } finally { + if (entityManager.getTransaction().isActive()) { + entityManager.getTransaction().rollback(); + } + entityManager.close(); + } + } + + public List findByNamedQuery(String query) { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + List result = null; + try { + entityManager.getTransaction().begin(); + result = entityManager.createNamedQuery(query).getResultList(); + entityManager.getTransaction().commit(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(); + } finally { + if (entityManager.getTransaction().isActive()) { + entityManager.getTransaction().rollback(); + } + entityManager.close(); + } + return result; + } +} diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/dao/DetailDao.java b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/dao/DetailDao.java new file mode 100644 index 00000000000..cd9cc4d7090 --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/dao/DetailDao.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +package org.eclipse.persistence.testing.helidon.dao; + +import jakarta.enterprise.context.Dependent; + +import org.eclipse.persistence.testing.helidon.models.DetailEntity; + +@Dependent +public class DetailDao extends BaseDao { +} diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/dao/MasterDao.java b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/dao/MasterDao.java new file mode 100644 index 00000000000..2a700b0a6dd --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/dao/MasterDao.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +package org.eclipse.persistence.testing.helidon.dao; + +import jakarta.enterprise.context.Dependent; + +import org.eclipse.persistence.testing.helidon.models.MasterEntity; + +@Dependent +public class MasterDao extends BaseDao { +} diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/models/BaseEntity.java b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/models/BaseEntity.java new file mode 100644 index 00000000000..814e7fdb111 --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/models/BaseEntity.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +package org.eclipse.persistence.testing.helidon.models; + +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public class BaseEntity { + + @Id + private long id; + + public BaseEntity() { + } + + public BaseEntity(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + +} diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/models/DetailEntity.java b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/models/DetailEntity.java new file mode 100644 index 00000000000..a1d248a1338 --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/models/DetailEntity.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +package org.eclipse.persistence.testing.helidon.models; + +import jakarta.json.bind.annotation.JsonbTransient; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "HELIDON_TAB_DETAIL") +public class DetailEntity extends BaseEntity { + + private String name; + + @ManyToOne() + @JoinColumn(name = "MASTER_ID_FK") + @JsonbTransient + private MasterEntity master; + + public DetailEntity() { + } + + public DetailEntity(long id) { + super(id); + } + + public DetailEntity(long id, String name) { + super(id); + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public MasterEntity getMaster() { + return master; + } + + public void setMaster(MasterEntity master) { + this.master = master; + } + + @Override + public String toString() { + return "DetailEntity{" + + "id='" + getId() + '\'' + + "name='" + name + '\'' + + '}'; + } +} diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/models/MasterEntity.java b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/models/MasterEntity.java new file mode 100644 index 00000000000..ec4feb9f994 --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/models/MasterEntity.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +package org.eclipse.persistence.testing.helidon.models; + +import jakarta.persistence.Entity; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Entity +@Table(name = "HELIDON_TAB_MASTER") +@NamedQueries({ + @NamedQuery(name = "MasterEntity.findAll", query = "SELECT e FROM MasterEntity e"), +}) +public class MasterEntity extends BaseEntity { + + private String name; + + @OneToMany(mappedBy = "master") + private List details = new ArrayList<>(); + + public MasterEntity() { + } + + public MasterEntity(long id) { + super(id); + } + + public MasterEntity(long id, String name) { + super(id); + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getDetails() { + return details; + } + + public void setDetails(List details) { + this.details = details; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MasterEntity that = (MasterEntity) o; + return getId() == that.getId() && Objects.equals(name, that.name) && Objects.equals(details, that.details); + } + + @Override + public int hashCode() { + return Objects.hash(getId(), name, details); + } + + @Override + public String toString() { + return "MasterEntity{" + + "id='" + getId() + '\'' + + "name='" + name + '\'' + + ", details=" + details + + '}'; + } +} diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/provider/MasterProvider.java b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/provider/MasterProvider.java new file mode 100644 index 00000000000..a35e9bb4515 --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/provider/MasterProvider.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +package org.eclipse.persistence.testing.helidon.provider; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.eclipse.persistence.testing.helidon.dao.MasterDao; +import org.eclipse.persistence.testing.helidon.models.MasterEntity; + +@ApplicationScoped +public class MasterProvider { + + @Inject + private MasterDao masterDao; + + public MasterEntity getMasterOne() { + MasterEntity masterEntity = masterDao.find(MasterEntity.class, 1L); + return masterEntity; + } + + public List getMasterAll() { + List masterEntities = masterDao.findByNamedQuery("MasterEntity.findAll"); + return masterEntities; + } + + public void createRemove() { + MasterEntity masterEntity = new MasterEntity(3L, "Master 3"); + masterDao.create(masterEntity); + masterDao.remove(masterEntity); + } + +} diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/resource/MasterResource.java b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/resource/MasterResource.java new file mode 100644 index 00000000000..c1d8c479667 --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/java/org/eclipse/persistence/testing/helidon/resource/MasterResource.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 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 v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial API and implementation +package org.eclipse.persistence.testing.helidon.resource; + +import java.util.List; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.eclipse.persistence.testing.helidon.models.MasterEntity; +import org.eclipse.persistence.testing.helidon.provider.MasterProvider; + +@Path("/master") +public class MasterResource { + + @Inject + MasterProvider masterProvider; + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("one") + public MasterEntity masterOne() { + return masterProvider.getMasterOne(); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("all") + public List masterAll() { + return masterProvider.getMasterAll(); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("create_remove") + public void createRemove() { + masterProvider.createRemove();; + } + +} diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/beans.xml b/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/beans.xml new file mode 100644 index 00000000000..6110162ea7b --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/beans.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/microprofile-config.properties b/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 00000000000..1373c60774e --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,17 @@ +# +# Copyright (c) 2024 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 v. 2.0 which is available at +# http://www.eclipse.org/legal/epl-2.0, +# or the Eclipse Distribution License v. 1.0 which is available at +# http://www.eclipse.org/org/documents/edl-v10.php. +# +# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause +# +# Contributors: +# Oracle - initial API and implementation + +# Microprofile server properties +server.port=8080 +server.host=0.0.0.0 diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/persistence.xml b/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/persistence.xml new file mode 100644 index 00000000000..145e190a15b --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,32 @@ + + + + + org.eclipse.persistence.testing.helidon.models.MasterEntity + org.eclipse.persistence.testing.helidon.models.DetailEntity + + + + + + + + + + + + diff --git a/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/sql/init.sql b/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/sql/init.sql new file mode 100644 index 00000000000..577e5b28bb2 --- /dev/null +++ b/jpa/eclipselink.jpa.helidon.test/src/main/resources/META-INF/sql/init.sql @@ -0,0 +1,26 @@ +-- +-- Copyright (c) 2024 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 v. 2.0 which is available at +-- http://www.eclipse.org/legal/epl-2.0, +-- or the Eclipse Distribution License v. 1.0 which is available at +-- http://www.eclipse.org/org/documents/edl-v10.php. +-- +-- SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause +-- +-- Contributors: +-- Oracle - initial API and implementation + +DROP TABLE HELIDON_TAB_DETAIL +DROP TABLE HELIDON_TAB_MASTER +CREATE TABLE HELIDON_TAB_MASTER (ID INTEGER PRIMARY KEY, NAME VARCHAR(200)) +CREATE TABLE HELIDON_TAB_DETAIL (ID INTEGER PRIMARY KEY, MASTER_ID_FK INTEGER, NAME VARCHAR(200)) +ALTER TABLE HELIDON_TAB_DETAIL ADD CONSTRAINT TEST_TAB_FK_DETAIL FOREIGN KEY ( MASTER_ID_FK ) REFERENCES HELIDON_TAB_MASTER (ID) + +INSERT INTO HELIDON_TAB_MASTER (ID, NAME)VALUES (1, 'Master 1') +INSERT INTO HELIDON_TAB_MASTER (ID, NAME)VALUES (2, 'Master 2') +INSERT INTO HELIDON_TAB_DETAIL (ID, MASTER_ID_FK, NAME)VALUES (101, 1, 'Detail 101') +INSERT INTO HELIDON_TAB_DETAIL (ID, MASTER_ID_FK, NAME)VALUES (102, 1, 'Detail 102') +INSERT INTO HELIDON_TAB_DETAIL (ID, MASTER_ID_FK, NAME)VALUES (201, 2, 'Detail 201') +INSERT INTO HELIDON_TAB_DETAIL (ID, MASTER_ID_FK, NAME)VALUES (202, 2, 'Detail 202') diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerFactoryDelegate.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerFactoryDelegate.java index c6b70b5d10d..031b3710c41 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerFactoryDelegate.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/EntityManagerFactoryDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1998, 2021 IBM Corporation. All rights reserved. * * This program and the accompanying materials are made available under the @@ -36,6 +36,8 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import jakarta.persistence.Cache; import jakarta.persistence.EntityGraph; @@ -152,6 +154,8 @@ public class EntityManagerFactoryDelegate implements EntityManagerFactory, Persi /** Pointer to the EntityManagerFactoryImpl that created me */ protected JpaEntityManagerFactory owner = null; + private final Lock instanceLock = new ReentrantLock(); + /** * Will return an instance of the Factory. Should only be called by * EclipseLink. @@ -208,7 +212,8 @@ public DatabaseSessionImpl getDatabaseSession() { public AbstractSession getAbstractSession() { if (this.session == null) { // PERF: Avoid synchronization. - synchronized (this) { + instanceLock.lock(); + try { // DCL ok as isLoggedIn is volatile boolean, set after login is // complete. if (this.session == null) { @@ -230,6 +235,8 @@ public AbstractSession getAbstractSession() { processProperties(propertiesToProcess); this.session = tempSession; } + } finally { + instanceLock.unlock(); } } return this.session; @@ -274,18 +281,23 @@ public SessionBroker getSessionBroker() { * will return false. */ @Override - public synchronized void close() { - verifyOpen(); - isOpen = false; - // Do not invalidate the metaModel field - // (a reopened emf will re-populate the same metaModel) - // (a new persistence unit will generate a new metaModel) - if (setupImpl != null) { - // 260511 null check so that closing a EM - // created from the constructor no longer throws a NPE - setupImpl.undeploy(); + public void close() { + instanceLock.lock(); + try { + verifyOpen(); + isOpen = false; + // Do not invalidate the metaModel field + // (a reopened emf will re-populate the same metaModel) + // (a new persistence unit will generate a new metaModel) + if (setupImpl != null) { + // 260511 null check so that closing a EM + // created from the constructor no longer throws a NPE + setupImpl.undeploy(); + } + owner = null; + } finally { + instanceLock.unlock(); } - owner = null; } /** diff --git a/pom.xml b/pom.xml index 85bea2e0c89..dba0ad219f1 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ jpa/eclipselink.jpa.test.jse + + jpa/eclipselink.jpa.helidon.test jpa/eclipselink.jpa.spring.test