From c2846a0a3d194403e56cba161a36913394521eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Le=20Callonnec?= Date: Fri, 1 Nov 2024 17:07:54 +0000 Subject: [PATCH] Migrate HOZip to Kotlin. --- .../java/core/db/backup/BackupHelper.java | 74 ------------- src/main/java/core/db/backup/BackupHelper.kt | 72 +++++++++++++ src/main/java/core/db/backup/HOZip.java | 92 ---------------- src/main/java/core/db/backup/HOZip.kt | 52 +++++++++ .../java/core/db/backup/BackupHelperTest.kt | 100 ++++++++++++++++++ src/test/java/core/db/backup/HOZipTest.kt | 36 +++++++ src/test/resources/tools/sample.txt | 5 + 7 files changed, 265 insertions(+), 166 deletions(-) delete mode 100644 src/main/java/core/db/backup/BackupHelper.java create mode 100644 src/main/java/core/db/backup/BackupHelper.kt delete mode 100644 src/main/java/core/db/backup/HOZip.java create mode 100644 src/main/java/core/db/backup/HOZip.kt create mode 100644 src/test/java/core/db/backup/BackupHelperTest.kt create mode 100644 src/test/java/core/db/backup/HOZipTest.kt create mode 100644 src/test/resources/tools/sample.txt diff --git a/src/main/java/core/db/backup/BackupHelper.java b/src/main/java/core/db/backup/BackupHelper.java deleted file mode 100644 index fb125f9c5..000000000 --- a/src/main/java/core/db/backup/BackupHelper.java +++ /dev/null @@ -1,74 +0,0 @@ -package core.db.backup; - -import core.db.user.UserManager; -import core.file.ExampleFileFilter; -import core.util.HOLogger; -import java.io.File; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; - -/** - * HSQL DB zipper - * @author Thorsten Dietz - */ -public class BackupHelper { - - // zip and delete db - public static void backup(File dbDirectory) { - if (!dbDirectory.exists()) {return;} - - File[] filesToBackup = getFilesToBackup(dbDirectory); - if (filesToBackup.length == 0) {return;} - - HOZip zOut; - try { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); - - zOut = new HOZip(dbDirectory + File.separator + "db_" + UserManager.instance().getCurrentUser().getTeamName() - + "-" + sdf.format(new Date()) + ".zip"); - - for (File file : filesToBackup) { - zOut.addFile(file); - } - - zOut.closeArchive(); - } catch (Exception e) { - HOLogger.instance().log(BackupHelper.class, e); - } - - deleteOldFiles(dbDirectory); - } - - /** - * delete old zip files, which are out of backuplevel - */ - private static void deleteOldFiles(File dbDirectory) { - File toDelete = null; - ExampleFileFilter filter = new ExampleFileFilter("zip"); - filter.setIgnoreDirectories(true); - File[] files = dbDirectory.listFiles(filter); - if (files != null && files.length > UserManager.instance().getCurrentUser().getNumberOfBackups()) { - for (int i = 0; i < files.length; i++) { - if (i == 0 - || (toDelete != null && toDelete.lastModified() > files[i].lastModified())) { - toDelete = files[i]; - } - } - if (toDelete != null) - toDelete.delete(); - } - } - - private static File[] getFilesToBackup(File dbDirectory) { - return dbDirectory.listFiles(file -> { - String name = file.getName(); - return (name.endsWith(".script") || - name.endsWith(".data") || - name.endsWith(".backup") || - name.endsWith(".log") || - name.endsWith(".properties")); - }); - } - -} diff --git a/src/main/java/core/db/backup/BackupHelper.kt b/src/main/java/core/db/backup/BackupHelper.kt new file mode 100644 index 000000000..4924b8330 --- /dev/null +++ b/src/main/java/core/db/backup/BackupHelper.kt @@ -0,0 +1,72 @@ +package core.db.backup + +import core.db.user.UserManager +import core.util.HOLogger +import java.io.File +import java.text.SimpleDateFormat +import java.util.* + +/** + * HSQL DB zipper + * @author Thorsten Dietz + */ + +object BackupHelper { + private val sdf = SimpleDateFormat("yyyy-MM-dd") + private val extensions = listOf("script", "data", "backup", "log", "properties") + + // zip and delete db + @JvmStatic + fun backup(dbDirectory: File) { + if (!dbDirectory.exists()) { + return + } + + val filesToBackup = getFilesToBackup(dbDirectory) + if (filesToBackup.isEmpty()) { + return + } + + val zOut: HOZip + try { + + zOut = HOZip( + """$dbDirectory${File.separator}db_${UserManager.instance().currentUser.teamName}-${sdf.format(Date())}.${HOZip.zipExt}""" + ) + + for (file in filesToBackup) { + zOut.addFile(file) + } + + zOut.closeArchive() + } catch (e: Exception) { + HOLogger.instance().log(BackupHelper::class.java, e) + } + + deleteOldFiles(dbDirectory) + } + + /** + * Deletes old zip files in the directory dbDirectory. + * + * @param dbDirectory Directory where to find the zip files to be deleted. + */ + private fun deleteOldFiles(dbDirectory: File) { + val files = dbDirectory.listFiles { file: File -> + file.isFile && file.extension == HOZip.zipExt + + }?.toList() + + if (files != null) { + files.sortedByDescending { f -> f.lastModified() } + .drop(UserManager.instance().currentUser.numberOfBackups) + .forEach { f -> f.delete() } + } + } + + private fun getFilesToBackup(dbDirectory: File): Array { + return dbDirectory.listFiles { file: File -> + file.isFile && extensions.any { suffix -> file.extension == suffix } + } ?: arrayOf() + } +} diff --git a/src/main/java/core/db/backup/HOZip.java b/src/main/java/core/db/backup/HOZip.java deleted file mode 100644 index 64bdba135..000000000 --- a/src/main/java/core/db/backup/HOZip.java +++ /dev/null @@ -1,92 +0,0 @@ -package core.db.backup; - -import core.util.HOLogger; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - -public class HOZip extends File { - //~ Instance fields ---------------------------------------------------------------------------- - private static final long serialVersionUID = 2172587062884736633L; - private ZipOutputStream zOut; - private int fileCount = 0; - - //~ Constructors ------------------------------------------------------------------------------- - /** - * Creates a new HOZip object. - */ - public HOZip(String filename) throws Exception { - super(filename); - - HOLogger.instance().info(getClass(), "Create Backup: " + filename); - - // Open the ZipOutputStream - zOut = new ZipOutputStream(new FileOutputStream(this)); - zOut.setMethod(ZipOutputStream.DEFLATED); - zOut.setLevel(5); - } - - //~ Methods ------------------------------------------------------------------------------------ - public int getFileCount() { - return fileCount; - } - - public void addFile(File file) throws Exception { - FileInputStream tFINS = new FileInputStream(file); - final int bufLength = 1024; - byte[] buffer = new byte[bufLength]; - int readReturn = 0; - - // Set next Entry - zOut.putNextEntry(new ZipEntry(file.getName())); - - do { - readReturn = tFINS.read(buffer); - - if (readReturn != -1) { - zOut.write(buffer, 0, readReturn); - } - } while (readReturn != -1); - - zOut.closeEntry(); - fileCount++; - } - - /** - * Closes the archive if it is still open. - * - * @throws Exception - */ - public void closeArchive() throws Exception { - if (zOut != null) { - zOut.finish(); - zOut.close(); - } - } - - /** - * Deconstructor - * - * @throws Exception - */ - @Override - protected void finalize() throws Exception { - if (zOut != null) { - zOut.finish(); - zOut.close(); - } - } - - public void addStringEntry(String filename, String data) throws Exception { - - // Set next Entry - zOut.putNextEntry(new ZipEntry(filename)); - zOut.write(data.getBytes()); - zOut.closeEntry(); - - fileCount++; - } -} diff --git a/src/main/java/core/db/backup/HOZip.kt b/src/main/java/core/db/backup/HOZip.kt new file mode 100644 index 000000000..4e1268e9c --- /dev/null +++ b/src/main/java/core/db/backup/HOZip.kt @@ -0,0 +1,52 @@ +package core.db.backup + +import core.util.HOLogger +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.text.SimpleDateFormat +import java.util.* +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream + +private const val COMPRESSION_LEVEL = 5 +private const val COMPRESSION_METHOD = ZipOutputStream.DEFLATED + +class HOZip(filename: String) : File(filename) { + private val zOut: ZipOutputStream + + var fileCount: Int = 0 + private set + + /** + * Creates a new HOZip object. + */ + init { + HOLogger.instance().info(javaClass, "Create Backup: $filename") + zOut = ZipOutputStream(FileOutputStream(this)) + zOut.setMethod(COMPRESSION_METHOD) + zOut.setLevel(COMPRESSION_LEVEL) + } + + @Throws(Exception::class) + fun addFile(file: File) { + FileInputStream(file).use { fis -> + zOut.putNextEntry(ZipEntry(file.name)) + fis.copyTo(zOut) + zOut.closeEntry() + } + fileCount++ + } + + @Throws(Exception::class) + fun closeArchive() { + zOut.finish() + zOut.close() + } + + companion object { + val zipExt = "zip" + private val sdf = SimpleDateFormat("yyyy-MM-dd") + fun createZipName(prefix: String): String = prefix + sdf.format(Date()) + ".${zipExt}" + } +} diff --git a/src/test/java/core/db/backup/BackupHelperTest.kt b/src/test/java/core/db/backup/BackupHelperTest.kt new file mode 100644 index 000000000..e64d9a83b --- /dev/null +++ b/src/test/java/core/db/backup/BackupHelperTest.kt @@ -0,0 +1,100 @@ +package core.db.backup + +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import java.io.File +import java.nio.file.Files +import java.nio.file.attribute.FileTime +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter +import java.util.zip.ZipFile + + +internal class BackupHelperTest { + + private val testResourcesDir = File("./src/test/resources") + private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + + private fun listZipInDir(path: String): Array { + val dir = File(path) + val output = dir.listFiles { _, fileName -> + fileName.endsWith(".zip") + } + return output ?: arrayOf() + } + + private fun zipFileName() = "db_user-${formatter.format(LocalDate.now())}.zip" + + + private fun listFilesInZip(zipPath: String): List { + return ZipFile(zipPath) + .entries() + .toList().map { e -> e.name } + } + + @Test + fun testBackupDbDoesNothingIfDirDoesntExist() { + val noDir = File(testResourcesDir, "none") + BackupHelper.backup(noDir) + + val zips = listZipInDir(noDir.absolutePath) + Assertions.assertNotNull(zips) + listZipInDir(noDir.absolutePath).let { Assertions.assertTrue(it.isEmpty()) } + } + + @Test + fun testBackupDbDoesNothingIfNoMatchingFiles() { + val exportDir = File(testResourcesDir, "export") + BackupHelper.backup(exportDir) + + val zips = listZipInDir(exportDir.absolutePath) + Assertions.assertNotNull(zips) + listZipInDir(exportDir.absolutePath).let { Assertions.assertTrue(it.isEmpty()) } + } + + @Test + fun testBackupIncludesRelevantFiles() { + val dbDir = File(testResourcesDir, "db") + BackupHelper.backup(dbDir) + + val zips = listZipInDir(dbDir.absolutePath) + Assertions.assertNotNull(zips) + Assertions.assertEquals(1, zips.size) + + val entries = listFilesInZip(zips.first().absolutePath) + Assertions.assertEquals(3, entries.size) + Assertions.assertEquals(zipFileName(), zips.first().name) + } + + @Test + fun testBackupOnlyKeepsMaxNumber() { + val dbDir = File(testResourcesDir, "db") + + val currentDate = LocalDateTime.now() + (1..5).forEach { i -> + val date = currentDate.minusDays(i.toLong()) + val f = File(testResourcesDir, "db/db_user-${formatter.format(date)}.zip") + f.createNewFile() + Files.setLastModifiedTime(f.toPath(), FileTime.from(date.toInstant(ZoneOffset.UTC))) + } + + BackupHelper.backup(dbDir) + + val zips = listZipInDir(dbDir.absolutePath) + Assertions.assertNotNull(zips) + Assertions.assertEquals(3, zips.size) + } + + @AfterEach + fun cleanup() { + File(testResourcesDir, "db").listFiles() + ?.forEach { f -> + if (f.extension == "zip") { + f.delete() + } + } + } +} diff --git a/src/test/java/core/db/backup/HOZipTest.kt b/src/test/java/core/db/backup/HOZipTest.kt new file mode 100644 index 000000000..6ea82632d --- /dev/null +++ b/src/test/java/core/db/backup/HOZipTest.kt @@ -0,0 +1,36 @@ +package core.db.backup + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import java.io.File +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +internal class HOZipTest { + private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + + private fun generateTempZipFileName(): String { + val customDir = System.getProperty("java.io.tmpdir") + val tempFileName = System.currentTimeMillis() + return "${customDir}${File.separator}${tempFileName}.zip" + } + + @Test + fun testAddFile() { + val fileName = generateTempZipFileName() + val hoZip = HOZip(fileName) + + val testFile = this.javaClass.classLoader.getResource("tools/sample.txt") + assertTrue(testFile != null) + hoZip.addFile(File(testFile!!.path)) + hoZip.closeArchive() + assertTrue(File(fileName).exists()) + assertEquals(1, hoZip.fileCount) + } + + @Test + fun testCreateZipFileName() { + assertEquals("test-" + formatter.format(LocalDateTime.now()) + ".zip", HOZip.createZipName("test-")) + } +} diff --git a/src/test/resources/tools/sample.txt b/src/test/resources/tools/sample.txt new file mode 100644 index 000000000..ec36e66ae --- /dev/null +++ b/src/test/resources/tools/sample.txt @@ -0,0 +1,5 @@ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc feugiat, leo in varius placerat, diam mi rhoncus nisi, nec volutpat erat tortor sed lectus. Aenean varius augue urna, vulputate blandit est rutrum id. Duis metus dui, rutrum eu arcu et, ultricies aliquet est. Nulla efficitur urna a neque malesuada, ac maximus nunc elementum. Morbi nec auctor lacus. Sed et nunc pulvinar, mattis dui id, eleifend mauris. Nam porttitor massa eget lorem imperdiet, at maximus purus volutpat. Nunc quis tortor ut massa convallis pretium. Ut quis facilisis ligula, sagittis hendrerit lacus. Pellentesque vehicula non lacus eget pulvinar. + +Phasellus quis ipsum consequat, sagittis nulla et, faucibus urna. Praesent ultrices, est quis blandit placerat, tellus odio vehicula felis, id iaculis leo felis commodo sapien. Nam mattis vel leo eget egestas. Vestibulum facilisis vulputate nibh, eu sagittis ipsum dictum a. In faucibus eget mi non efficitur. Vivamus consectetur in quam eu dapibus. Nam facilisis turpis eu malesuada ornare. + +Fusce dapibus augue ac metus ultricies sagittis. Curabitur quis tellus sit amet est condimentum suscipit. Sed sit amet rutrum nulla. Vivamus elementum nec sem nec porttitor. Cras nisl odio, egestas non felis vitae, mattis congue purus. Vestibulum dui lorem, eleifend ac ante sit amet, varius efficitur mi. In molestie malesuada urna vel tempor. Maecenas tortor nisl, pulvinar sit amet posuere commodo, laoreet non sem.