|
1 | 1 | package net.laprun.sustainability.power.persistence;
|
2 | 2 |
|
3 |
| -import java.sql.Connection; |
4 |
| -import java.sql.DriverManager; |
| 3 | +import java.io.IOException; |
| 4 | +import java.nio.file.Files; |
| 5 | +import java.nio.file.Paths; |
| 6 | +import java.nio.file.StandardCopyOption; |
5 | 7 | import java.sql.SQLException;
|
6 |
| -import java.sql.Statement; |
| 8 | +import java.util.concurrent.TimeUnit; |
| 9 | +import java.util.concurrent.atomic.AtomicBoolean; |
7 | 10 |
|
| 11 | +import jakarta.enterprise.context.ApplicationScoped; |
8 | 12 | import jakarta.enterprise.event.Observes;
|
| 13 | +import jakarta.inject.Inject; |
| 14 | +import jakarta.persistence.EntityManager; |
9 | 15 |
|
10 | 16 | import org.eclipse.microprofile.config.inject.ConfigProperty;
|
| 17 | +import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; |
| 18 | +import org.hibernate.internal.SessionImpl; |
| 19 | +import org.jboss.logging.Logger; |
11 | 20 |
|
12 |
| -import io.quarkus.logging.Log; |
13 | 21 | import io.quarkus.runtime.ShutdownEvent;
|
| 22 | +import io.quarkus.scheduler.Scheduled; |
14 | 23 |
|
| 24 | +@ApplicationScoped |
15 | 25 | public class SQLiteFilePersister {
|
16 | 26 | @ConfigProperty(name = "quarkus.datasource.jdbc.url")
|
17 | 27 | String jdbcUrl;
|
18 | 28 |
|
19 |
| - @ConfigProperty(name = "power-server.db.location") |
20 |
| - String fileName; |
| 29 | + private static final Logger LOGGER = Logger.getLogger(SQLiteFilePersister.class.getName()); |
21 | 30 |
|
22 |
| - // public void onStartup(@Observes StartupEvent event) { |
23 |
| - // Log.info("Restoring data from " + fileName); |
24 |
| - // try ( |
25 |
| - // Connection connection = DriverManager.getConnection(jdbcUrl); |
26 |
| - // Statement statement = connection.createStatement()) { |
27 |
| - // statement.executeUpdate("restore from " + fileName); |
28 |
| - // } catch (SQLException e) { |
29 |
| - // e.printStackTrace(System.err); |
30 |
| - // } |
31 |
| - // } |
| 31 | + @Inject |
| 32 | + EntityManager entityManager; |
32 | 33 |
|
| 34 | + private final AtomicBoolean executing = new AtomicBoolean(false); |
| 35 | + |
| 36 | + // Execute a backup every 10 seconds |
| 37 | + @Scheduled(delay = 10, delayUnit = TimeUnit.SECONDS, every = "10s") |
| 38 | + void scheduled() { |
| 39 | + backup(); |
| 40 | + } |
| 41 | + |
| 42 | + // Execute a backup during shutdown |
33 | 43 | public void onShutdown(@Observes ShutdownEvent event) {
|
34 |
| - Log.info("Saving data to " + fileName); |
35 |
| - try ( |
36 |
| - Connection connection = DriverManager.getConnection(jdbcUrl); |
37 |
| - Statement statement = connection.createStatement()) { |
38 |
| - statement.executeUpdate("backup to " + fileName); |
39 |
| - } catch (SQLException e) { |
40 |
| - e.printStackTrace(System.err); |
| 44 | + backup(); |
| 45 | + } |
| 46 | + |
| 47 | + void backup() { |
| 48 | + if (executing.compareAndSet(false, true)) { |
| 49 | + try { |
| 50 | + int prefixLength = "jdbc:sqlite:".length(); |
| 51 | + int queryParamsIdx = jdbcUrl.indexOf('?'); |
| 52 | + int length = (queryParamsIdx != -1) ? queryParamsIdx : jdbcUrl.length(); |
| 53 | + String dbFile = jdbcUrl.substring(prefixLength, length); |
| 54 | + |
| 55 | + var originalDbFilePath = Paths.get(dbFile); |
| 56 | + LOGGER.info("Starting DB backup for file: " + dbFile); |
| 57 | + var backupDbFilePath = originalDbFilePath.toAbsolutePath().getParent() |
| 58 | + .resolve(originalDbFilePath.getFileName() + "_backup"); |
| 59 | + |
| 60 | + JdbcConnectionAccess access = entityManager |
| 61 | + .unwrap(SessionImpl.class) |
| 62 | + .getJdbcConnectionAccess(); |
| 63 | + try (var conn = access.obtainConnection(); |
| 64 | + var stmt = conn.createStatement()) { |
| 65 | + // Execute the backup |
| 66 | + stmt.executeUpdate("backup to " + backupDbFilePath); |
| 67 | + // Atomically substitute the DB file with its backup |
| 68 | + Files.move(backupDbFilePath, originalDbFilePath, StandardCopyOption.ATOMIC_MOVE, |
| 69 | + StandardCopyOption.REPLACE_EXISTING); |
| 70 | + } catch (SQLException e) { |
| 71 | + throw new RuntimeException("Failed to backup the database", e); |
| 72 | + } catch (IOException e) { |
| 73 | + throw new RuntimeException("Failed to create backup files or folders", e); |
| 74 | + } |
| 75 | + LOGGER.info("Backup of " + dbFile + " completed."); |
| 76 | + } finally { |
| 77 | + executing.set(false); |
| 78 | + } |
| 79 | + } else { |
| 80 | + LOGGER.info("Backup in progress."); |
41 | 81 | }
|
42 | 82 | }
|
| 83 | + |
43 | 84 | }
|
0 commit comments