diff --git a/README.md b/README.md index eb719df..28419ab 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,14 @@ You run the command bir: * All files in this directory are inspected, new files are added to the database, deleted files are removed from the database. * If the file last modification date in database and on the disk is the same, but the content checksum is different, then a bit rot is detected. +This program cannot restore files with bitrot. + * You have to backup up your files (do snapshots). + ## How to setup your environment on Linux Example: - alias bir='java -jar /rv/data/desktop/code/code.nanoboot.org/nanoboot/bit-inspector/target/bir-inspector-0.0.0-SNAPSHOT-jar-with-dependencies.jar' + alias bir='java -jar /rv/data/desktop/code/code.nanoboot.org/nanoboot/bit-inspector/target/bit-inspector-0.0.0-SNAPSHOT-jar-with-dependencies.jar' ## Usage diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b39292e --- /dev/null +++ b/build.sh @@ -0,0 +1 @@ +mvn clean install diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0b5e398 --- /dev/null +++ b/pom.xml @@ -0,0 +1,234 @@ + + + + 4.0.0 + + + org.nanoboot.essential + nanoboot-parent + 0.1.0-SNAPSHOT + + + org.nanoboot.tools + bit-inspector + 0.0.0-SNAPSHOT + jar + + Bit Inspector + Tool detecting bit rots in files. + + + UTF-8 + true + 2.0.1-SNAPSHOT + 19 + 19 + 0.1.0 + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven-deploy-plugin.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + + org.projectlombok + lombok + ${lombok.version} + + + ${javase.version} + ${javase.version} + + + + org.apache.maven.plugins + maven-assembly-plugin + ${maven-assembly-plugin.version} + + + + org.nanoboot.bitinspector.core.Main + + + + jar-with-dependencies + + + + + + make-assembly + package + + single + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + ${checkstyle.skip} + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.2.0 + + + add-resource + generate-resources + + add-resource + + + + + src/main/resources/db_migrations/sqlite/bitinspector + db_migrations/sqlite/bitinspector + + *.sql + + + + + + + + + + + + + + + org.nanoboot.powerframework + power-time + ${power.version} + + + org.nanoboot.powerframework + power-random + ${power.version} + + + org.nanoboot.powerframework + power-collections + ${power.version} + + + + + junit + junit + ${junit4.version} + test + + + org.nanoboot.tools.dbmigration + db-migration-core + ${db-migration-core.version} + + + org.xerial + sqlite-jdbc + ${sqlite-jdbc.version} + + + commons-io + commons-io + 2.11.0 + + + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + + + + + + + releases + nanoboot-releases-repository + https://maven.nanoboot.org/releases + + + snapshots + nanoboot-snapshots-repository + https://maven.nanoboot.org/snapshots + + + + + + releases + nanoboot-releases-repository + https://maven.nanoboot.org/releases + + + snapshots + nanoboot-snapshots-repository + https://maven.nanoboot.org/snapshots + + + + diff --git a/setmodtime.sh b/setmodtime.sh new file mode 100755 index 0000000..7edb99b --- /dev/null +++ b/setmodtime.sh @@ -0,0 +1,3 @@ +set_last_mod_time_the_same_as_this_file="$1" +set_last_mod_time_for_this_file="$2" +touch -r "$set_last_mod_time_the_same_as_this_file" "$set_last_mod_time_for_this_file" diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100755 index 0000000..e2bf9d3 --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2016-2022 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// + +module bitinspector { + requires org.apache.commons.io; + requires lombok; + requires org.apache.logging.log4j; + requires dbmigration.core; + requires java.sql; + requires powerframework.time; + requires powerframework.collections; +} diff --git a/src/main/java/org/nanoboot/bitinspector/commands/BirIgnoreRegex.java b/src/main/java/org/nanoboot/bitinspector/commands/BirIgnoreRegex.java new file mode 100644 index 0000000..36ba20d --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/commands/BirIgnoreRegex.java @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.commands; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import org.nanoboot.bitinspector.core.Utils; + +/** + * + * @author robertvokac + */ +public class BirIgnoreRegex implements Predicate { + + private final List patterns = new ArrayList<>(); + + public BirIgnoreRegex(File birIgnoreFile) { + String[] lines = birIgnoreFile.exists() ? Utils.readTextFromFile(birIgnoreFile).split("\\R") : new String[]{}; + if (lines.length == 0) { + //nothing to do + return; + } + for (String l : lines) { + if (l.isBlank() || l.trim().startsWith("#")) { + //nothing to do + continue; + } + patterns.add(convertUnixRegexToJavaRegex(l)); + } + patterns.add(convertUnixRegexToJavaRegex("*.birreport.csv")); + + + } + + @Override + public boolean test(String text) { + if (patterns.isEmpty()) { + //nothing to do + return false; + } + boolean ignore = false; + for (String p : patterns) { + boolean b = Pattern.matches(p, text); + if (b) { + ignore = true; + } else { + + } + } +// if (ignore) { +// System.out.println("ignoring file: " + text); +// } else { +// System.out.println("accepting file: " + text); +// } + return ignore; + } + + public static String convertUnixRegexToJavaRegex(String wildcard) { + StringBuffer s = new StringBuffer(wildcard.length()); + s.append('^'); + for (int i = 0, is = wildcard.length(); i < is; i++) { + char c = wildcard.charAt(i); + switch (c) { + case '*': + s.append(".*"); + break; + case '?': + s.append("."); + break; + // escape special regexp-characters + case '(': + case ')': + case '[': + case ']': + case '$': + case '^': + case '.': + case '{': + case '}': + case '|': + case '\\': + s.append("\\"); + s.append(c); + break; + default: + s.append(c); + break; + } + } + s.append('$'); + return (s.toString()); + } + +} diff --git a/src/main/java/org/nanoboot/bitinspector/commands/CheckCommand.java b/src/main/java/org/nanoboot/bitinspector/commands/CheckCommand.java new file mode 100644 index 0000000..02ee619 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/commands/CheckCommand.java @@ -0,0 +1,280 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.commands; + +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import org.nanoboot.bitinspector.core.Command; +import org.nanoboot.bitinspector.core.BitInspectorArgs; +import org.nanoboot.bitinspector.core.BitInspectorException; +import org.nanoboot.bitinspector.core.Utils; +import org.nanoboot.bitinspector.entity.FsFile; +import org.nanoboot.bitinspector.entity.SystemItem; +import org.nanoboot.bitinspector.persistence.api.FileRepository; +import org.nanoboot.bitinspector.persistence.api.SystemItemRepository; +import org.nanoboot.bitinspector.persistence.impl.sqlite.FileRepositoryImplSqlite; +import org.nanoboot.bitinspector.persistence.impl.sqlite.SqliteDatabaseMigration; +import org.nanoboot.bitinspector.persistence.impl.sqlite.SystemItemRepositoryImplSqlite; + +/** + * + * @author pc00289 + */ +public class CheckCommand implements Command { + + private final File currentDirRoot = new File("."); + private final File birSQLite3File = new File("./.bir.sqlite3"); + private final File birSQLite3FileSha512 = new File("./.bir.sqlite3.sha512"); + private final File birIgnore = new File("./.birignore"); + BirIgnoreRegex birIgnoreRegex = new BirIgnoreRegex(birIgnore); + + public CheckCommand() { + } + + @Override + public String getName() { + return "check"; + } + + static int i = 0; + + @Override + public void run(BitInspectorArgs bitInspectorArgs) { + SqliteDatabaseMigration sqliteDatabaseMigration = new SqliteDatabaseMigration(); + sqliteDatabaseMigration.migrate(); + + SystemItemRepository systemItemRepository = new SystemItemRepositoryImplSqlite(); + FileRepository fileRepository = new FileRepositoryImplSqlite(); + //// + String version = systemItemRepository.read("bir.version").getValue(); + System.out.println("bir.version=" + version); + if (version == null) { + systemItemRepository.create(new SystemItem("bir.version", "0.0.0-SNAPSHOT")); + } + System.out.println("Updating version in DB."); + version = systemItemRepository.read("bir.version").getValue(); + System.out.println("bir.version=" + version); + //// + + //// Check ,SQLite DB file has the expected SHA-512 hash sum + if (birSQLite3File.exists() && birSQLite3FileSha512.exists()) { + String expectedHash = Utils.readTextFromFile(birSQLite3FileSha512); + String returnedHash = Utils.calculateSHA512Hash(birSQLite3File); + if (!returnedHash.equals(expectedHash)) { + throw new BitInspectorException("Unexpected hash " + returnedHash + ". Expected SHA-512 hash sum was: " + expectedHash + " for file " + birSQLite3File.getAbsolutePath()); + } + } + //// Found files in directory + + List filesInDir = foundFilesInCurrentDir(currentDirRoot, new ArrayList<>()); + Set filesInDirSet = filesInDir.stream().map(f -> loadPathButOnlyTheNeededPart(currentDirRoot, f)).collect(Collectors.toSet()); + System.out.println("Found files:"); + filesInDir.stream().forEach((f -> System.out.println("#" + (++i) + " " + f.getAbsolutePath().substring(currentDirRoot.getAbsolutePath().length() + 1)))); + + //// Found files in DB + List filesInDb = fileRepository.list(); + Set filesInDbSet = filesInDb.stream().map(f -> f.getAbsolutePath()).collect(Collectors.toSet()); + System.out.println("Files in DB:"); + i = 0; + filesInDb.stream().forEach((f -> System.out.println("#" + (++i) + " " + f.toString()))); + + //// Add new files + Date lastChecked = new Date(); + org.nanoboot.powerframework.time.moment.LocalDateTime now = org.nanoboot.powerframework.time.moment.LocalDateTime.convertJavaUtilDateToPowerLocalDateTime(lastChecked); + + int processedCount0 = 0; + + List filesMissingInDb = new ArrayList<>(); + for (File fileInDir : filesInDir) { + processedCount0 = processedCount0 + 1; + if (processedCount0 % 100 == 0) { + double progress = ((double) processedCount0) / filesInDir.size() * 100; + System.out.println("Add - Progress: " + processedCount0 + "/" + filesInDir.size() + " " + String.format("%,.2f", progress) + "%"); + } + + String absolutePathOfFileInDir = loadPathButOnlyTheNeededPart(currentDirRoot, fileInDir); + if (!filesInDbSet.contains(absolutePathOfFileInDir)) { + Date lastModified = new Date(fileInDir.lastModified()); + org.nanoboot.powerframework.time.moment.LocalDateTime ldt = org.nanoboot.powerframework.time.moment.LocalDateTime.convertJavaUtilDateToPowerLocalDateTime(lastModified); + + FsFile fsFile = new FsFile( + UUID.randomUUID().toString(), + fileInDir.getName(), + absolutePathOfFileInDir, + ldt.toString(), + now.toString(), + Utils.calculateSHA512Hash(fileInDir), + "SHA-512" + ); + filesMissingInDb.add(fsFile); + } + + } + fileRepository.create(filesMissingInDb); + + //// Remove deleted files + List filesToBeRemovedFromDb = new ArrayList<>(); + int processedCount1 = 0; + + for (FsFile fileInDb : filesInDb) { + processedCount1 = processedCount1 + 1; + if (processedCount1 % 100 == 0) { + double progress = ((double) processedCount1) / filesInDb.size() * 100; + System.out.println("Remove - Progress: " + processedCount1 + "/" + filesInDb.size() + " " + String.format("%,.2f", progress) + "%"); + } + + String absolutePathOfFileInDb = fileInDb.getAbsolutePath(); + if (!filesInDirSet.contains(absolutePathOfFileInDb)) { + + filesToBeRemovedFromDb.add(fileInDb); + } + + } + for (FsFile f : filesToBeRemovedFromDb) { + fileRepository.remove(f); + } + + double countOfFilesToCalculateHashSum = filesInDb.size() - filesToBeRemovedFromDb.size(); + int processedCount = 0; + //// Update modified files with same last modification date + List filesWithBitRot = new ArrayList<>(); + List filesToUpdateLastCheckDate = new ArrayList<>(); + for (FsFile fileInDb : filesInDb) { + String absolutePathOfFileInDb = fileInDb.getAbsolutePath(); + if (filesToBeRemovedFromDb.contains(fileInDb)) { + //nothing to do + continue; + + } + processedCount = processedCount + 1; + if (processedCount % 100 == 0) { + double progress = ((double) processedCount) / countOfFilesToCalculateHashSum * 100; + System.out.println("Update - Progress: " + processedCount + "/" + countOfFilesToCalculateHashSum + " " + String.format("%,.2f", progress) + "%"); + } + File file = new File("./" + absolutePathOfFileInDb); + + Date lastModified = new Date(file.lastModified()); + org.nanoboot.powerframework.time.moment.LocalDateTime ldt = org.nanoboot.powerframework.time.moment.LocalDateTime.convertJavaUtilDateToPowerLocalDateTime(lastModified); + + String calculatedHash = Utils.calculateSHA512Hash(file); + if (ldt.toString().equals(fileInDb.getLastModificationDate()) && !calculatedHash.equals(fileInDb.getHashSumValue())) { + filesWithBitRot.add(fileInDb); + fileInDb.setLastCheckDate(now.toString()); + fileRepository.updateFile(fileInDb); + continue; + } + if (!ldt.toString().equals(fileInDb.getLastModificationDate())) { + fileInDb.setLastCheckDate(now.toString()); + fileInDb.setLastModificationDate(ldt.toString()); + fileInDb.setHashSumValue(calculatedHash); + fileInDb.setHashSumAlgorithm("SHA-512"); + fileRepository.updateFile(fileInDb); + continue; + } + if (ldt.toString().equals(fileInDb.getLastModificationDate())) { + filesToUpdateLastCheckDate.add(fileInDb); + continue; + } + + } + fileRepository.updateLastCheckDate(now.toString(), filesToUpdateLastCheckDate); + + //// Report files, which may have a bitrot and will have to be restored from a backup + System.out.println("\n\n"); + + if (filesWithBitRot.isEmpty()) { + System.out.println("No files with bit rot were found."); + } + filesWithBitRot.stream().forEach(f + -> System.out.println("Bit rot detected: \"" + f.getAbsolutePath() + "\"" + " expected_sha512=" + f.getHashSumValue() + " returned_sha512=" + Utils.calculateSHA512Hash(new File("./" + f.getAbsolutePath()))) + ); + if (bitInspectorArgs.hasArgument("reportid")) { + String reportId = bitInspectorArgs.getArgument("reportid"); + File reportIdFile = new File("./" + reportId + ".birreport.csv"); + if (reportIdFile.exists()) { + Long nowLong = org.nanoboot.powerframework.time.moment.UniversalDateTime.now().toLong(); + + File backup = new File(reportIdFile.getParentFile().getAbsolutePath() + "/" + nowLong + "." + reportIdFile.getName()); + System.out.println("backup=" + backup); + reportIdFile.renameTo(backup); + } + StringBuilder sb = new StringBuilder(); + if (!filesWithBitRot.isEmpty()) { + sb.append("file;expected;calculated\n"); + } + filesWithBitRot.stream().forEach(f + -> sb.append(f.getAbsolutePath()) + .append(";") + .append(f.getHashSumValue()) + .append(";") + .append(Utils.calculateSHA512Hash(new File("./" + f.getAbsolutePath()))) + .append("\n") + ); + Utils.writeTextToFile(sb.toString(), reportIdFile); + } + //// Calculate current checksum of DB file. + Utils.writeTextToFile(Utils.calculateSHA512Hash(birSQLite3File), birSQLite3FileSha512); + + System.out.println("foundFiles=" + foundFiles); + System.out.println("foundDirs=" + foundDirs); + + } + + private String loadPathButOnlyTheNeededPart(File currentDir, File file) { + return file.getAbsolutePath().substring(currentDir.getAbsolutePath().length() + 1); + } + static int iii = 0; + + private int foundFiles; + private int foundDirs; + + private List foundFilesInCurrentDir(File currentDir, List files) { + + for (File f : currentDir.listFiles()) { + if (f.isDirectory()) { + ++foundDirs; + foundFilesInCurrentDir(f, files); + } else { + ++foundFiles; + if (f.getAbsolutePath().equals(birSQLite3File.getAbsolutePath())) { + continue; + } + if (f.getAbsolutePath().equals(birSQLite3FileSha512.getAbsolutePath())) { + continue; + } + if (f.getAbsolutePath().equals(birIgnore.getAbsolutePath())) { + continue; + } + ++iii; + //System.out.println("Testing file: " + iii + "#" + " " + loadPathButOnlyTheNeededPart(currentDirRoot, f)); + if (birIgnoreRegex.test(loadPathButOnlyTheNeededPart(currentDirRoot, f))) { + continue; + } + files.add(f); + } + } + return files; + } + +} diff --git a/src/main/java/org/nanoboot/bitinspector/commands/GenCommand.java.original~ b/src/main/java/org/nanoboot/bitinspector/commands/GenCommand.java.original~ new file mode 100644 index 0000000..80f7bcf --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/commands/GenCommand.java.original~ @@ -0,0 +1,304 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// dog: Tool generating documentation. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.dog.commands; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Properties; +import org.nanoboot.dog.Command; +import org.nanoboot.dog.DogArgs; +import org.nanoboot.dog.Menu; +import org.nanoboot.dog.Utils; +import org.asciidoctor.Asciidoctor; +import static org.asciidoctor.Asciidoctor.Factory.create; +import org.nanoboot.dog.DogException; +import org.apache.commons.io.FileUtils; + +/** + * + * @author pc00289 + */ +public class GenCommand implements Command { + + private static final String ADOC_EXTENSION = ".adoc"; + + public GenCommand() { + } + + @Override + public String getName() { + return "gen"; + } + + @Override + public void run(DogArgs dogArgs) { + if (!dogArgs.hasArgument("in")) { + dogArgs.addArgument("in", new File(".").getAbsolutePath()); + } + + if (dogArgs.getArgument("in") == null) { + throw new DogException("Argument in must have a value (must not be empty)."); + } + if (dogArgs.hasArgument("out") && !(new File(dogArgs.getArgument("out")).exists())) { + throw new DogException("Argument out must be an existing directory."); + } + + File inDir = new File(dogArgs.getArgument("in")); + if (!inDir.exists()) { + throw new DogException("Argument in must be an existing directory, but that directory does not exist."); + } + File dogConfFile = new File(inDir, "dog.conf"); + if (!dogConfFile.exists()) { + throw new DogException("File dog.conf was not found."); + } + File generatedDir = new File((dogArgs.hasArgument("out") ? new File(dogArgs.getArgument("out")) : inDir), "generated"); + + if (generatedDir.exists()) { + try { + FileUtils.deleteDirectory(generatedDir); + } catch (IOException ex) { + ex.printStackTrace(); + throw new DogException("Deleting generated directory failed.", ex); + } + } + generatedDir.mkdir(); + if (!generatedDir.exists()) { + throw new DogException("Argument out must be an existing directory, but that directory does not exist."); + } + + // + Properties dogConfProperties = null; + try (InputStream input = new FileInputStream(dogConfFile.getAbsolutePath())) { + dogConfProperties = new Properties(); + dogConfProperties.load(input); + + } catch (IOException ex) { + ex.printStackTrace(); + throw new DogException("Loading file dog.conf failed.", ex); + } + Utils.writeTextToFile(Utils.readTextFromResourceFile("/dog.css"), new File(generatedDir, "dog.css")); + File contentDir = new File(inDir, "content"); + Menu menuInstance = new Menu(contentDir); + File templateDir = new File(contentDir.getParentFile().getAbsolutePath() + "/templates"); + String headerTemplate = Utils.readTextFromFile(new File(templateDir, "header.html")); + String footerTemplate = Utils.readTextFromFile(new File(templateDir, "footer.html")); + processContentDir(contentDir, generatedDir, contentDir, dogConfProperties, menuInstance, headerTemplate, footerTemplate); + } + + private static void processContentDir(File dir, File generatedDir, File contentDir, Properties dogConfProperties, Menu menuInstance, String headerTemplate, String footerTemplate) { + for (File inFile : dir.listFiles()) { + if (inFile.isFile()) { + processFileInContentDir(inFile, dir, contentDir, dogConfProperties, headerTemplate, menuInstance, footerTemplate, generatedDir); + } + if (inFile.isDirectory()) { + processDirInContentDir(generatedDir, inFile, contentDir, dogConfProperties, menuInstance, headerTemplate, footerTemplate); + } + } + } + + public static void processDirInContentDir(File generatedDir, File inFile, File contentDir, Properties dogConfProperties, Menu menuInstance, String headerTemplate, String footerTemplate) { + File generatedDir2 = new File(generatedDir, inFile.getName()); + generatedDir2.mkdir(); + processContentDir(inFile, generatedDir2, contentDir, dogConfProperties, menuInstance, headerTemplate, footerTemplate); + } + + public static void processFileInContentDir(File inFile, File dir, File contentDir, Properties dogConfProperties, String headerTemplate, Menu menuInstance, String footerTemplate, File generatedDir) { + if (inFile.getName().endsWith(ADOC_EXTENSION)) { + + Asciidoctor asciidoctor = create(); + String asciidocText = Utils.readTextFromFile(inFile); + + String asciidocCompiled = asciidoctor + .convert(asciidocText, new HashMap()); + String pathToRoot = dir.getAbsolutePath().replace(contentDir.getAbsolutePath(), ""); + + if (!pathToRoot.trim().isEmpty()) { + int count = 0; + for (char ch : pathToRoot.toCharArray()) { + if (ch == '/') { + count++; + } + } + StringBuilder sb = new StringBuilder(); + for (int i = 1; i <= count; i++) { + sb.append("../"); + } + pathToRoot = sb.toString(); + + } + String titleSeparator = dogConfProperties.containsKey("titleSeparator") ? dogConfProperties.getProperty("titleSeparator") : "::"; + final String humanName = createHumanName(inFile, dogConfProperties); + + String start + = """ + + + + + + + + + """ + + humanName + + (dogConfProperties.containsKey("title") + ? (" " + titleSeparator + " " + dogConfProperties.getProperty("title")) : "") + + """ + + + + + """ + + headerTemplate + + """ + """ + + createMenu(menuInstance, inFile) + + """ +
+ """; + String end + = """ +
+ + + + """; + + List dirs = new ArrayList<>(); + File currentFile = inFile; + String rootContentDirPath = contentDir.getAbsolutePath(); + while (!currentFile.getAbsolutePath().equals(rootContentDirPath)) { + dirs.add(currentFile.getName()); + currentFile = currentFile.getParentFile(); + } + StringBuilder sb = new StringBuilder(); + for (int i = dirs.size() - 1; i >= 0; i--) { + String d = dirs.get(i); + sb.append(d); + if (i > 0) { + sb.append("/"); + } + } + String editThisPage = "
Edit this page"; + String htmlOutput = start + asciidocCompiled + editThisPage + end; + + File htmlFile = new File(generatedDir, inFile.getName().replace(ADOC_EXTENSION, ".html")); + Utils.writeTextToFile(htmlOutput, htmlFile); + + } else { + Utils.copyFile(inFile, generatedDir); + } + } + + + private static String createMenu(Menu menu, File currentFile) { + + return //"
" + menu.toAsciidoc(currentFile.getAbsolutePath().split("content")[1]) + "
" + + menu.toHtml(currentFile.getAbsolutePath().split("content")[1]); + + } + + private static String createNavigation(File adocFile, File rootContentDir, Properties dogConfProperties) { + List files = new ArrayList<>(); + File currentFile = adocFile; + while (!currentFile.getAbsolutePath().equals(rootContentDir.getAbsolutePath())) { + + if (currentFile.getName().equals("content")) { + continue; + } + files.add(currentFile); + currentFile = currentFile.getParentFile(); + } + + StringBuilder sb = new StringBuilder("
Home"); + if (files.size() > 1 || !currentFile.getName().equals("index.adoc")) { + sb.append(" > "); + } + for (int i = (files.size() - 1); i >= 0; i--) { + File file = files.get(i); + if (file.getName().equals("index.adoc")) { + continue; + } + sb + .append("") + .append(createHumanName(file, dogConfProperties)) + .append("\n"); + + sb.append(" > "); + + } + sb.append("
"); + String result = sb.toString(); + if (result.endsWith(" > ")) { + result = result.substring(0, result.length() - 9); + result = result + ""; + } + return result + "
"; + } + + private static String createHumanName(File inFile, Properties dogConfProperties) { + + String result = inFile.getName(); + if (result.endsWith(ADOC_EXTENSION)) { + result = result.substring(0, inFile.getName().length() - ADOC_EXTENSION.length()); + } + result = result.replace("_", " "); + if (Character.isLetter(result.charAt(0))) { + result = Character.toUpperCase(result.charAt(0)) + result.substring(1); + } + if (result.equals("Index")) { + File parentFile = inFile.getParentFile(); + if (parentFile.getName().equals("content")) { + String frontPageName = dogConfProperties.getProperty("frontPageName", ""); + return frontPageName.isBlank() ? "Home" : frontPageName; + } + return createHumanName(parentFile, dogConfProperties); + } + return result; + } +} diff --git a/src/main/java/org/nanoboot/bitinspector/commands/HelpCommand.java b/src/main/java/org/nanoboot/bitinspector/commands/HelpCommand.java new file mode 100644 index 0000000..7edadba --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/commands/HelpCommand.java @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.commands; + +import org.nanoboot.bitinspector.core.Command; +import org.nanoboot.bitinspector.core.BitInspectorArgs; + +/** + * + * @author pc00289 + */ +public class HelpCommand implements Command { + + public HelpCommand() { + } + + @Override + public String getName() { + return "help"; + } + + @Override + public void run(BitInspectorArgs bitInspectorArgs) { + String str = """ + NAME + bir - " Bit Inspector" + + SYNOPSIS + bir [command] [options] + If no command is provided, then the default command check is used. This means, if you run "bit-inspector", it is the same, as to run "bir check". + + DESCRIPTION + Detects bit rotten files in the given directory to keep your files forever. + + COMMAND + check Generates the static website + OPTIONS + reportid={unique name for this report, usually `date +'%Y%m%d_%H%M%S'`} + Optional. Default= (nothing will be reported to file report.{reportid}.bitreport.txt). + help Display help information + version Display version information +"""; + System.out.println(str); + + } + +} diff --git a/src/main/java/org/nanoboot/bitinspector/commands/HelpCommand.java.original~ b/src/main/java/org/nanoboot/bitinspector/commands/HelpCommand.java.original~ new file mode 100644 index 0000000..7fab53a --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/commands/HelpCommand.java.original~ @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// dog: Tool generating documentation. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.nanoboot.dog.commands; + +import org.nanoboot.dog.Command; + +/** + * + * @author pc00289 + */ +public class HelpCommand implements Command { + + public HelpCommand() { + } + + @Override + public String getName() { + return "help"; + } + +} diff --git a/src/main/java/org/nanoboot/bitinspector/commands/VersionCommand.java b/src/main/java/org/nanoboot/bitinspector/commands/VersionCommand.java new file mode 100644 index 0000000..99f9c9d --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/commands/VersionCommand.java @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.nanoboot.bitinspector.commands; + +import org.nanoboot.bitinspector.core.Command; +import org.nanoboot.bitinspector.core.BitInspectorArgs; + +/** + * + * @author pc00289 + */ +public class VersionCommand implements Command { + + public VersionCommand() { + } + + @Override + public String getName() { + return "version"; + } + + @Override + public void run(BitInspectorArgs bitInspectorArgs) { + System.out.println("Bit Inspector 0.0.0-SNAPSHOT"); + } + +} diff --git a/src/main/java/org/nanoboot/bitinspector/core/BitInspectorArgs.java b/src/main/java/org/nanoboot/bitinspector/core/BitInspectorArgs.java new file mode 100644 index 0000000..b61b258 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/core/BitInspectorArgs.java @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.nanoboot.bitinspector.core; + +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; + +/** + * + * @author pc00289 + */ +public class BitInspectorArgs { + + @Getter + private final String command; + private final Map internalMap = new HashMap<>(); + + public BitInspectorArgs(String[] args) { + command = args.length == 0 ? "check" : args[0]; + + if (args.length > 1) { + + for (String arg : args) { + if (arg == null) { + continue; + } + if (args[0].equals(arg)) { + continue; + } + String[] keyValue = arg.split("=", 2); + internalMap.put(keyValue[0], keyValue.length > 1 ? keyValue[1] : null); + } + for (String key : internalMap.keySet()) { + System.out.println("Found argument: " + key + "(=)" + internalMap.get(key)); + } + + } + } + public boolean hasArgument(String arg) { + return internalMap.containsKey(arg); + } + public void addArgument(String arg, String value) { + this.internalMap.put(arg,value); + } + + public String getArgument(String arg) { + return internalMap.get(arg); + } + +} diff --git a/src/main/java/org/nanoboot/bitinspector/core/BitInspectorException.java b/src/main/java/org/nanoboot/bitinspector/core/BitInspectorException.java new file mode 100644 index 0000000..88477b5 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/core/BitInspectorException.java @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.nanoboot.bitinspector.core; + +/** + * @author Robert Vokac + * @since 0.0.0 + */ +public class BitInspectorException extends RuntimeException { + + public BitInspectorException(String msg) { + super(msg); + } + + public BitInspectorException(String msg, Exception e) { + super(msg, e); + } + + public BitInspectorException(Exception e) { + super(e); + } + +} \ No newline at end of file diff --git a/src/main/java/org/nanoboot/bitinspector/core/Command.java b/src/main/java/org/nanoboot/bitinspector/core/Command.java new file mode 100644 index 0000000..bf7d017 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/core/Command.java @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.nanoboot.bitinspector.core; + +/** + * + * @author pc00289 + */ +public interface Command { + public String getName(); + default void run(BitInspectorArgs bitInspectorArgs) { + throw new BitInspectorException("Not yet implemented."); + } + +} diff --git a/src/main/java/org/nanoboot/bitinspector/core/Constants.java b/src/main/java/org/nanoboot/bitinspector/core/Constants.java new file mode 100644 index 0000000..1177ce0 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/core/Constants.java @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.nanoboot.bitinspector.core; + +import java.text.SimpleDateFormat; + +/** + * + * @author pc00289 + */ +public class Constants { + + public static final SimpleDateFormat YYYYMMDDHHMMSSZ_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ"); + +} diff --git a/src/main/java/org/nanoboot/bitinspector/core/Main.java b/src/main/java/org/nanoboot/bitinspector/core/Main.java new file mode 100755 index 0000000..b3454f2 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/core/Main.java @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.core; + +import org.nanoboot.bitinspector.commands.HelpCommand; +import org.nanoboot.bitinspector.commands.CheckCommand; +import org.nanoboot.bitinspector.commands.VersionCommand; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Robert Vokac + * @since 0.0.0 + */ +public class Main { + + public static void main(String[] args) { + System.out.println("Bir - Detects bit rotten files in the given directory to keep your files forever.\n"); + + BitInspectorArgs BitInspectorArgs = new BitInspectorArgs(args); + String command = BitInspectorArgs.getCommand(); + + Set commandImplementations = new HashSet<>(); + commandImplementations.add(new CheckCommand()); + commandImplementations.add(new HelpCommand()); + commandImplementations.add(new VersionCommand()); + Command foundCommand = null; + for(Command e:commandImplementations) { + if(e.getName().equals(command)) { + foundCommand = e; + break; + } + } + if(foundCommand == null) { + System.err.println("Error: Command \"" + command + "\" is not supported.\n"); + + new HelpCommand().run(BitInspectorArgs); + System.exit(1); + } + foundCommand.run(BitInspectorArgs); + } + + +} diff --git a/src/main/java/org/nanoboot/bitinspector/core/Utils.java b/src/main/java/org/nanoboot/bitinspector/core/Utils.java new file mode 100644 index 0000000..a2d045e --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/core/Utils.java @@ -0,0 +1,199 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.core; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author pc00289 + */ +public class Utils { + + private static final String UNDERSCORE = "_"; + + private Utils() { + //Not meant to be instantiated. + } + + public static String replaceUnderscoresBySpaces(String s) { + if (!s.contains(UNDERSCORE)) { + //nothing to do + return s; + } + return s.replace(UNDERSCORE, " "); + } + + public static String makeFirstLetterUppercase(String s) { + if (Character.isLetter(s.charAt(0)) && Character.isLowerCase(s.charAt(0))) { + return Character.toUpperCase(s.charAt(0)) + + (s.length() == 1 ? "" : s.substring(1)); + } else { + return s; + } + } + + public static int getCountOfSlashOccurences(String string) { + int i = 0; + for (char ch : string.toCharArray()) { + if (ch == '/') { + i++; + } + } + return i++; + } + + public static List listAdocFilesInDir(File dir) { + return listAdocFilesInDir(dir, new ArrayList<>()); + } + + private static List listAdocFilesInDir(File dir, List files) { + List allFiles = listAllFilesInDir(dir, files); + List adocFiles = new ArrayList<>(); + for (File f : allFiles) { + if (f.getName().endsWith(".adoc") && !f.isDirectory()) { + adocFiles.add(f); + } + } + return adocFiles; + } + + public static List listAllFilesInDir(File dir) { + return listAllFilesInDir(dir, new ArrayList<>()); + } + + private static List listAllFilesInDir(File dir, List files) { + files.add(dir); + for (File f : dir.listFiles()) { + if (f.isDirectory()) { + listAllFilesInDir(f, files); + } else { + files.add(f); + } + } + return files; + } + + public static String createDoubleDotSlash(int times) { + StringBuilder sb = new StringBuilder(); + for (int i = 1; i <= times; i++) { + sb.append("../"); + } + String result = sb.toString(); + return result;//.substring(0, result.length() - 1); + } + + public static void copyFile(File originalFile, File copiedFile) throws BitInspectorException { + Path originalPath = originalFile.toPath(); + Path copied = new File(copiedFile, originalFile.getName()).toPath(); + + try { + Files.copy(originalPath, copied, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException ex) { + ex.printStackTrace(); + throw new BitInspectorException("Copying file failed: " + originalFile.getAbsolutePath()); + } + } + + public static void writeTextToFile(String text, File file) { + FileWriter fileWriter; + try { + fileWriter = new FileWriter(file); + } catch (IOException ex) { + ex.printStackTrace(); + throw new BitInspectorException("Writing to file failed: " + file.getName(), ex); + } + PrintWriter printWriter = new PrintWriter(fileWriter); + printWriter.print(text); + printWriter.close(); + } + + public static String readTextFromFile(File file) { + if (!file.exists()) { + return ""; + } + try { + return new String(Files.readAllBytes(Paths.get(file.getAbsolutePath()))); + } catch (IOException ex) { + throw new BitInspectorException("Reading file failed: " + file.getName(), ex); + } + } + + public static String readTextFromResourceFile(String fileName) { + try { + Class clazz = Main.class; + InputStream inputStream = clazz.getResourceAsStream(fileName); + return readFromInputStream(inputStream); + } catch (IOException ex) { + throw new BitInspectorException("Reading file failed: " + fileName, ex); + } + + } + + public static String readFromInputStream(InputStream inputStream) + throws IOException { + StringBuilder resultStringBuilder = new StringBuilder(); + try (BufferedReader br + = new BufferedReader(new InputStreamReader(inputStream))) { + String line; + while ((line = br.readLine()) != null) { + resultStringBuilder.append(line).append("\n"); + } + } + return resultStringBuilder.toString(); + } + + public static String calculateSHA512Hash(File file) { + final Path path = Paths.get(file.getAbsolutePath()); + byte[] bytes; + try { + bytes = Files.readAllBytes(path); + } catch (IOException ex) { + throw new BitInspectorException(ex); + } + byte[] sha512sumByteArray; + try { + sha512sumByteArray = MessageDigest.getInstance("SHA-512").digest(bytes); + } catch (NoSuchAlgorithmException ex) { + throw new BitInspectorException(ex); + } + StringBuilder sb = new StringBuilder(sha512sumByteArray.length * 2); + for (byte b : sha512sumByteArray) { + sb.append(String.format("%02x", b)); + } + String hexString = sb.toString(); + return hexString; + } +} diff --git a/src/main/java/org/nanoboot/bitinspector/entity/FsFile.java b/src/main/java/org/nanoboot/bitinspector/entity/FsFile.java new file mode 100644 index 0000000..fa99f3d --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/entity/FsFile.java @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.nanoboot.bitinspector.entity; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * + * @author robertvokac + */ +@Getter +@Setter +@AllArgsConstructor +@ToString +public class FsFile { + private final String id; + private String name; + private String absolutePath; + private String lastModificationDate; + private String lastCheckDate; + private String hashSumValue; + private String hashSumAlgorithm; + +} diff --git a/src/main/java/org/nanoboot/bitinspector/entity/SystemItem.java b/src/main/java/org/nanoboot/bitinspector/entity/SystemItem.java new file mode 100644 index 0000000..aa32625 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/entity/SystemItem.java @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.nanoboot.bitinspector.entity; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +/** + * + * @author robertvokac + */ +@Getter +@Setter +@AllArgsConstructor +public class SystemItem { + private String key; + private String value; + + +} diff --git a/src/main/java/org/nanoboot/bitinspector/lombok.config b/src/main/java/org/nanoboot/bitinspector/lombok.config new file mode 100644 index 0000000..2066d75 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/lombok.config @@ -0,0 +1 @@ +lombok.log.fieldName=LOG diff --git a/src/main/java/org/nanoboot/bitinspector/persistence/api/FileRepository.java b/src/main/java/org/nanoboot/bitinspector/persistence/api/FileRepository.java new file mode 100644 index 0000000..6f1f93a --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/persistence/api/FileRepository.java @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.nanoboot.bitinspector.persistence.api; + +import java.util.List; +import org.nanoboot.bitinspector.entity.FsFile; + +/** + * + * @author robertvokac + */ +public interface FileRepository { + void create(List files); + List list(); + void remove(FsFile file); + void updateFile(FsFile file); + void updateLastCheckDate(String lastCheckDate, List files); + +} diff --git a/src/main/java/org/nanoboot/bitinspector/persistence/api/SystemItemRepository.java b/src/main/java/org/nanoboot/bitinspector/persistence/api/SystemItemRepository.java new file mode 100644 index 0000000..c61ea18 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/persistence/api/SystemItemRepository.java @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.nanoboot.bitinspector.persistence.api; + +import java.util.List; +import org.nanoboot.bitinspector.entity.SystemItem; + +/** + * + * @author robertvokac + */ +public interface SystemItemRepository { + String create(SystemItem systemItem); + List list(); + SystemItem read(String key); + void remove(String key); + void update(SystemItem systemItem); + +} diff --git a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/Constants.java b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/Constants.java new file mode 100644 index 0000000..2c078b3 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/Constants.java @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.persistence.impl.sqlite; + +/** + * + * @author robertvokac + */ +public class Constants { + static final String JDBC_URL = "jdbc:sqlite:" + "." + "/" + ".bir.sqlite3?foreign_keys=on;"; + private Constants() { + //Not meant to be instantiated. + } + +} diff --git a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/FileRepositoryImplSqlite.java b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/FileRepositoryImplSqlite.java new file mode 100644 index 0000000..37a2b1b --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/FileRepositoryImplSqlite.java @@ -0,0 +1,301 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.persistence.impl.sqlite; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.nanoboot.bitinspector.entity.FsFile; +import org.nanoboot.bitinspector.persistence.api.FileRepository; + +/** + * + * @author robertvokac + */ +public class FileRepositoryImplSqlite implements FileRepository { + + @Override + public void create(List files) { + if (files.isEmpty()) { + return; + } + if (files.size() > 100) { + List tmpList = new ArrayList<>(); + for (int i = 0; i < files.size(); i++) { + FsFile e = files.get(i); + tmpList.add(e); + if (tmpList.size() >= 100) { + create(tmpList); + tmpList.clear(); + } + } + if (!tmpList.isEmpty()) { + create(tmpList); + tmpList.clear(); + } + return; + } + + StringBuilder sb = new StringBuilder(); + sb + .append("INSERT INTO ") + .append(FileTable.TABLE_NAME) + .append("(") + .append(FileTable.ID).append(",") + .append(FileTable.NAME).append(",") + .append(FileTable.ABSOLUTE_PATH).append(",") + .append(FileTable.LAST_MODIFICATION_DATE).append(",") + .append(FileTable.LAST_CHECK_DATE).append(",") + // + .append(FileTable.HASH_SUM_VALUE).append(",") + .append(FileTable.HASH_SUM_ALGORITHM); + + sb.append(") VALUES "); + + int index = 0; + for (FsFile f : files) { + sb.append(" (?,?,?,?,?, ?,?)"); + boolean lastFile = index == (files.size() - 1); + if (!lastFile) { + sb.append(","); + } + index = index + 1; + } + + String sql = sb.toString(); + System.err.println(sql); + try ( + Connection connection = createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { + int i = 0; + + for (FsFile f : files) { + stmt.setString(++i, f.getId()); + stmt.setString(++i, f.getName()); + stmt.setString(++i, f.getAbsolutePath()); + stmt.setString(++i, f.getLastModificationDate()); + // + stmt.setString(++i, f.getLastCheckDate()); + // + stmt.setString(++i, f.getHashSumValue()); + stmt.setString(++i, f.getHashSumAlgorithm()); + + } + // + stmt.execute(); + System.out.println(stmt.toString()); + + } catch (SQLException e) { + System.out.println(e.getMessage()); + throw new RuntimeException(e); + } catch (ClassNotFoundException ex) { + Logger.getLogger(FileRepositoryImplSqlite.class.getName()).log(Level.SEVERE, null, ex); + } + + } + + @Override + public List list() { + + List result = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + sb + .append("SELECT * FROM ") + .append(FileTable.TABLE_NAME); + + String sql = sb.toString(); + System.err.println(sql); + int i = 0; + ResultSet rs = null; + try ( + Connection connection = createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { + + System.err.println(stmt.toString()); + rs = stmt.executeQuery(); + + while (rs.next()) { + result.add(extractFileFromResultSet(rs)); + } + } catch (SQLException e) { + System.out.println(e.getMessage()); + throw new RuntimeException(e); + } catch (ClassNotFoundException ex) { + Logger.getLogger(FileRepositoryImplSqlite.class.getName()).log(Level.SEVERE, null, ex); + } finally { + try { + if (rs != null) { + rs.close(); + } + } catch (SQLException ex) { + Logger.getLogger(FileRepositoryImplSqlite.class.getName()).log(Level.SEVERE, null, ex); + } + } + return result; + } + + @Override + public void remove(FsFile file) { + + StringBuilder sb = new StringBuilder(); + sb + .append("DELETE FROM ") + .append(FileTable.TABLE_NAME); + sb.append(" WHERE "); + + sb.append(FileTable.ID); + sb.append("=?"); + String sql = sb.toString(); + System.err.println("SQL::" + sql); + int i = 0; + + try ( + Connection connection = createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { + + stmt.setString(++i, file.getId()); + + System.err.println(stmt.toString()); + stmt.execute(); + + } catch (SQLException e) { + System.out.println(e.getMessage()); + throw new RuntimeException(e); + } catch (ClassNotFoundException ex) { + Logger.getLogger(FileRepositoryImplSqlite.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private Connection createConnection() throws ClassNotFoundException { + return new SqliteConnectionFactory().createConnection(); + } + + @Override + public void updateFile(FsFile file) { + + StringBuilder sb = new StringBuilder(); + sb + .append("UPDATE ") + .append(FileTable.TABLE_NAME) + .append(" SET ") + .append(FileTable.LAST_MODIFICATION_DATE).append("=?, ") + .append(FileTable.LAST_CHECK_DATE).append("=?, ") + .append(FileTable.HASH_SUM_VALUE).append("=?, ") + .append(FileTable.HASH_SUM_ALGORITHM).append("=? ") + .append(" WHERE ").append(FileTable.ID).append("=?"); + + String sql = sb.toString(); + //System.err.println(sql); + try ( + Connection connection = createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { + int i = 0; + stmt.setString(++i, file.getLastModificationDate()); + stmt.setString(++i, file.getLastCheckDate()); + stmt.setString(++i, file.getHashSumValue()); + stmt.setString(++i, file.getHashSumAlgorithm()); + + stmt.setString(++i, file.getId()); + + int numberOfUpdatedRows = stmt.executeUpdate(); + //System.out.println("numberOfUpdatedRows=" + numberOfUpdatedRows); + } catch (SQLException e) { + System.out.println(e.getMessage()); + throw new RuntimeException(e); + } catch (ClassNotFoundException ex) { + Logger.getLogger(FileRepositoryImplSqlite.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private FsFile extractFileFromResultSet(final ResultSet rs) throws SQLException { + return new FsFile( + rs.getString(FileTable.ID), + rs.getString(FileTable.NAME), + rs.getString(FileTable.ABSOLUTE_PATH), + rs.getString(FileTable.LAST_MODIFICATION_DATE), + rs.getString(FileTable.LAST_CHECK_DATE), + rs.getString(FileTable.HASH_SUM_VALUE), + rs.getString(FileTable.HASH_SUM_ALGORITHM) + ); + } + + @Override + public void updateLastCheckDate(String lastCheckDate, List files) { + + if (files.isEmpty()) { + return; + } + if (files.size() > 100) { + List tmpList = new ArrayList<>(); + for (int i = 0; i < files.size(); i++) { + FsFile e = files.get(i); + tmpList.add(e); + if (tmpList.size() >= 100) { + updateLastCheckDate(lastCheckDate, tmpList); + tmpList.clear(); + } + } + if (!tmpList.isEmpty()) { + updateLastCheckDate(lastCheckDate, tmpList); + tmpList.clear(); + } + return; + } + + StringBuilder sb = new StringBuilder(); + sb + .append("UPDATE ") + .append(FileTable.TABLE_NAME) + .append(" SET ") + .append(FileTable.LAST_CHECK_DATE).append("=? ") + .append(" WHERE ").append(FileTable.ID).append(" IN ("); + int index = 0; + for (FsFile f : files) { + index = index + 1; + sb.append("?"); + if (index < files.size()) { + sb.append(","); + } + } + sb.append(")"); + + String sql = sb.toString(); + //System.err.println(sql); + try ( + Connection connection = createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { + int i = 0; + + stmt.setString(++i, lastCheckDate); + + for (FsFile f : files) { + stmt.setString(++i, f.getId()); + } + + int numberOfUpdatedRows = stmt.executeUpdate(); + //System.out.println("numberOfUpdatedRows=" + numberOfUpdatedRows); + } catch (SQLException e) { + System.out.println(e.getMessage()); + throw new RuntimeException(e); + } catch (ClassNotFoundException ex) { + Logger.getLogger(FileRepositoryImplSqlite.class.getName()).log(Level.SEVERE, null, ex); + } + } + +} diff --git a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/FileTable.java b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/FileTable.java new file mode 100644 index 0000000..3f06f90 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/FileTable.java @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.persistence.impl.sqlite; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +/** + * + * @author robertvokac + */ +@Getter +@Setter +@AllArgsConstructor +class FileTable { + public static final String TABLE_NAME = "FILE"; + + public static final String ID = "ID"; + public static final String NAME = "NAME"; + public static final String ABSOLUTE_PATH = "ABSOLUTE_PATH"; + public static final String LAST_MODIFICATION_DATE = "LAST_MODIFICATION_DATE"; + public static final String LAST_CHECK_DATE = "LAST_CHECK_DATE"; + // + public static final String HASH_SUM_VALUE = "HASH_SUM_VALUE"; + public static final String HASH_SUM_ALGORITHM = "HASH_SUM_ALGORITHM"; + +} diff --git a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SqliteConnectionFactory.java b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SqliteConnectionFactory.java new file mode 100644 index 0000000..5e62c7a --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SqliteConnectionFactory.java @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.persistence.impl.sqlite; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +/** + * + * @author robertvokac + */ +public class SqliteConnectionFactory { + + public Connection createConnection() throws ClassNotFoundException { + try { + Class.forName("org.sqlite.JDBC"); + + Connection conn = DriverManager.getConnection(Constants.JDBC_URL); + + return conn; + + } catch (SQLException ex) { + if (true) { + throw new RuntimeException(ex); + } + System.err.println(ex.getMessage()); + return null; + } + } + +} diff --git a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SqliteDatabaseMigration.java b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SqliteDatabaseMigration.java new file mode 100644 index 0000000..943de25 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SqliteDatabaseMigration.java @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.persistence.impl.sqlite; + +import org.nanoboot.dbmigration.core.main.DBMigration; + +/** + * + * @author robertvokac + */ +public class SqliteDatabaseMigration { + + public void migrate() { + try { + Class.forName("org.sqlite.JDBC"); + } catch (ClassNotFoundException ex) { + System.err.println(ex.getMessage()); + throw new RuntimeException(ex); + } + System.err.println("jdbcUrl=" + Constants.JDBC_URL); + String clazz = this.getClass().getName(); + DBMigration dbMigration = DBMigration + .configure() + .dataSource(Constants.JDBC_URL) + .installedBy("bitinspector-persistence-impl-sqlite") + .name("bitinspector") + .sqlDialect("sqlite", "org.nanoboot.dbmigration.core.persistence.impl.sqlite.DBMigrationPersistenceSqliteImpl") + .sqlMigrationsClass(clazz) + .load(); + dbMigration.migrate(); + } + + +} diff --git a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SystemItemRepositoryImplSqlite.java b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SystemItemRepositoryImplSqlite.java new file mode 100644 index 0000000..4138ade --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SystemItemRepositoryImplSqlite.java @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.persistence.impl.sqlite; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.nanoboot.bitinspector.entity.SystemItem; +import org.nanoboot.bitinspector.persistence.api.SystemItemRepository; + +/** + * + * @author robertvokac + */ +public class SystemItemRepositoryImplSqlite implements SystemItemRepository { + + @Override + public String create(SystemItem systemItem) { + + StringBuilder sb = new StringBuilder(); + sb + .append("INSERT INTO ") + .append(SystemItemTable.TABLE_NAME) + .append("(") + .append(SystemItemTable.KEY).append(",") + // + .append(SystemItemTable.VALUE); + + sb.append(")") + .append(" VALUES (?,?)"); + + String sql = sb.toString(); + System.err.println(sql); + try ( + Connection connection = new SqliteConnectionFactory().createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { + int i = 0; + stmt.setString(++i, systemItem.getKey()); + stmt.setString(++i, systemItem.getValue()); + + // + stmt.execute(); + System.out.println(stmt.toString()); + + return systemItem.getKey(); + + } catch (SQLException e) { + System.out.println(e.getMessage()); + throw new RuntimeException(e); + } catch (ClassNotFoundException ex) { + Logger.getLogger(SystemItemRepositoryImplSqlite.class.getName()).log(Level.SEVERE, null, ex); + } + System.err.println("Error."); + return ""; + } + + @Override + public List list() { + throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + } + + @Override + public SystemItem read(String key) { + if (key == null) { + throw new RuntimeException("key is null"); + } + StringBuilder sb = new StringBuilder(); + sb + .append("SELECT * FROM ") + .append(SystemItemTable.TABLE_NAME) + .append(" WHERE ") + .append(SystemItemTable.KEY) + .append("=?"); + + String sql = sb.toString(); + int i = 0; + ResultSet rs = null; + try ( + Connection connection = new SqliteConnectionFactory().createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { + + stmt.setString(++i, key); + + rs = stmt.executeQuery(); + + while (rs.next()) { + return extractSystemItemFromResultSet(rs); + } + return new SystemItem(key, null); + } catch (SQLException e) { + System.out.println(e.getMessage()); + throw new RuntimeException(e); + } catch (ClassNotFoundException ex) { + Logger.getLogger(SystemItemRepositoryImplSqlite.class.getName()).log(Level.SEVERE, null, ex); + } finally { + try { + if (rs != null) { + rs.close(); + } + } catch (SQLException ex) { + Logger.getLogger(SystemItemRepositoryImplSqlite.class.getName()).log(Level.SEVERE, null, ex); + } + } + return null; + } + + @Override + public void remove(String key) { + throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + } + + @Override + public void update(SystemItem systemItem) { + throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + } + + private SystemItem extractSystemItemFromResultSet(final ResultSet rs) throws SQLException { + return new SystemItem( + rs.getString(SystemItemTable.KEY), + rs.getString(SystemItemTable.VALUE) + ); + } +} diff --git a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SystemItemTable.java b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SystemItemTable.java new file mode 100644 index 0000000..6fbcb3c --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SystemItemTable.java @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// bit-inspector: Tool detecting bit rots in files. +// Copyright (C) 2023-2023 the original author or authors. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 +// of the License only. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +/////////////////////////////////////////////////////////////////////////////////////////////// +package org.nanoboot.bitinspector.persistence.impl.sqlite; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +/** + * + * @author robertvokac + */ +@Getter +@Setter +@AllArgsConstructor +class SystemItemTable { + public static final String TABLE_NAME = "SYSTEM_ITEM"; + + public static final String KEY = "KEY"; + public static final String VALUE = "VALUE"; + +} diff --git a/src/main/resources/.gitkeep b/src/main/resources/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/db_migrations/sqlite/bitinspector/V000001__create_table_file.sql b/src/main/resources/db_migrations/sqlite/bitinspector/V000001__create_table_file.sql new file mode 100644 index 0000000..f8e41f5 --- /dev/null +++ b/src/main/resources/db_migrations/sqlite/bitinspector/V000001__create_table_file.sql @@ -0,0 +1,14 @@ +CREATE TABLE "FILE" ( + "ID" TEXT, + "NAME" TEXT NOT NULL, + "ABSOLUTE_PATH" TEXT NOT NULL, + "LAST_MODIFICATION_DATE" TEXT NOT NULL, + "LAST_CHECK_DATE" TEXT NOT NULL, + "HASH_SUM_VALUE" TEXT NOT NULL, + "HASH_SUM_ALGORITHM" TEXT NOT NULL, + UNIQUE(ABSOLUTE_PATH), + PRIMARY KEY("ID") +); + +CREATE UNIQUE INDEX IDX_FILE_ABSOLUTE_PATH +ON FILE (ABSOLUTE_PATH); \ No newline at end of file diff --git a/src/main/resources/db_migrations/sqlite/bitinspector/V000002__create_table_system_item.sql b/src/main/resources/db_migrations/sqlite/bitinspector/V000002__create_table_system_item.sql new file mode 100644 index 0000000..2802975 --- /dev/null +++ b/src/main/resources/db_migrations/sqlite/bitinspector/V000002__create_table_system_item.sql @@ -0,0 +1,6 @@ +CREATE TABLE "SYSTEM_ITEM" ( + "KEY" TEXT, + "VALUE" TEXT NOT NULL, + PRIMARY KEY("KEY") +) + diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties new file mode 100644 index 0000000..2939b9d --- /dev/null +++ b/src/main/resources/log4j2.properties @@ -0,0 +1,5 @@ +appender.console.type = Console +appender.console.name = STDOUT +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n +rootLogger=info, STDOUT diff --git a/src/test/java/org/nanoboot/bitinspector/.gitkeep b/src/test/java/org/nanoboot/bitinspector/.gitkeep new file mode 100644 index 0000000..e69de29