From 35a3f4a1c09c4543ed55cce0010987ec440342df Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 16 Oct 2015 10:36:20 +0100 Subject: [PATCH] Reinstate the use of shutdown hooks in the tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit adf2c44b was an attempt to prevent HSQLDB from throwing an exception when the JVM exits. This was achieved by disabling the application context’s shutdown hook in the tests. This had the unwanted side effect of causing tests’ application contexts not to be closed. The reported symptom was that @Destroy methods were no longer being invoked. We need a different solution to the problem. The exception was: Caused by: org.hsqldb.HsqlException: Database lock acquisition failure: attempt to connect while db opening /closing at org.hsqldb.error.Error.error(Unknown Source) at org.hsqldb.error.Error.error(Unknown Source) at org.hsqldb.error.Error.error(Unknown Source) at org.hsqldb.DatabaseManager.getDatabase(Unknown Source) at org.hsqldb.DatabaseManager.newSession(Unknown Source) ... 23 common frames omitted I originally thought this was due to a race between the application context’s shutdown hook and HSQLDB’s shutdown hook, however HSQLDB doesn’t use a shutdown hook. I believe that the problem is due to an HSQLDB database being created with shutdown=true in its URL, similar to the problem described here [1]. This will shut down the database when the last connection to it is closed, however the shutdown will happen asynchronously. If the JVM then runs the application context’s shutdown hook, EmbeddedDatabaseFactory will attempt to connect to the database to execute the SHUTDOWN command. This executes synchronously but will race with the asynchronous shutdown that’s executing as a result of shutdown=true in the JDBC url and the last connection to the database being closed. This commit reinstates the use of application context shutdown hooks in the tests, and updates the documentation to recommend that, if a user manually configures the URL for their embedded database, they do so in such a way that the database doesn’t shutdown automatically, thereby allowing the shutdown to be driven by application context close. Closes gh-4208 [1] http://sourceforge.net/p/hsqldb/bugs/1400/ --- .../src/main/asciidoc/spring-boot-features.adoc | 7 +++++++ .../boot/test/SpringApplicationContextLoader.java | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 58be6140cda..6400155c364 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -1526,6 +1526,13 @@ For example, typical POM dependencies would be: ---- +TIP: If, for whatever reason, you do configure the connection URL for an embedded +database, care should be taken to ensure that the database’s automatic shutdown is +disabled. If you're using H2 you should use `DB_CLOSE_ON_EXIT=FALSE` to do so. If you're +using HSQLDB, you should ensure that `shutdown=true` is not used. Disabling the database's +automatic shutdown allows Spring Boot to control when the database is closed, thereby +ensuring that it happens once access to the database is no longer needed. + NOTE: You need a dependency on `spring-jdbc` for an embedded database to be auto-configured. In this example it's pulled in transitively via `spring-boot-starter-data-jpa`. diff --git a/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java b/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java index 024d5dabce5..c9b1d142c69 100644 --- a/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java +++ b/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java @@ -82,7 +82,6 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { throws Exception { assertValidAnnotations(config.getTestClass()); SpringApplication application = getSpringApplication(); - application.setRegisterShutdownHook(false); application.setMainApplicationClass(config.getTestClass()); application.setSources(getSources(config)); ConfigurableEnvironment environment = new StandardEnvironment();