diff --git a/ebean-api/src/main/java/io/ebean/Database.java b/ebean-api/src/main/java/io/ebean/Database.java index 31064ee3c..509fd4764 100644 --- a/ebean-api/src/main/java/io/ebean/Database.java +++ b/ebean-api/src/main/java/io/ebean/Database.java @@ -1746,7 +1746,7 @@ public interface Database { * TenantMode that does not support DDL run on startup. *

* Note: runDdl is normally executed at startup automatically, if - * ebean.db.runDdl=true is set. Calling this method, will not check + * ebean.db.ddl.run=true is set. Calling this method, will not check * that flag. *

*/ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 392f509b2..9e3aadc86 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -387,6 +387,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { if (config.isRunMigration()) { runMigration(); } + } else if (config.isDdlRun() || config.isRunMigration()) { + log.warn("There was a request to run DDL or migration, but this is currently not possible, as the database is not available"); } startQueryPlanCapture(); } diff --git a/ebean-test/pom.xml b/ebean-test/pom.xml index 76b06fa60..f3ff709bb 100644 --- a/ebean-test/pom.xml +++ b/ebean-test/pom.xml @@ -187,8 +187,8 @@ com.oracle.database.jdbc - ojdbc8 19.12.0.0 diff --git a/ebean-test/src/test/java/io/ebean/config/TestServerOffline.java b/ebean-test/src/test/java/io/ebean/config/TestServerOffline.java new file mode 100644 index 000000000..bed9ab01e --- /dev/null +++ b/ebean-test/src/test/java/io/ebean/config/TestServerOffline.java @@ -0,0 +1,151 @@ +package io.ebean.config; + + +import io.ebean.Database; +import io.ebean.DatabaseFactory; +import io.ebean.annotation.ForPlatform; +import io.ebean.annotation.Platform; +import io.ebean.datasource.DataSourceAlert; +import io.ebean.datasource.DataSourceInitialiseException; + +import org.junit.jupiter.api.Test; +import org.tests.model.basic.EBasicVer; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; + +import javax.persistence.PersistenceException; +import javax.sql.DataSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class TestServerOffline { + + @Test + @ForPlatform({Platform.H2}) + public void testOffline_default() throws SQLException { + + String url = "jdbc:h2:mem:testoffline1"; + try (Connection bootup = DriverManager.getConnection(url, "sa", "secret")) { + Properties props = props(url); + DatabaseConfig config = config(props); + + assertThatThrownBy(() -> DatabaseFactory.create(config)) + .isInstanceOf(DataSourceInitialiseException.class); + } + + } + + private static class LazyDatasourceInitializer implements DataSourceAlert { + + public Database server; + + private boolean initialized; + + @Override + public void dataSourceUp(DataSource dataSource) { + if (!initialized) { + initDatabase(); + } + } + + public synchronized void initDatabase() { + if (!initialized) { + server.runDdl(); + server.runMigration(); + initialized = true; + } + } + + @Override + public void dataSourceDown(DataSource dataSource, SQLException reason) {} + + @Override + public void dataSourceWarning(DataSource dataSource, String msg) {} + + } + + @Test + @ForPlatform({Platform.H2}) + public void testOffline_recovery() throws SQLException { + + String url = "jdbc:h2:mem:testoffline3"; + try (Connection bootup = DriverManager.getConnection(url, "sa", "secret")) { + + Properties props = props(url); + + // to bring up ebean without a database, we must disable varous things + // that happen on startup + props.setProperty("datasource.h2_offline.failOnStart", "false"); + props.setProperty("ebean.h2_offline.skipDataSourceCheck", "true"); + props.setProperty("ebean.h2_offline.ddl.run", "false"); + DatabaseConfig config = config(props); + + LazyDatasourceInitializer alert = new LazyDatasourceInitializer() ; + config.getDataSourceConfig().setAlert(alert); + config.getDataSourceConfig().setHeartbeatFreqSecs(1); + + Database h2Offline = DatabaseFactory.create(config); + alert.server = h2Offline; + assertThat(h2Offline).isNotNull(); + // DB is online now in offline mode + + // Accessing the DB will throw a PE + assertThatThrownBy(() -> alert.initDatabase()) + .isInstanceOf(PersistenceException.class) + .hasMessageContaining("Failed to obtain connection to run DDL"); + + assertThatThrownBy(() -> h2Offline.find(EBasicVer.class).findCount()).isInstanceOf(PersistenceException.class); + + // so - reset the password so that the server can reconnect + try (Statement stmt = bootup.createStatement()) { + stmt.execute("alter user sa set password 'sa'"); + } + + assertThat(alert.initialized).isFalse(); + + // next access to ebean should bring DS online + h2Offline.find(EBasicVer.class).findCount(); + assertThat(alert.initialized).isTrue(); + + // check if server is working (ie ddl was run) + EBasicVer bean = new EBasicVer("foo"); + h2Offline.save(bean); + assertThat(h2Offline.find(EBasicVer.class).findCount()).isEqualTo(1); + h2Offline.delete(bean); + } + } + + private Properties props(String url) { + + Properties props = new Properties(); + + props.setProperty("datasource.h2_offline.username", "sa"); + props.setProperty("datasource.h2_offline.password", "sa"); + props.setProperty("datasource.h2_offline.url", url); + props.setProperty("datasource.h2_offline.driver", "org.h2.Driver"); + + props.setProperty("ebean.h2_offline.databasePlatformName", "h2"); + props.setProperty("ebean.h2_offline.ddl.extra", "false"); + + props.setProperty("ebean.h2_offline.ddl.generate", "true"); + props.setProperty("ebean.h2_offline.ddl.run", "true"); + + return props; + } + + private DatabaseConfig config(Properties props) { + DatabaseConfig config = new DatabaseConfig(); + config.setName("h2_offline"); + config.loadFromProperties(props); + config.setDefaultServer(false); + config.setRegister(false); + config.getClasses().add(EBasicVer.class); + return config; + } + +} diff --git a/pom.xml b/pom.xml index 76c596d4d..99861c0e2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.avaje java8-oss - 3.2 + 3.3 io.ebean diff --git a/release.properties b/release.properties deleted file mode 100644 index e846a5665..000000000 --- a/release.properties +++ /dev/null @@ -1,83 +0,0 @@ -#release configuration -#Fri Dec 17 17:24:24 NZDT 2021 -projectVersionPolicyId=default -project.scm.io.ebean\:ebean-core-type.tag=HEAD -scm.branchCommitComment=@{prefix} prepare branch @{releaseLabel} -project.rel.io.ebean\:ebean-ddl-generator=12.13.2 -project.dev.io.ebean\:ebean-core=12.13.3-SNAPSHOT -project.scm.io.ebean\:ebean-api.tag=HEAD -project.scm.io.ebean\:ebean-redis.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.scm.io.ebean\:ebean-querybean.tag=HEAD -project.scm.io.ebean\:ebean-api.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.dev.io.ebean\:ebean-api=12.13.3-SNAPSHOT -project.rel.io.ebean\:ebean=12.13.2 -project.dev.io.ebean\:ebean-core-type=12.13.3-SNAPSHOT -project.scm.io.ebean\:ebean-ddl-generator.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -exec.activateProfiles=release -project.dev.io.ebean\:ebean-test=12.13.3-SNAPSHOT -project.scm.io.ebean\:kotlin-querybean-generator.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.scm.io.ebean\:ebean-querybean.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.scm.io.ebean\:ebean-bom.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.scm.io.ebean\:ebean-core.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.rel.io.ebean\:ebean-core-type=12.13.2 -project.rel.io.ebean\:kotlin-querybean-generator=12.13.2 -project.dev.io.ebean\:querybean-generator=12.13.3-SNAPSHOT -project.scm.io.ebean\:ebean-autotune.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -scm.commentPrefix=[maven-release-plugin] -releaseStrategyId=default -project.rel.io.ebean\:ebean-querybean=12.13.2 -project.scm.io.ebean\:ebean-externalmapping-api.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.scm.io.ebean\:querybean-generator.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.dev.io.ebean\:kotlin-querybean-generator=12.13.3-SNAPSHOT -project.scm.io.ebean\:ebean-core-type.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -completedPhase=generate-release-poms -project.rel.io.ebean\:ebean-redis=12.13.2 -project.scm.io.ebean\:ebean-test.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.scm.io.ebean\:ebean-postgis.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.dev.io.ebean\:ebean-autotune=12.13.3-SNAPSHOT -project.rel.io.ebean\:ebean-api=12.13.2 -project.scm.io.ebean\:ebean-postgis.tag=HEAD -project.scm.io.ebean\:querybean-generator.tag=HEAD -project.rel.io.ebean\:querybean-generator=12.13.2 -scm.tag=ebean-parent-12.13.2 -project.rel.io.ebean\:ebean-externalmapping-xml=12.13.2 -project.scm.io.ebean\:ebean-ddl-generator.tag=HEAD -project.rel.io.ebean\:ebean-externalmapping-api=12.13.2 -project.scm.io.ebean\:ebean-externalmapping-xml.tag=HEAD -preparationGoals=clean verify -exec.pomFileName=pom.xml -project.dev.io.ebean\:ebean-externalmapping-api=12.13.3-SNAPSHOT -project.dev.io.ebean\:ebean=12.13.3-SNAPSHOT -project.scm.io.ebean\:ebean.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.dev.io.ebean\:ebean-parent=12.13.3-SNAPSHOT -project.dev.io.ebean\:ebean-externalmapping-xml=12.13.3-SNAPSHOT -project.rel.io.ebean\:ebean-core=12.13.2 -project.dev.io.ebean\:ebean-redis=12.13.3-SNAPSHOT -project.dev.io.ebean\:ebean-postgis=12.13.3-SNAPSHOT -project.scm.io.ebean\:ebean-redis.tag=HEAD -project.scm.io.ebean\:ebean-parent.tag=HEAD -pushChanges=true -project.scm.io.ebean\:ebean-externalmapping-xml.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -project.rel.io.ebean\:ebean-test=12.13.2 -project.scm.io.ebean\:kotlin-querybean-generator.tag=HEAD -project.scm.io.ebean\:ebean-bom.tag=HEAD -project.scm.io.ebean\:ebean-autotune.tag=HEAD -project.scm.io.ebean\:ebean.tag=HEAD -project.scm.io.ebean\:ebean-core.tag=HEAD -project.rel.io.ebean\:ebean-autotune=12.13.2 -scm.rollbackCommitComment=@{prefix} rollback the release of @{releaseLabel} -remoteTagging=true -project.dev.io.ebean\:ebean-bom=12.13.3-SNAPSHOT -project.dev.io.ebean\:ebean-querybean=12.13.3-SNAPSHOT -project.rel.io.ebean\:ebean-bom=12.13.2 -project.scm.io.ebean\:ebean-parent.developerConnection=scm\:git\:git@github.com\:ebean-orm/ebean.git -scm.url=scm\:git\:git@github.com\:ebean-orm/ebean.git -scm.developmentCommitComment=@{prefix} prepare for next development iteration -project.scm.io.ebean\:ebean-externalmapping-api.tag=HEAD -scm.tagNameFormat=@{project.artifactId}-@{project.version} -project.scm.io.ebean\:ebean-test.tag=HEAD -project.dev.io.ebean\:ebean-ddl-generator=12.13.3-SNAPSHOT -exec.snapshotReleasePluginAllowed=false -scm.releaseCommitComment=@{prefix} prepare release @{releaseLabel} -project.rel.io.ebean\:ebean-postgis=12.13.2 -project.rel.io.ebean\:ebean-parent=12.13.2