diff --git a/ebean-api/src/main/java/io/ebean/DatabaseFactory.java b/ebean-api/src/main/java/io/ebean/DatabaseFactory.java
index b9b6a994f..13a0c4125 100644
--- a/ebean-api/src/main/java/io/ebean/DatabaseFactory.java
+++ b/ebean-api/src/main/java/io/ebean/DatabaseFactory.java
@@ -9,6 +9,7 @@ import javax.persistence.PersistenceException;
import java.util.Iterator;
import java.util.Properties;
import java.util.ServiceLoader;
+import java.util.concurrent.locks.ReentrantLock;
/**
* Creates Database instances.
@@ -30,6 +31,7 @@ import java.util.ServiceLoader;
*/
public class DatabaseFactory {
+ private static final ReentrantLock lock = new ReentrantLock(false);
private static SpiContainer container;
static {
@@ -42,44 +44,63 @@ public class DatabaseFactory {
* Call this prior to creating any Database instances or alternatively set the
* ContainerConfig on the DatabaseConfig when creating the first Database instance.
*/
- public static synchronized void initialiseContainer(ContainerConfig containerConfig) {
- getContainer(containerConfig);
+ public static void initialiseContainer(ContainerConfig containerConfig) {
+ lock.lock();
+ try {
+ getContainer(containerConfig);
+ } finally {
+ lock.unlock();
+ }
}
/**
* Create using properties to configure the database.
*/
- public static synchronized Database create(String name) {
- // construct based on loading properties files
- return getContainer(null).createServer(name);
+ public static Database create(String name) {
+ lock.lock();
+ try {
+ return getContainer(null).createServer(name);
+ } finally {
+ lock.unlock();
+ }
}
/**
* Create using the DatabaseConfig object to configure the database.
*/
- public static synchronized Database create(DatabaseConfig config) {
- if (config.getName() == null) {
- throw new PersistenceException("The name is null (it is required)");
+ public static Database create(DatabaseConfig config) {
+ lock.lock();
+ try {
+ if (config.getName() == null) {
+ throw new PersistenceException("The name is null (it is required)");
+ }
+ Database server = createInternal(config);
+ if (config.isRegister()) {
+ DbPrimary.setSkip(true);
+ DbContext.getInstance().register(server, config.isDefaultServer());
+ }
+ return server;
+ } finally {
+ lock.unlock();
}
- Database server = createInternal(config);
- if (config.isRegister()) {
- DbPrimary.setSkip(true);
- DbContext.getInstance().register(server, config.isDefaultServer());
- }
- return server;
}
/**
* Create using the DatabaseConfig additionally specifying a classLoader to use as the context class loader.
*/
- public static synchronized Database createWithContextClassLoader(DatabaseConfig config, ClassLoader classLoader) {
- ClassLoader currentContextLoader = Thread.currentThread().getContextClassLoader();
- Thread.currentThread().setContextClassLoader(classLoader);
+ public static Database createWithContextClassLoader(DatabaseConfig config, ClassLoader classLoader) {
+ lock.lock();
try {
- return DatabaseFactory.create(config);
+ ClassLoader currentContextLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(classLoader);
+ try {
+ return DatabaseFactory.create(config);
+ } finally {
+ // set the currentContextLoader back
+ Thread.currentThread().setContextClassLoader(currentContextLoader);
+ }
} finally {
- // set the currentContextLoader back
- Thread.currentThread().setContextClassLoader(currentContextLoader);
+ lock.unlock();
}
}
@@ -89,8 +110,13 @@ public class DatabaseFactory {
* This is typically invoked via JVM shutdown hook and not explicitly called.
*
*/
- public static synchronized void shutdown() {
- container.shutdown();
+ public static void shutdown() {
+ lock.lock();
+ try {
+ container.shutdown();
+ } finally {
+ lock.unlock();
+ }
}
private static Database createInternal(DatabaseConfig config) {
@@ -103,8 +129,7 @@ public class DatabaseFactory {
* @param containerConfig the configuration controlling clustering communication
*/
private static SpiContainer getContainer(ContainerConfig containerConfig) {
-
- // thread safe in that all calling methods are synchronized
+ // thread safe in that all calling methods hold lock
if (container != null) {
return container;
}
@@ -123,7 +148,6 @@ public class DatabaseFactory {
* Create the container instance using the configuration.
*/
protected static SpiContainer createContainer(ContainerConfig containerConfig) {
-
Iterator factories = ServiceLoader.load(SpiContainerFactory.class).iterator();
if (factories.hasNext()) {
return factories.next().create(containerConfig);
diff --git a/ebean-api/src/main/java/io/ebean/DbPrimary.java b/ebean-api/src/main/java/io/ebean/DbPrimary.java
index 2bad03025..9cbdc2eba 100644
--- a/ebean-api/src/main/java/io/ebean/DbPrimary.java
+++ b/ebean-api/src/main/java/io/ebean/DbPrimary.java
@@ -3,6 +3,7 @@ package io.ebean;
import io.avaje.config.Config;
import java.util.Properties;
+import java.util.concurrent.locks.ReentrantLock;
/**
* Provides singleton state for the default database.
@@ -11,47 +12,66 @@ import java.util.Properties;
*/
class DbPrimary {
+ private static final ReentrantLock lock = new ReentrantLock(false);
private static String defaultServerName;
-
private static boolean skip;
/**
* Set whether to skip automatically creating the primary database.
*/
- static synchronized void setSkip(boolean skip) {
- DbPrimary.skip = skip;
+ static void setSkip(boolean skip) {
+ lock.lock();
+ try {
+ DbPrimary.skip = skip;
+ } finally {
+ lock.unlock();
+ }
}
/**
* Return true to skip automatically creating the primary database.
*/
- static synchronized boolean isSkip() {
- return skip;
+ static boolean isSkip() {
+ lock.lock();
+ try {
+ return skip;
+ } finally {
+ lock.unlock();
+ }
}
/**
* Return the default database name.
*/
- static synchronized String getDefaultServerName() {
- getProperties();
- return defaultServerName;
+ static String getDefaultServerName() {
+ lock.lock();
+ try {
+ getProperties();
+ return defaultServerName;
+ } finally {
+ lock.unlock();
+ }
}
/**
* Return the default configuration Properties.
*/
- static synchronized Properties getProperties() {
- if (defaultServerName == null) {
- defaultServerName = determineDefaultServerName();
+ static Properties getProperties() {
+ lock.lock();
+ try {
+ if (defaultServerName == null) {
+ defaultServerName = determineDefaultServerName();
+ }
+ return Config.asProperties();
+ } finally {
+ lock.unlock();
}
- return Config.asProperties();
}
/**
* Determine and return the default server name checking system environment variables and then global properties.
*/
private static String determineDefaultServerName() {
-
String defaultServerName = System.getenv("EBEAN_DB");
defaultServerName = System.getProperty("db", defaultServerName);
defaultServerName = System.getProperty("ebean_db", defaultServerName);
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java
index fba258d02..9b3204f48 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayList.java
@@ -18,6 +18,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.locks.ReentrantLock;
import static java.util.Collections.EMPTY_LIST;
@@ -33,6 +34,7 @@ public class ScalarTypeArrayList extends ScalarTypeArrayBase implements Sc
static class Factory implements PlatformArrayTypeFactory {
+ private final ReentrantLock lock = new ReentrantLock(false);
private final Map cache = new HashMap<>();
/**
@@ -40,7 +42,8 @@ public class ScalarTypeArrayList extends ScalarTypeArrayBase implements Sc
*/
@Override
public ScalarTypeArrayList typeFor(Type valueType, boolean nullable) {
- synchronized (this) {
+ lock.lock();
+ try {
String key = valueType + ":" + nullable;
if (valueType.equals(UUID.class)) {
return cache.computeIfAbsent(key, s -> new ScalarTypeArrayList(nullable, "uuid", DocPropertyType.UUID, ArrayElementConverter.UUID));
@@ -58,6 +61,8 @@ public class ScalarTypeArrayList extends ScalarTypeArrayBase implements Sc
return cache.computeIfAbsent(key, s -> new ScalarTypeArrayList(nullable, "varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING));
}
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping");
+ } finally {
+ lock.unlock();
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayListH2.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayListH2.java
index 22d6e932d..192da6742 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayListH2.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArrayListH2.java
@@ -9,6 +9,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.locks.ReentrantLock;
import static java.util.Collections.EMPTY_LIST;
@@ -24,6 +25,7 @@ class ScalarTypeArrayListH2 extends ScalarTypeArrayList {
static class Factory implements PlatformArrayTypeFactory {
+ private final ReentrantLock lock = new ReentrantLock(false);
private final Map cache = new HashMap<>();
/**
@@ -31,7 +33,8 @@ class ScalarTypeArrayListH2 extends ScalarTypeArrayList {
*/
@Override
public ScalarType> typeFor(Type valueType, boolean nullable) {
- synchronized (this) {
+ lock.lock();
+ try {
String key = valueType + ":" + nullable;
if (valueType.equals(UUID.class)) {
return cache.computeIfAbsent(key, s -> new ScalarTypeArrayListH2(nullable, "uuid", DocPropertyType.UUID, ArrayElementConverter.UUID));
@@ -49,6 +52,8 @@ class ScalarTypeArrayListH2 extends ScalarTypeArrayList {
return cache.computeIfAbsent(key, s -> new ScalarTypeArrayListH2(nullable, "varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING));
}
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping");
+ } finally {
+ lock.unlock();
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java
index 10825e0b0..ba1f25805 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySet.java
@@ -18,6 +18,7 @@ import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.locks.ReentrantLock;
import static java.util.Collections.EMPTY_SET;
@@ -33,6 +34,7 @@ public class ScalarTypeArraySet extends ScalarTypeArrayBase implements Scal
static class Factory implements PlatformArrayTypeFactory {
+ private final ReentrantLock lock = new ReentrantLock(false);
private final Map cache = new HashMap<>();
/**
@@ -40,7 +42,8 @@ public class ScalarTypeArraySet extends ScalarTypeArrayBase implements Scal
*/
@Override
public ScalarType> typeFor(Type valueType, boolean nullable) {
- synchronized (this) {
+ lock.lock();
+ try {
String key = valueType + ":" + nullable;
if (valueType.equals(UUID.class)) {
return cache.computeIfAbsent(key, s -> new ScalarTypeArraySet(nullable, "uuid", DocPropertyType.UUID, ArrayElementConverter.UUID));
@@ -58,6 +61,8 @@ public class ScalarTypeArraySet extends ScalarTypeArrayBase implements Scal
return cache.computeIfAbsent(key, s -> new ScalarTypeArraySet(nullable, "varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING));
}
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping");
+ } finally {
+ lock.unlock();
}
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySetH2.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySetH2.java
index 1c6e39c6d..e893dcd6e 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySetH2.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeArraySetH2.java
@@ -9,6 +9,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.locks.ReentrantLock;
import static java.util.Collections.EMPTY_SET;
@@ -24,6 +25,7 @@ class ScalarTypeArraySetH2 extends ScalarTypeArraySet {
static class Factory implements PlatformArrayTypeFactory {
+ private final ReentrantLock lock = new ReentrantLock(false);
private final Map cache = new HashMap<>();
/**
@@ -31,7 +33,8 @@ class ScalarTypeArraySetH2 extends ScalarTypeArraySet {
*/
@Override
public ScalarType> typeFor(Type valueType, boolean nullable) {
- synchronized (this) {
+ lock.lock();
+ try {
String key = valueType + ":" + nullable;
if (valueType.equals(UUID.class)) {
return cache.computeIfAbsent(key, s -> new ScalarTypeArraySetH2(nullable, "uuid", DocPropertyType.UUID, ArrayElementConverter.UUID));
@@ -49,6 +52,8 @@ class ScalarTypeArraySetH2 extends ScalarTypeArraySet {
return cache.computeIfAbsent(key, s -> new ScalarTypeArraySetH2(nullable, "varchar", DocPropertyType.TEXT, ArrayElementConverter.STRING));
}
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping");
+ } finally {
+ lock.unlock();
}
}
diff --git a/ebean-core/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey.java b/ebean-core/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey.java
index 42bcd846d..3b2cfc5f5 100644
--- a/ebean-core/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey.java
+++ b/ebean-core/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey.java
@@ -30,7 +30,7 @@ public class TestCacheViaComplexNaturalKey extends BaseTestCase {
return server().getServerCacheManager();
}
- private static synchronized void insertSome() {
+ private static void insertSome() {
if (!loadOnce) {
Ebean.find(OCachedNatKeyBean.class).delete();
for (String store : Arrays.asList("abc", "def")) {
diff --git a/ebean-core/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java b/ebean-core/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java
index 463f73d88..236b2545e 100644
--- a/ebean-core/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java
+++ b/ebean-core/src/test/java/org/tests/model/basic/cache/TestCacheViaComplexNaturalKey3.java
@@ -28,7 +28,7 @@ public class TestCacheViaComplexNaturalKey3 extends BaseTestCase {
return server().getServerCacheManager();
}
- private static synchronized void insertSome() {
+ private static void insertSome() {
if (!loadOnce) {
Ebean.find(OCachedNatKeyBean3.class).delete();
diff --git a/ebean-test/src/main/java/io/ebean/test/config/RunOnceMarker.java b/ebean-test/src/main/java/io/ebean/test/config/RunOnceMarker.java
index a8eea6904..31f534c24 100644
--- a/ebean-test/src/main/java/io/ebean/test/config/RunOnceMarker.java
+++ b/ebean-test/src/main/java/io/ebean/test/config/RunOnceMarker.java
@@ -2,15 +2,23 @@ package io.ebean.test.config;
import io.ebeaninternal.api.DbOffline;
+import java.util.concurrent.locks.ReentrantLock;
+
class RunOnceMarker {
+ private static final ReentrantLock lock = new ReentrantLock(false);
private static boolean hasRun;
- static synchronized boolean isRun() {
- if (DbOffline.isSet() || hasRun) {
- return false;
+ static boolean isRun() {
+ lock.lock();
+ try {
+ if (DbOffline.isSet() || hasRun) {
+ return false;
+ }
+ hasRun = true;
+ return true;
+ } finally {
+ lock.unlock();
}
- hasRun = true;
- return true;
}
}