diff --git a/src/main/java/io/ebean/config/ModuleInfo.java b/src/main/java/io/ebean/config/ModuleInfo.java
new file mode 100644
index 000000000..b4e6b95a3
--- /dev/null
+++ b/src/main/java/io/ebean/config/ModuleInfo.java
@@ -0,0 +1,21 @@
+package io.ebean.config;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used by generated code to hold entity classes to register with Ebean.
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ModuleInfo {
+
+ /**
+ * Returns the entity classes with db name prefix.
+ *
+ * The db name prefix is added to entity classes for non default databases.
+ */
+ String[] entities() default {};
+}
diff --git a/src/main/java/io/ebean/config/ModuleInfoLoader.java b/src/main/java/io/ebean/config/ModuleInfoLoader.java
new file mode 100644
index 000000000..d32f80630
--- /dev/null
+++ b/src/main/java/io/ebean/config/ModuleInfoLoader.java
@@ -0,0 +1,19 @@
+package io.ebean.config;
+
+import java.util.List;
+
+/**
+ * Loads and returns entity classes to register with Ebean databases.
+ */
+public interface ModuleInfoLoader {
+
+ /**
+ * Return the entity classes to register with the default DB.
+ */
+ List> entityClasses();
+
+ /**
+ * Return entity classes to register for a named DB (not default DB).
+ */
+ List> entityClassesFor(String dbName);
+}
diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java
index 625c6e3dd..53127965d 100644
--- a/src/main/java/io/ebean/config/ServerConfig.java
+++ b/src/main/java/io/ebean/config/ServerConfig.java
@@ -132,6 +132,15 @@ public class ServerConfig {
private TenantCatalogProvider tenantCatalogProvider;
+ /**
+ * When true will load entity classes via ModuleInfoLoader.
+ *
+ * NB: ModuleInfoLoader implementations are generated by querybean generator.
+ * Having this on and registering entity classes means we don't need to manually
+ * write that code or use classpath scanning to find entity classes.
+ */
+ private boolean loadModuleInfo;
+
/**
* List of interesting classes such as entities, embedded, ScalarTypes,
* Listeners, Finders, Controllers etc.
@@ -2276,12 +2285,18 @@ public class ServerConfig {
* server.
*/
public void addClass(Class> cls) {
- if (classes == null) {
- classes = new ArrayList<>();
- }
classes.add(cls);
}
+ /**
+ * Register all the classes (typically entity classes).
+ */
+ public void addAll(List> classList) {
+ if (classList != null && !classList.isEmpty()) {
+ classes.addAll(classList);
+ }
+ }
+
/**
* Add a package to search for entities via class path search.
*
@@ -2289,9 +2304,6 @@ public class ServerConfig {
*
*/
public void addPackage(String packageName) {
- if (packages == null) {
- packages = new ArrayList<>();
- }
packages.add(packageName);
}
@@ -2838,6 +2850,7 @@ public class ServerConfig {
}
loadDocStoreSettings(p);
+ loadModuleInfo = p.getBoolean("loadModuleInfo", loadModuleInfo);
maxCallStack = p.getInt("maxCallStack", maxCallStack);
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
@@ -2962,20 +2975,20 @@ public class ServerConfig {
return classes;
}
- List> classes = new ArrayList<>();
+ List> classList = new ArrayList<>();
String[] split = StringHelper.splitNames(classNames);
for (String cn : split) {
if (!"class".equalsIgnoreCase(cn)) {
try {
- classes.add(Class.forName(cn));
+ classList.add(Class.forName(cn));
} catch (ClassNotFoundException e) {
String msg = "Error registering class [" + cn + "] from [" + classNames + "]";
throw new RuntimeException(msg, e);
}
}
}
- return classes;
+ return classList;
}
private List getSearchList(String searchNames, List defaultValue) {
@@ -3259,6 +3272,17 @@ public class ServerConfig {
this.dumpMetricsOptions = dumpMetricsOptions;
}
+ /**
+ * Return true if entity classes should be loaded and registered via ModuleInfoLoader.
+ *
+ * When false we either register entity classes via application code or use classpath
+ * scanning to find and register entity classes.
+ *
+ */
+ public boolean isAutoLoadModuleInfo() {
+ return loadModuleInfo && classes.isEmpty();
+ }
+
public enum UuidVersion {
VERSION4,
VERSION1,
diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java b/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java
index 8be384042..a90f4869b 100644
--- a/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java
+++ b/src/main/java/io/ebeaninternal/server/core/DefaultContainer.java
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.core;
import io.ebean.config.ContainerConfig;
+import io.ebean.config.ModuleInfoLoader;
import io.ebean.config.ServerConfig;
import io.ebean.config.ServerConfigProvider;
import io.ebean.config.TenantMode;
@@ -85,11 +86,7 @@ public class DefaultContainer implements SpiContainer {
public SpiEbeanServer createServer(ServerConfig serverConfig) {
synchronized (this) {
- if (serverConfig.isDefaultServer()) {
- for (ServerConfigProvider configProvider : ServiceLoader.load(ServerConfigProvider.class)) {
- configProvider.apply(serverConfig);
- }
- }
+ applyConfigServices(serverConfig);
setNamingConvention(serverConfig);
BootupClasses bootupClasses = getBootupClasses(serverConfig);
@@ -120,7 +117,6 @@ public class DefaultContainer implements SpiContainer {
// executor and l2 caching service setup early (used during server construction)
SpiBackgroundExecutor executor = createBackgroundExecutor(serverConfig);
-
InternalConfiguration c = new InternalConfiguration(online, clusterManager, executor, serverConfig, bootupClasses);
DefaultServer server = new DefaultServer(c, c.cacheManager());
@@ -128,24 +124,45 @@ public class DefaultContainer implements SpiContainer {
// generate and run DDL if required
// if there are any other tasks requiring action in their plugins, do them as well
if (!DbOffline.isGenerateMigration()) {
- server.executePlugins(online);
-
- // initialise prior to registering with clusterManager
- server.initialise();
- if (online) {
- if (clusterManager.isClustering()) {
- // register the server once it has been created
- clusterManager.registerServer(server);
- }
- }
- // start any services after registering with clusterManager
- server.start();
+ startServer(online, server);
}
DbOffline.reset();
return server;
}
}
+ private void applyConfigServices(ServerConfig config) {
+ if (config.isDefaultServer()) {
+ for (ServerConfigProvider configProvider : ServiceLoader.load(ServerConfigProvider.class)) {
+ configProvider.apply(config);
+ }
+ if (config.isAutoLoadModuleInfo()) {
+ // auto register entity classes (default db)
+ for (ModuleInfoLoader loader : ServiceLoader.load(ModuleInfoLoader.class)) {
+ config.addAll(loader.entityClasses());
+ }
+ }
+ } else if (config.isAutoLoadModuleInfo()) {
+ // auto register entity classes (other named db)
+ for (ModuleInfoLoader loader : ServiceLoader.load(ModuleInfoLoader.class)) {
+ config.addAll(loader.entityClassesFor(config.getName()));
+ }
+ }
+ }
+
+ private void startServer(boolean online, DefaultServer server) {
+ server.executePlugins(online);
+ // initialise prior to registering with clusterManager
+ server.initialise();
+ if (online) {
+ if (clusterManager.isClustering()) {
+ clusterManager.registerServer(server);
+ }
+ }
+ // start any services after registering with clusterManager
+ server.start();
+ }
+
/**
* Get the entities, scalarTypes, Listeners etc combining the class registered
* ones with the already created instances.
diff --git a/src/test/java/io/ebean/config/ServerConfigTest.java b/src/test/java/io/ebean/config/ServerConfigTest.java
index 2279a864e..c9c20d6d5 100644
--- a/src/test/java/io/ebean/config/ServerConfigTest.java
+++ b/src/test/java/io/ebean/config/ServerConfigTest.java
@@ -71,6 +71,7 @@ public class ServerConfigTest {
props.setProperty("idGeneratorAutomatic", "true");
props.setProperty("enabledL2Regions", "r0,users,orgs");
props.setProperty("caseSensitiveCollation", "false");
+ props.setProperty("loadModuleInfo", "true");
serverConfig.loadFromProperties(props);
@@ -79,6 +80,8 @@ public class ServerConfigTest {
assertTrue(serverConfig.isNotifyL2CacheInForeground());
assertTrue(serverConfig.isDbOffline());
assertTrue(serverConfig.isAutoReadOnlyDataSource());
+ assertTrue(serverConfig.isAutoLoadModuleInfo());
+
assertTrue(serverConfig.isIdGeneratorAutomatic());
assertFalse(serverConfig.getPlatformConfig().isCaseSensitiveCollation());
@@ -134,6 +137,7 @@ public class ServerConfigTest {
assertEquals(JsonConfig.DateTime.ISO8601, serverConfig.getJsonDateTime());
assertEquals(JsonConfig.Date.ISO8601, serverConfig.getJsonDate());
assertTrue(serverConfig.getPlatformConfig().isCaseSensitiveCollation());
+ assertFalse(serverConfig.isAutoLoadModuleInfo());
}
@Test