title: How to call a method after a SpringBoot application has destroyed the ApplicationContext
body: <p>I have a SpringBoot application that uses a Hikari jdbc connection pool to access a file based H2 database.</p>
  <p><strong>application.yml</strong></p>
  <pre><code>spring:
    datasource:
      hikari:
        driver-class-name: org.h2.Driver
        jdbc-url: jdbc:h2:${media.db.file};DB_CLOSE_ON_EXIT=FALSE
        username:
        password:
        maximum-pool-size: 5
        connection-test-query: &quot;SELECT 1&quot;
        pool-name: media-pool
  </code></pre>
  <p>Now, I have a requirement that after application shutdown I need to create a local backup of the h2 database file.</p>
  <p>Obviously, this will only work after the db connection has been closed and since I have various beans that inject a DataSource, I assume that the db connection will only be closed after those beans have been destroyed (and obviously the class creating the copy cannot be a spring bean).</p>
  <p>So my issue is how to detect when all beans have been destroyed and the application context has been destroyed.</p>
  <p>Obviously annotation a method with @PreDestroy will not work as such methods are called <em>before</em> beans are destroyed.</p>
  <p>I have found various ways to register for application life cycle events, however, if I call <code>BackupUtility.backupDatabase()</code> from any of them I always get the same exception:</p>
  <pre><code>java.io.IOException: The process cannot access the file because another process has locked a portion of the file
      at java.base/java.io.FileInputStream.readBytes(Native Method)
      at java.base/java.io.FileInputStream.read(FileInputStream.java:257)
      at ch.bee.mediathek.business.utility.BackupUtility.backupDatabase(BackupUtility.java:63)
  </code></pre>
  <p>I assume that this means that the db connection has not yet been closed and I therefore tried to create the backup too early.</p>
  <p>I have tried the following ways to be notified of my application shutdown:</p>
  <pre><code>  public static void main(String[] args) {
      ConfigurableApplicationContext context = SpringApplication.run(MediaSpringApplication.class, args);
      context.registerShutdownHook();
      log.info(&quot;Application started...&quot;);
      context.addApplicationListener(MediaSpringApplication::onApplicationEvent);
    }
  
    private static void onApplicationEvent(final ApplicationEvent event) {
      if (event instanceof ContextClosedEvent) {
        log.info(&quot;===== ContextClosed (1) =====&quot;);
        BackupUtility.backupDatabase();
      }
    }
  
    @Bean
    ServletListenerRegistrationBean&lt;ServletContextListener&gt; myServletListener() {
      ServletListenerRegistrationBean&lt;ServletContextListener&gt; srb = new ServletListenerRegistrationBean&lt;&gt;();
      srb.setListener(new MyServletContextListener());
      return srb;
    }
  
    public class MyServletContextListener implements ServletContextListener {
      @Override
      public void contextInitialized(ServletContextEvent sce) {
      }
  
      @Override
      public void contextDestroyed(ServletContextEvent sce) {
        log.info(&quot;===== ContextDestroyed (2) =====&quot;);
        BackupUtility.backupDatabase();
      }
    }
  
    @EventListener({ContextClosedEvent.class})
    public void contextClosed() {
      log.info(&quot;===== ContextClosed (3) =====&quot;);
      BackupUtility.backupDatabase();
    }
  </code></pre>
  <p>Looking at the log output I see the following:</p>
  <pre><code>[2021-01-06 19:38:12.023] [Thread-8] [INFO ] c.b.m.a.MediaSpringApplication::contextClosed(144) -    ===== ContextClosed (3) =====
  [2021-01-06 19:38:12.030] [Thread-8] [ERROR] c.b.m.b.u.BackupUtility::backupDatabase(67) -    Error while copying file contents
  java.io.IOException: Der Prozess kann nicht auf die Datei zugreifen, da ein anderer Prozess einen Teil der Datei gesperrt hat
      at java.base/java.io.FileInputStream.readBytes(Native Method)
      at java.base/java.io.FileInputStream.read(FileInputStream.java:257)
      at ch.bee.mediathek.business.utility.BackupUtility.backupDatabase(BackupUtility.java:63)
      at ch.bee.mediathek.app.MediaSpringApplication.contextClosed(MediaSpringApplication.java:145)
  [2021-01-06 19:38:12.030] [Thread-8] [INFO ] c.b.m.a.MediaSpringApplication::onApplicationEvent(56) -    ===== ContextClosed (1) =====
  [2021-01-06 19:38:12.031] [Thread-8] [ERROR] c.b.m.b.u.BackupUtility::backupDatabase(67) -    Error while copying file contents
  java.io.IOException: Der Prozess kann nicht auf die Datei zugreifen, da ein anderer Prozess einen Teil der Datei gesperrt hat
      at java.base/java.io.FileInputStream.readBytes(Native Method)
      at java.base/java.io.FileInputStream.read(FileInputStream.java:257)
      at ch.bee.mediathek.business.utility.BackupUtility.backupDatabase(BackupUtility.java:63)
      at ch.bee.mediathek.app.MediaSpringApplication.onApplicationEvent(MediaSpringApplication.java:57)
  [2021-01-06 19:38:12.138] [Thread-8] [INFO ] c.b.m.a.MediaSpringApplication::contextDestroyed(137) -    ===== ContextDestroyed (2) =====
  [2021-01-06 19:38:12.140] [Thread-8] [ERROR] c.b.m.b.u.BackupUtility::backupDatabase(67) -    Error while copying file contents
  java.io.IOException: Der Prozess kann nicht auf die Datei zugreifen, da ein anderer Prozess einen Teil der Datei gesperrt hat
      at java.base/java.io.FileInputStream.readBytes(Native Method)
      at java.base/java.io.FileInputStream.read(FileInputStream.java:257)
      at ch.bee.mediathek.business.utility.BackupUtility.backupDatabase(BackupUtility.java:63)
      at ch.bee.mediathek.app.MediaSpringApplication$MyServletContextListener.contextDestroyed(MediaSpringApplication.java:138)
  [2021-01-06 19:38:12.393] [Thread-8] [WARN ] o.s.b.f.s.DisposableBeanAdapter::destroy(267) -    Invocation of destroy method failed on bean with name 'inMemoryDatabaseShutdownExecutor': org.h2.jdbc.JdbcSQLNonTransientConnectionException: Database is already closed (to disable automatic closing at VM shutdown, add &quot;;DB_CLOSE_ON_EXIT=FALSE&quot; to the db URL) [90121-200]
  [2021-01-06 19:38:12.395] [Thread-8] [INFO ] c.z.h.HikariDataSource::close(350) -    media-pool - Shutdown initiated...
  [2021-01-06 19:38:12.398] [Thread-8] [INFO ] c.z.h.HikariDataSource::close(352) -    media-pool - Shutdown completed.
  </code></pre>
  <p>Looking at the last four lines of my logs, I see that when I try to backup the database file after the last of my callbacks (the one triggered by the ServletContextListener), the file is apparently still open. However, on the next line I see that the DisposableBeanAdapter tries to do something which fails because the database has already been closed. The next two lines show that the HikariDataSource class only shuts down even later.</p>
  <p>So, it seems I need to find a way to start my Backup after the HikariDataSource has shutdown the media pool. I would have expected that this would happen when those beans are destroyed and that my context destruction callback would only be called after that has happened, but it does not seem to work that way.</p>
  <p>I have tried to remove the DB_CLOSE_ON_EXIT=FALSE from <code>spring.datasource.hikari.jdbc-url</code> in my <strong>application.yml</strong> file but that still fails to creat the backup but in addition also adds the following exception at the end of shutting down multiple times (probably once per data pool member):</p>
  <pre><code>[2021-01-06 20:04:30.466] [Thread-9] [WARN ] c.z.h.p.PoolBase::isConnectionAlive(184) -    media-pool - Failed to validate connection conn1: url=jdbc:h2:/workspaces/media-spring/database/Media-local user= (Database is already closed (to disable automatic closing at VM shutdown, add &quot;;DB_CLOSE_ON_EXIT=FALSE&quot; to the db URL) [90121-200]). Possibly consider using a shorter maxLifetime value.
  </code></pre>
  <p>Am I doing something wrong? Or is there another way to be notified even later that I haven't found yet?</p>