diff --git a/.gitignore b/.gitignore index 4aef104..3d5fb8b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ #*.class /target/ +/nbproject/ diff --git a/pom.xml b/pom.xml index 0b5e398..ca12fa9 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ org.nanoboot.essential nanoboot-parent - 0.1.0-SNAPSHOT + 0.1.1-SNAPSHOT org.nanoboot.tools @@ -42,7 +42,7 @@ 2.0.1-SNAPSHOT 19 19 - 0.1.0 + 0.1.1-SNAPSHOT @@ -158,11 +158,24 @@ - junit - junit - ${junit4.version} + org.junit.jupiter + junit-jupiter-engine + ${junit-jupiter.version} test + + org.junit.jupiter + junit-jupiter-api + ${junit-jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit-jupiter.version} + test + + org.nanoboot.tools.dbmigration db-migration-core @@ -202,6 +215,11 @@ log4j-slf4j-impl ${log4j.version} + + dev.mccue + guava-io + 0.0.3 + diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index e2bf9d3..02c269b 100755 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -25,4 +25,5 @@ module bitinspector { requires java.sql; requires powerframework.time; requires powerframework.collections; + requires dev.mccue.guava.io; } diff --git a/src/main/java/org/nanoboot/bitinspector/commands/BirIgnoreRegex.java b/src/main/java/org/nanoboot/bitinspector/commands/BirIgnoreRegex.java index 36ba20d..e91b0e7 100644 --- a/src/main/java/org/nanoboot/bitinspector/commands/BirIgnoreRegex.java +++ b/src/main/java/org/nanoboot/bitinspector/commands/BirIgnoreRegex.java @@ -27,30 +27,41 @@ import org.nanoboot.bitinspector.core.Utils; /** * - * @author robertvokac + * @author Robert Vokac */ public class BirIgnoreRegex implements Predicate { - + private final List patterns = new ArrayList<>(); - + public BirIgnoreRegex(File birIgnoreFile) { + + patterns.add(convertUnixRegexToJavaRegex("*.birreport.csv")); + addBirIgnoreFile(birIgnoreFile); + + } + + public final void addBirIgnoreFile(File birIgnoreFile) { + addBirIgnoreFile(birIgnoreFile, null); + } + public final void addBirIgnoreFile(File birIgnoreFile, File workingDir) { String[] lines = birIgnoreFile.exists() ? Utils.readTextFromFile(birIgnoreFile).split("\\R") : new String[]{}; - if (lines.length == 0) { - //nothing to do - return; - } + String addPrefix = workingDir == null ? "" : birIgnoreFile.getParentFile().getAbsolutePath().replace(workingDir.getAbsolutePath() + "/", ""); + for (String l : lines) { if (l.isBlank() || l.trim().startsWith("#")) { //nothing to do continue; } - patterns.add(convertUnixRegexToJavaRegex(l)); + if(addPrefix == null) { + patterns.add(convertUnixRegexToJavaRegex(l)); + } else { + patterns.add(convertUnixRegexToJavaRegex(addPrefix + l)); + patterns.forEach(e->System.out.println("$$$" + e)); + } + } - patterns.add(convertUnixRegexToJavaRegex("*.birreport.csv")); - - } - + @Override public boolean test(String text) { if (patterns.isEmpty()) { @@ -63,7 +74,7 @@ public class BirIgnoreRegex implements Predicate { if (b) { ignore = true; } else { - + } } // if (ignore) { @@ -73,7 +84,7 @@ public class BirIgnoreRegex implements Predicate { // } return ignore; } - + public static String convertUnixRegexToJavaRegex(String wildcard) { StringBuffer s = new StringBuffer(wildcard.length()); s.append('^'); @@ -109,5 +120,5 @@ public class BirIgnoreRegex implements Predicate { 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 index 02ee619..15c6abb 100644 --- a/src/main/java/org/nanoboot/bitinspector/commands/CheckCommand.java +++ b/src/main/java/org/nanoboot/bitinspector/commands/CheckCommand.java @@ -22,225 +22,195 @@ 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.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.nanoboot.bitinspector.core.BirContext; import org.nanoboot.bitinspector.core.Command; -import org.nanoboot.bitinspector.core.BitInspectorArgs; +import org.nanoboot.bitinspector.core.BirArgs; import org.nanoboot.bitinspector.core.BitInspectorException; +import org.nanoboot.bitinspector.core.BirFiles; +import org.nanoboot.bitinspector.core.ListSet; 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; +import org.nanoboot.dbmigration.core.main.MigrationResult; +import org.nanoboot.powerframework.time.duration.Duration; +import org.nanoboot.powerframework.time.moment.LocalDateTime; +import org.nanoboot.powerframework.time.utils.RemainingTimeCalculator; +import org.nanoboot.powerframework.time.utils.TimeUnit; /** * - * @author pc00289 + * @author r */ 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); + private static final Logger LOG = LogManager.getLogger(CheckCommand.class); + public static final String NAME = "check"; public CheckCommand() { } @Override public String getName() { - return "check"; + return NAME; } - static int i = 0; + enum CheckCommandPart { + + CHECK_OLD_DB_CHECKSUM(1), + MIGRATE_DB_SCHEMA_IF_NEEDED(2), + UPDATE_VERSION(3), + FOUND_FILES_IN_FILESYSTEM(4), + FOUND_FILES_IN_DB(5), + ADD_NEW_FILES_TO_DB(6), + REMOVE_DELETED_FILES_FROM_DB(7), + COMPARE_CONTENT_AND_LAST_MODTIME(8), + CREATE_REPORT_CSV_IF_NEEDED(9), + CHECK_NEW_DB_CHECKSUM(10); + + private int number; + + CheckCommandPart(int number) { + this.number = number; + } + + public String toText() { + return "Part " + number + ": "; + } + } + + static int iStatic = 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")); + public String run(BirArgs birArgs) { + BirFiles birFiles = new BirFiles(birArgs); + BirContext birContext = new BirContext(birFiles.getWorkingDirAbsolutePath()); + // + //part 1: + part1CheckDbHasExpectedHashSum(birFiles); + //part 2: + boolean part2Result = part2MigrateDbSchemaIfNeeded(birFiles); + if(!part2Result) { + return "part 2 failed"; } - System.out.println("Updating version in DB."); - version = systemItemRepository.read("bir.version").getValue(); - System.out.println("bir.version=" + version); - //// + //part 3: + part3UpdateVersionInDbIfNeeded(birContext); - //// 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 + ListSet filesInFileSystem = part4FoundFilesInFileSystem(birFiles, birArgs); + ListSet filesInDb = part5FoundFilesInDb(birContext.getFileRepository(), birArgs); - 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)))); + LocalDateTime now = part6AddNewFilesToDb(filesInFileSystem, birFiles, filesInDb, birContext); - //// 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()))); + List filesToBeRemovedFromDb = part7RemoveDeletedFilesFromDb(filesInDb, filesInFileSystem, birContext); - //// Add new files - Date lastChecked = new Date(); - org.nanoboot.powerframework.time.moment.LocalDateTime now = org.nanoboot.powerframework.time.moment.LocalDateTime.convertJavaUtilDateToPowerLocalDateTime(lastChecked); + List filesWithBitRot = part8CompareContentAndLastModificationDate(filesInDb, filesToBeRemovedFromDb, birContext, now); - int processedCount0 = 0; + part9CreateReportCsvIfNeeded(birArgs, birFiles, filesWithBitRot); + part10CalculateCurrentHashSumOfDbFile(birFiles); - 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"); + LOG.info("=========="); + LOG.info("Summary"); 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"); - } + LOG.info("Summary: OK : No files with bit rot were found."); + } else { + LOG.error("Summary: KO : Some files {} with bit rot were found.", filesWithBitRot.size()); filesWithBitRot.stream().forEach(f - -> sb.append(f.getAbsolutePath()) - .append(";") - .append(f.getHashSumValue()) - .append(";") - .append(Utils.calculateSHA512Hash(new File("./" + f.getAbsolutePath()))) - .append("\n") + -> LOG.error("Bit rot detected: \"" + f.getAbsolutePath() + "\"" + " expected_sha512=" + f.getHashSumValue() + " returned_sha512=" + Utils.calculateSHA512Hash(new File("./" + f.getAbsolutePath()))) ); - 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); + return filesWithBitRot.isEmpty() ? "" : filesWithBitRot.stream().map(FsFile::getAbsolutePath).collect(Collectors.joining("\n")); } + /** + * Checks, if SQLite DB file has the expected SHA-512 hash sum + * + * @param birSQLite3File + * @param birSQLite3FileSha512 + * @throws BitInspectorException - if this check fails. + */ + private void part1CheckDbHasExpectedHashSum(BirFiles birInspectorFiles) throws BitInspectorException { + LOG.info("** Part {}: Checking DB, if has expected check sum.", CheckCommandPart.CHECK_OLD_DB_CHECKSUM.number); + final boolean dbExists = birInspectorFiles.getBirSQLite3File().exists(); + final boolean checkSumExists = birInspectorFiles.getBirSQLite3FileSha512().exists(); + if (dbExists && checkSumExists) { + String expectedHash = Utils.readTextFromFile(birInspectorFiles.getBirSQLite3FileSha512()); + String returnedHash = Utils.calculateSHA512Hash(birInspectorFiles.getBirSQLite3File()); + if (!returnedHash.equals(expectedHash)) { + String msg + = "Part {}: KO. " + + "Unexpected hash " + + returnedHash + + ". Expected SHA-512 hash sum was: " + + expectedHash + + " for file " + + birInspectorFiles.getBirSQLite3File().getAbsolutePath(); + LOG.error(msg, CheckCommandPart.CHECK_OLD_DB_CHECKSUM.number); + LOG.info("Exiting because of the previous error."); + throw new BitInspectorException(msg); + } + } else { + LOG.info("Part {}: OK. Nothing to do: {}", + CheckCommandPart.CHECK_OLD_DB_CHECKSUM.number, + !dbExists ? "DB does not yet exist." : "Check sum file does not exist."); + } + } + + private boolean part2MigrateDbSchemaIfNeeded(BirFiles birFiles) { + LOG.info("** Part {}: Migrating schema, if needed.", CheckCommandPart.MIGRATE_DB_SCHEMA_IF_NEEDED.number); + try { + + MigrationResult migrationResult = SqliteDatabaseMigration.getInstance().migrate(birFiles.getWorkingDirAbsolutePath()); + if (migrationResult == MigrationResult.SUCCESS) { + LOG.info("Part {}: OK. Success.", CheckCommandPart.MIGRATE_DB_SCHEMA_IF_NEEDED.number); + return true; + } else { + LOG.error("Part {}: KO. Failed.", CheckCommandPart.MIGRATE_DB_SCHEMA_IF_NEEDED.number); + throw new RuntimeException("Part " + CheckCommandPart.MIGRATE_DB_SCHEMA_IF_NEEDED.number + ": KO. Failed."); + } + } catch (Exception e) { + LOG.error("Part {}: KO. {}", CheckCommandPart.MIGRATE_DB_SCHEMA_IF_NEEDED.number, e.getMessage()); + return false; + } + } + + private void part3UpdateVersionInDbIfNeeded(BirContext birContext) { + LOG.info("** Part {}: Updating version, if needed.", CheckCommandPart.UPDATE_VERSION.number); + String version = birContext.getSystemItemRepository().read("bir.version").getValue(); + System.out.println("Before: bir.version=" + version); + if (version == null) { + birContext.getSystemItemRepository().create(new SystemItem("bir.version", "0.0.0-SNAPSHOT")); + } + System.out.println("Updating version in DB."); + version = birContext.getSystemItemRepository().read("bir.version").getValue(); + System.out.println("After: bir.version=" + version); + LOG.info("Part {}: OK.", CheckCommandPart.UPDATE_VERSION.number); + } + + private ListSet part4FoundFilesInFileSystem(BirFiles birFiles, BirArgs birArgs) { + LOG.info("** Part {}: Loading files in filesystem", CheckCommandPart.FOUND_FILES_IN_FILESYSTEM.number); + String workingDir = birFiles.getWorkingDirAbsolutePath(); + List filesAlreadyFound = new ArrayList<>(); + List filesInDirList = foundFilesInCurrentDir(birFiles.getWorkingDir(), filesAlreadyFound, birFiles); + + ListSet listSet = new ListSet<>(filesInDirList, f -> loadPathButOnlyTheNeededPart(birFiles.getWorkingDir(), f)); + + LOG.info("Part {}: Found {} files.", CheckCommandPart.FOUND_FILES_IN_FILESYSTEM.number, listSet.size()); + if (birArgs.isVerboseLoggingEnabled()) { + filesInDirList.stream().forEach((f -> LOG.info("#" + (++iStatic) + " " + f.getAbsolutePath().substring(workingDir.length() + 1)))); + } + return listSet; + } + private String loadPathButOnlyTheNeededPart(File currentDir, File file) { return file.getAbsolutePath().substring(currentDir.getAbsolutePath().length() + 1); } @@ -249,32 +219,244 @@ public class CheckCommand implements Command { private int foundFiles; private int foundDirs; - private List foundFilesInCurrentDir(File currentDir, List files) { + private List foundFilesInCurrentDir(File currentDir, List filesAlreadyFound, BirFiles birFiles) { for (File f : currentDir.listFiles()) { + boolean isAlsoBirIgnore =f.getName().equals(birFiles.getBirIgnore().getName()); + if(isAlsoBirIgnore && !f.getAbsolutePath().equals(birFiles.getBirIgnore().getAbsoluteFile())) { + birFiles.getBirIgnoreRegex().addBirIgnoreFile(f, birFiles.getWorkingDir()); + } if (f.isDirectory()) { ++foundDirs; - foundFilesInCurrentDir(f, files); + foundFilesInCurrentDir(f, filesAlreadyFound, birFiles); } else { ++foundFiles; - if (f.getAbsolutePath().equals(birSQLite3File.getAbsolutePath())) { + if (f.getAbsolutePath().equals(birFiles.getBirSQLite3File().getAbsolutePath())) { continue; } - if (f.getAbsolutePath().equals(birSQLite3FileSha512.getAbsolutePath())) { - continue; - } - if (f.getAbsolutePath().equals(birIgnore.getAbsolutePath())) { + if (f.getAbsolutePath().equals(birFiles.getBirSQLite3FileSha512().getAbsolutePath())) { continue; } + ++iii; //System.out.println("Testing file: " + iii + "#" + " " + loadPathButOnlyTheNeededPart(currentDirRoot, f)); - if (birIgnoreRegex.test(loadPathButOnlyTheNeededPart(currentDirRoot, f))) { + if (birFiles.getBirIgnoreRegex().test(loadPathButOnlyTheNeededPart(birFiles.getWorkingDir(), f))) { continue; } - files.add(f); + filesAlreadyFound.add(f); } } - return files; + return filesAlreadyFound; + } + + private ListSet part5FoundFilesInDb(FileRepository fileRepository, BirArgs birArgs) { + LOG.info("** Part {}: Loading files in DB", CheckCommandPart.FOUND_FILES_IN_DB.number); + List filesInDb = fileRepository.list(); + + ListSet listSet = new ListSet<>(filesInDb, f -> f.getAbsolutePath()); + LOG.info("Part {}: Found {} files.", CheckCommandPart.FOUND_FILES_IN_DB.number, listSet.size()); + iStatic = 0; + if (birArgs.isVerboseLoggingEnabled()) { + filesInDb.stream().forEach((f -> System.out.println("#" + (++iStatic) + " " + f.toString()))); + } + return listSet; + } + + private LocalDateTime part6AddNewFilesToDb(ListSet filesInFileSystem, BirFiles birFiles, ListSet filesInDb, BirContext birContext) { + LOG.info("** Part {}: Adding new files to DB", CheckCommandPart.ADD_NEW_FILES_TO_DB.number); + 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 : filesInFileSystem.getList()) { + processedCount0 = processedCount0 + 1; + if (processedCount0 % 100 == 0) { + double progress = ((double) processedCount0) / filesInFileSystem.getList().size() * 100; + LOG.info("Part {}: Add - Progress: {}/{} {} %", + CheckCommandPart.ADD_NEW_FILES_TO_DB.number, + processedCount0, + filesInFileSystem.getList().size(), + String.format("%,.2f", progress)); + } + + String absolutePathOfFileInDir = loadPathButOnlyTheNeededPart(birFiles.getWorkingDir(), fileInDir); + if (!filesInDb.doesSetContains(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", + fileInDir.length(), + "OK" + ); + filesMissingInDb.add(fsFile); + } + + } + LOG.info("Adding new files: {}", filesMissingInDb.size()); + birContext.getFileRepository().create(filesMissingInDb); + return now; + } + + private List part7RemoveDeletedFilesFromDb(ListSet filesInDb, ListSet filesInFileSystem, BirContext birContext) { + LOG.info("** Part {}: Removing deleted files from DB", CheckCommandPart.REMOVE_DELETED_FILES_FROM_DB.number); + List filesToBeRemovedFromDb = new ArrayList<>(); + int processedCount = 0; + + for (FsFile fileInDb : filesInDb.getList()) { + processedCount = processedCount + 1; + if (processedCount % 100 == 0) { + double progress = ((double) processedCount) / filesInDb.getList().size() * 100; + LOG.info( + "Part {}: Remove - Progress: {}/{} {}%", + CheckCommandPart.REMOVE_DELETED_FILES_FROM_DB.number, + processedCount, + filesInDb.getList().size(), + String.format("%,.2f", progress) + ); + } + + String absolutePathOfFileInDb = fileInDb.getAbsolutePath(); + if (!filesInFileSystem.doesSetContains(absolutePathOfFileInDb)) { + + filesToBeRemovedFromDb.add(fileInDb); + } + + } + LOG.info("Part {}: Removing files: {}", + CheckCommandPart.REMOVE_DELETED_FILES_FROM_DB.number, + filesToBeRemovedFromDb.size()); + for (FsFile f : filesToBeRemovedFromDb) { + birContext.getFileRepository().remove(f); + } + return filesToBeRemovedFromDb; + } + + private List part8CompareContentAndLastModificationDate( + ListSet filesInDb, List filesToBeRemovedFromDb, BirContext birContext, LocalDateTime now) { + LOG.info("** Part {}: Comparing Content and last modification date", CheckCommandPart.COMPARE_CONTENT_AND_LAST_MODTIME.number); + 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<>(); + int contentAndModTimeWereChanged = 0; + + RemainingTimeCalculator rtc = new RemainingTimeCalculator(filesInDb.size() - filesToBeRemovedFromDb.size()); + for (FsFile fileInDb : filesInDb) { + String absolutePathOfFileInDb = fileInDb.getAbsolutePath(); + if (filesToBeRemovedFromDb.contains(fileInDb)) { + //nothing to do + continue; + + } + rtc.nextDone(); + processedCount = processedCount + 1; + if (processedCount % 100 == 0) { + double progress = ((double) processedCount) / countOfFilesToCalculateHashSum * 100; + LOG.info("Update - Progress: " + processedCount + "/" + countOfFilesToCalculateHashSum + " " + String.format("%,.2f", progress) + "%"); + LOG.info("Remains: " + Duration.of(rtc.remainingSecondsUntilEnd(), TimeUnit.SECOND).toString()); + } + 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()); + fileInDb.setLastCheckResult("KO"); + birContext.getFileRepository().updateFile(fileInDb); + continue; + } + if (!ldt.toString().equals(fileInDb.getLastModificationDate())) { + fileInDb.setLastCheckDate(now.toString()); + fileInDb.setLastModificationDate(ldt.toString()); + fileInDb.setHashSumValue(calculatedHash); + fileInDb.setHashSumAlgorithm("SHA-512"); + fileInDb.setSize(file.length()); + fileInDb.setLastCheckResult("OK"); + birContext.getFileRepository().updateFile(fileInDb); + //System.out.println(fileInDb.toString()); + contentAndModTimeWereChanged++; + continue; + } + if (ldt.toString().equals(fileInDb.getLastModificationDate())) { + fileInDb.setLastCheckResult("OK"); + + if (fileInDb.getSize() == 0) { + fileInDb.setSize(file.length()); + birContext.getFileRepository().updateFile(fileInDb); + } else { + filesToUpdateLastCheckDate.add(fileInDb); + } + continue; + } + + } + LOG.info("Part {}: Updating files - found bit rots - content was changed and last modification is the same): {}", + CheckCommandPart.COMPARE_CONTENT_AND_LAST_MODTIME.number, + filesWithBitRot.size()); + LOG.info("Part {}: Updating files - content and last modification date were changed): {}", + CheckCommandPart.COMPARE_CONTENT_AND_LAST_MODTIME.number, + contentAndModTimeWereChanged); + LOG.info("Part {}: Updating files - content and last modification date were not changed): {}", + CheckCommandPart.COMPARE_CONTENT_AND_LAST_MODTIME.number, + filesToUpdateLastCheckDate.size()); + birContext.getFileRepository().updateLastCheckDate(now.toString(), filesToUpdateLastCheckDate); + + return filesWithBitRot; + } + + private void part9CreateReportCsvIfNeeded(BirArgs birArgs, BirFiles birFiles, List filesWithBitRot) { + LOG.info("** Part {}: Creating csv report, if needed", CheckCommandPart.CREATE_REPORT_CSV_IF_NEEDED.number); + if (!birArgs.hasArgument("report")) { + LOG.info(" Part {}: OK. Nothing to do. No option report was passed.", CheckCommandPart.CREATE_REPORT_CSV_IF_NEEDED.number); + return; + } + if (!birArgs.getArgument("report").equals("true")) { + LOG.info("Part {}: Nothing to do. Option report={}", + CheckCommandPart.CREATE_REPORT_CSV_IF_NEEDED.number, + birArgs.getArgument("report")); + return; + } + + File birReportCsv = birFiles.getBirReportCsv(); + if (birReportCsv.exists()) { + Long nowLong = org.nanoboot.powerframework.time.moment.UniversalDateTime.now().toLong(); + + File backup = new File(birReportCsv.getParentFile().getAbsolutePath() + "/" + nowLong + "." + birReportCsv.getName()); + birReportCsv.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(), birReportCsv); + LOG.info("Part {}: OK.", + CheckCommandPart.CREATE_REPORT_CSV_IF_NEEDED.number); + } + + private void part10CalculateCurrentHashSumOfDbFile(BirFiles birFiles) { + LOG.info("** Part {}: Calculating current hash sum of DB file", CheckCommandPart.CHECK_NEW_DB_CHECKSUM.number); + Utils.writeTextToFile(Utils.calculateSHA512Hash(birFiles.getBirSQLite3File()), birFiles.getBirSQLite3FileSha512()); } } diff --git a/src/main/java/org/nanoboot/bitinspector/commands/GenCommand.java.original~ b/src/main/java/org/nanoboot/bitinspector/commands/GenCommand.java.original~ deleted file mode 100644 index 80f7bcf..0000000 --- a/src/main/java/org/nanoboot/bitinspector/commands/GenCommand.java.original~ +++ /dev/null @@ -1,304 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////////////////////// -// 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 index 7edadba..a02eb19 100644 --- a/src/main/java/org/nanoboot/bitinspector/commands/HelpCommand.java +++ b/src/main/java/org/nanoboot/bitinspector/commands/HelpCommand.java @@ -19,24 +19,26 @@ package org.nanoboot.bitinspector.commands; import org.nanoboot.bitinspector.core.Command; -import org.nanoboot.bitinspector.core.BitInspectorArgs; +import org.nanoboot.bitinspector.core.BirArgs; /** * - * @author pc00289 + * @author Robert Vokac */ public class HelpCommand implements Command { + public static final String NAME = "help"; + public HelpCommand() { } @Override public String getName() { - return "help"; + return NAME; } @Override - public void run(BitInspectorArgs bitInspectorArgs) { + public String run(BirArgs bitInspectorArgs) { String str = """ NAME bir - " Bit Inspector" @@ -51,13 +53,15 @@ public class HelpCommand implements Command { 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). + dir={working directory to be checked for bit rot} + Optional. Default=. (current working directory) + report=true or false + Optional. Default= false (nothing will be reported to file .birreport.csv). help Display help information version Display version information """; System.out.println(str); - + return str; } } diff --git a/src/main/java/org/nanoboot/bitinspector/commands/VersionCommand.java b/src/main/java/org/nanoboot/bitinspector/commands/VersionCommand.java index 99f9c9d..b3f9cc7 100644 --- a/src/main/java/org/nanoboot/bitinspector/commands/VersionCommand.java +++ b/src/main/java/org/nanoboot/bitinspector/commands/VersionCommand.java @@ -19,26 +19,32 @@ package org.nanoboot.bitinspector.commands; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.nanoboot.bitinspector.core.Command; -import org.nanoboot.bitinspector.core.BitInspectorArgs; +import org.nanoboot.bitinspector.core.BirArgs; /** * - * @author pc00289 +* @author Robert Vokac */ public class VersionCommand implements Command { - + +public static final String NAME = "version"; +private static final Logger LOG = LogManager.getLogger(VersionCommand.class); public VersionCommand() { } @Override public String getName() { - return "version"; + return NAME; } @Override - public void run(BitInspectorArgs bitInspectorArgs) { - System.out.println("Bit Inspector 0.0.0-SNAPSHOT"); + public String run(BirArgs bitInspectorArgs) { + String result = "Bit Inspector 0.0.0-SNAPSHOT"; + LOG.info(result); + return result; } } diff --git a/src/main/java/org/nanoboot/bitinspector/core/BitInspectorArgs.java b/src/main/java/org/nanoboot/bitinspector/core/BirArgs.java similarity index 68% rename from src/main/java/org/nanoboot/bitinspector/core/BitInspectorArgs.java rename to src/main/java/org/nanoboot/bitinspector/core/BirArgs.java index b61b258..97669b1 100644 --- a/src/main/java/org/nanoboot/bitinspector/core/BitInspectorArgs.java +++ b/src/main/java/org/nanoboot/bitinspector/core/BirArgs.java @@ -16,7 +16,6 @@ // 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; @@ -25,15 +24,33 @@ import lombok.Getter; /** * - * @author pc00289 + * @author Robert Vokac */ -public class BitInspectorArgs { +public class BirArgs { @Getter private final String command; private final Map internalMap = new HashMap<>(); - public BitInspectorArgs(String[] args) { + private static String[] convertToStringArray(String command, Map map) { + String[] array = new String[1 + map.size()]; + array[0] = command; + int i = 0; + for (String key : map.keySet()) { + array[++i] = key + "=" + map.get(key); + } + return array; + } + + public BirArgs(BitInspectorCommand command, Map map) { + this(convertToStringArray(command.name().toLowerCase(), map)); + } + + public BirArgs(String command, Map map) { + this(convertToStringArray(command, map)); + } + + public BirArgs(String[] args) { command = args.length == 0 ? "check" : args[0]; if (args.length > 1) { @@ -54,15 +71,20 @@ public class BitInspectorArgs { } } + public boolean hasArgument(String arg) { return internalMap.containsKey(arg); } + public void addArgument(String arg, String value) { - this.internalMap.put(arg,value); + this.internalMap.put(arg, value); } public String getArgument(String arg) { return internalMap.get(arg); } + public boolean isVerboseLoggingEnabled() { + return hasArgument("verbose")&&getArgument("verbose").equals("true"); + } } diff --git a/src/main/java/org/nanoboot/bitinspector/core/BirContext.java b/src/main/java/org/nanoboot/bitinspector/core/BirContext.java new file mode 100644 index 0000000..fd186e6 --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/core/BirContext.java @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// 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 lombok.Getter; +import org.nanoboot.bitinspector.persistence.api.ConnectionFactory; +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.SqliteConnectionFactory; +import org.nanoboot.bitinspector.persistence.impl.sqlite.SystemItemRepositoryImplSqlite; + +/** + * +* @author Robert Vokac + */ +public class BirContext { + private final String directoryWhereSqliteFileIs; + private ConnectionFactory connectionFactory; + @Getter + private SystemItemRepository systemItemRepository; + @Getter + private FileRepository fileRepository; + + public BirContext(String directoryWhereSqliteFileIs) { + this.directoryWhereSqliteFileIs = directoryWhereSqliteFileIs; + this.connectionFactory = new SqliteConnectionFactory(directoryWhereSqliteFileIs); + systemItemRepository = new SystemItemRepositoryImplSqlite((SqliteConnectionFactory) connectionFactory); + fileRepository = new FileRepositoryImplSqlite((SqliteConnectionFactory) connectionFactory); + } + +} diff --git a/src/main/java/org/nanoboot/bitinspector/core/BirFiles.java b/src/main/java/org/nanoboot/bitinspector/core/BirFiles.java new file mode 100644 index 0000000..4981a5c --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/core/BirFiles.java @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// 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.File; +import lombok.Data; +import lombok.Getter; +import org.nanoboot.bitinspector.commands.BirIgnoreRegex; + +/** + * + * @author Robert Vokac + */ +@Data +@Getter +public class BirFiles { + + private final File workingDir; + private final String workingDirAbsolutePath; + private final File birSQLite3File; + private final File birSQLite3FileSha512; + private final File birIgnore; + private final BirIgnoreRegex birIgnoreRegex; + private final File birReportCsv; + + + public BirFiles(BirArgs bitInspectorArgs) { + workingDir = new File(bitInspectorArgs.hasArgument("dir") ? bitInspectorArgs.getArgument("dir") : "."); + workingDirAbsolutePath = workingDir.getAbsolutePath(); + birSQLite3File = new File(workingDirAbsolutePath + "/.bir.sqlite3"); + birSQLite3FileSha512 = new File(workingDirAbsolutePath + "/.bir.sqlite3.sha512"); + birIgnore = new File(workingDirAbsolutePath + "/.birignore"); + birIgnoreRegex = new BirIgnoreRegex(birIgnore); + birReportCsv = new File(workingDirAbsolutePath + "/.birreport.csv"); + } +} diff --git a/src/main/java/org/nanoboot/bitinspector/core/BitInspector.java b/src/main/java/org/nanoboot/bitinspector/core/BitInspector.java new file mode 100644 index 0000000..771570f --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/core/BitInspector.java @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// 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.HashSet; +import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.nanoboot.bitinspector.commands.CheckCommand; +import org.nanoboot.bitinspector.commands.HelpCommand; +import org.nanoboot.bitinspector.commands.VersionCommand; + +/** + * + * @author Robert Vokac + */ +public class BitInspector { + + private static final Logger LOG = LogManager.getLogger(BitInspector.class); + + private final Set commandImplementations; + public BitInspector() { + commandImplementations = new HashSet<>(); + commandImplementations.add(new CheckCommand()); + commandImplementations.add(new HelpCommand()); + commandImplementations.add(new VersionCommand()); + } + + public void run(String[] args) { + run(new BirArgs(args)); + } + + public void run(BirArgs bitInspectorArgs) { + String command = bitInspectorArgs.getCommand(); + Command foundCommand = null; + for(Command e:commandImplementations) { + if(e.getName().equals(command)) { + foundCommand = e; + break; + } + } + if(foundCommand == null) { + String msg = "Command \"" + command + "\" is not supported."; + LOG.error(msg); + + new HelpCommand().run(bitInspectorArgs); + throw new BitInspectorException(msg); + } + foundCommand.run(bitInspectorArgs); + + } +} diff --git a/src/main/java/org/nanoboot/bitinspector/core/BitInspectorCommand.java b/src/main/java/org/nanoboot/bitinspector/core/BitInspectorCommand.java new file mode 100644 index 0000000..806aa1d --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/core/BitInspectorCommand.java @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// 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 + */ +public enum BitInspectorCommand { + CHECK, HELP, VERSION; +} diff --git a/src/main/java/org/nanoboot/bitinspector/core/Command.java b/src/main/java/org/nanoboot/bitinspector/core/Command.java index bf7d017..30d0f71 100644 --- a/src/main/java/org/nanoboot/bitinspector/core/Command.java +++ b/src/main/java/org/nanoboot/bitinspector/core/Command.java @@ -21,11 +21,11 @@ package org.nanoboot.bitinspector.core; /** * - * @author pc00289 +* @author Robert Vokac */ public interface Command { public String getName(); - default void run(BitInspectorArgs bitInspectorArgs) { + default String run(BirArgs 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 index 1177ce0..8292f6c 100644 --- a/src/main/java/org/nanoboot/bitinspector/core/Constants.java +++ b/src/main/java/org/nanoboot/bitinspector/core/Constants.java @@ -23,7 +23,7 @@ import java.text.SimpleDateFormat; /** * - * @author pc00289 +* @author Robert Vokac */ public class Constants { diff --git a/src/main/java/org/nanoboot/bitinspector/core/ListSet.java b/src/main/java/org/nanoboot/bitinspector/core/ListSet.java new file mode 100644 index 0000000..3b7432f --- /dev/null +++ b/src/main/java/org/nanoboot/bitinspector/core/ListSet.java @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// 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.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import lombok.Getter; + +/** + * + * @author robertvokac + */ +public class ListSet implements Iterable { + @Getter + private final List list; + @Getter + private final Set set; + + public ListSet(List list, Function mapper) { + this.list = Collections.unmodifiableList(list); + this.set = Collections.unmodifiableSet(list.stream().map(mapper).collect(Collectors.toSet())); + } + public boolean doesSetContains(String s) { + return set.contains(s); + } + public int size() { + return list.size(); + } + + @Override + public Iterator iterator() { + return list.iterator(); + } + + +} diff --git a/src/main/java/org/nanoboot/bitinspector/core/Main.java b/src/main/java/org/nanoboot/bitinspector/core/Main.java index b3454f2..1c58f6b 100755 --- a/src/main/java/org/nanoboot/bitinspector/core/Main.java +++ b/src/main/java/org/nanoboot/bitinspector/core/Main.java @@ -18,12 +18,6 @@ /////////////////////////////////////////////////////////////////////////////////////////////// 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 @@ -32,28 +26,9 @@ 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); + + BitInspector bitInspector = new BitInspector(); + bitInspector.run(args); } diff --git a/src/main/java/org/nanoboot/bitinspector/core/Utils.java b/src/main/java/org/nanoboot/bitinspector/core/Utils.java index a2d045e..ae7d558 100644 --- a/src/main/java/org/nanoboot/bitinspector/core/Utils.java +++ b/src/main/java/org/nanoboot/bitinspector/core/Utils.java @@ -16,8 +16,11 @@ // 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 dev.mccue.guava.hash.Hashing; +import dev.mccue.guava.io.Files; import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; @@ -25,12 +28,10 @@ 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; @@ -38,7 +39,7 @@ import java.util.logging.Logger; /** * - * @author pc00289 + * @author Robert Vokac */ public class Utils { @@ -65,31 +66,6 @@ public class Utils { } } - 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<>()); } @@ -106,21 +82,12 @@ public class Utils { 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); + java.nio.file.Files.copy(originalPath, copied, StandardCopyOption.REPLACE_EXISTING); } catch (IOException ex) { ex.printStackTrace(); throw new BitInspectorException("Copying file failed: " + originalFile.getAbsolutePath()); @@ -145,7 +112,7 @@ public class Utils { return ""; } try { - return new String(Files.readAllBytes(Paths.get(file.getAbsolutePath()))); + return new String(java.nio.file.Files.readAllBytes(Paths.get(file.getAbsolutePath()))); } catch (IOException ex) { throw new BitInspectorException("Reading file failed: " + file.getName(), ex); } @@ -176,24 +143,36 @@ public class Utils { } public static String calculateSHA512Hash(File file) { - final Path path = Paths.get(file.getAbsolutePath()); - byte[] bytes; try { - bytes = Files.readAllBytes(path); + return Files.hash(file, Hashing.sha512()).toString(); +// final Path path = Paths.get(file.getAbsolutePath()); +// byte[] bytes; +// try { +// bytes = Files.readAllBytes(path); +// } catch (IOException ex) { +// throw new BitInspectorException(ex); +// } +// byte[] sha512sumByteArray; +// if(file.length() >= Integer.MAX_VALUE) { +// throw new RuntimeException("File is too large: " + file.getAbsolutePath()); +// } +// 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; } catch (IOException ex) { + Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, 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; + } + public static String createJdbcUrl(String directoryWhereSqliteFileIs) { + return "jdbc:sqlite:" + directoryWhereSqliteFileIs + "/" + ".bir.sqlite3?foreign_keys=on;"; } } diff --git a/src/main/java/org/nanoboot/bitinspector/entity/FsFile.java b/src/main/java/org/nanoboot/bitinspector/entity/FsFile.java index fa99f3d..d424b63 100644 --- a/src/main/java/org/nanoboot/bitinspector/entity/FsFile.java +++ b/src/main/java/org/nanoboot/bitinspector/entity/FsFile.java @@ -26,7 +26,7 @@ import lombok.ToString; /** * - * @author robertvokac +* @author Robert Vokac */ @Getter @Setter @@ -40,5 +40,7 @@ public class FsFile { private String lastCheckDate; private String hashSumValue; private String hashSumAlgorithm; + private long size; + private String lastCheckResult; } diff --git a/src/main/java/org/nanoboot/bitinspector/entity/SystemItem.java b/src/main/java/org/nanoboot/bitinspector/entity/SystemItem.java index aa32625..c89555a 100644 --- a/src/main/java/org/nanoboot/bitinspector/entity/SystemItem.java +++ b/src/main/java/org/nanoboot/bitinspector/entity/SystemItem.java @@ -25,7 +25,7 @@ import lombok.Setter; /** * - * @author robertvokac +* @author Robert Vokac */ @Getter @Setter diff --git a/src/main/java/org/nanoboot/bitinspector/commands/HelpCommand.java.original~ b/src/main/java/org/nanoboot/bitinspector/persistence/api/ConnectionFactory.java similarity index 74% rename from src/main/java/org/nanoboot/bitinspector/commands/HelpCommand.java.original~ rename to src/main/java/org/nanoboot/bitinspector/persistence/api/ConnectionFactory.java index 7fab53a..8afd03a 100644 --- a/src/main/java/org/nanoboot/bitinspector/commands/HelpCommand.java.original~ +++ b/src/main/java/org/nanoboot/bitinspector/persistence/api/ConnectionFactory.java @@ -1,38 +1,30 @@ -/////////////////////////////////////////////////////////////////////////////////////////////// -// 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"; - } - -} +/////////////////////////////////////////////////////////////////////////////////////////////// +// 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.sql.Connection; + +/** + * +* @author Robert Vokac + */ +public interface ConnectionFactory { + + Connection createConnection() throws ClassNotFoundException; +} diff --git a/src/main/java/org/nanoboot/bitinspector/persistence/api/FileRepository.java b/src/main/java/org/nanoboot/bitinspector/persistence/api/FileRepository.java index 6f1f93a..dffe51a 100644 --- a/src/main/java/org/nanoboot/bitinspector/persistence/api/FileRepository.java +++ b/src/main/java/org/nanoboot/bitinspector/persistence/api/FileRepository.java @@ -24,7 +24,7 @@ import org.nanoboot.bitinspector.entity.FsFile; /** * - * @author robertvokac +* @author Robert Vokac */ public interface FileRepository { void create(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 index c61ea18..9c8c21e 100644 --- a/src/main/java/org/nanoboot/bitinspector/persistence/api/SystemItemRepository.java +++ b/src/main/java/org/nanoboot/bitinspector/persistence/api/SystemItemRepository.java @@ -24,7 +24,7 @@ import org.nanoboot.bitinspector.entity.SystemItem; /** * - * @author robertvokac +* @author Robert Vokac */ public interface SystemItemRepository { String create(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 index 2c078b3..addc5ed 100644 --- a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/Constants.java +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/Constants.java @@ -20,10 +20,10 @@ package org.nanoboot.bitinspector.persistence.impl.sqlite; /** * - * @author robertvokac +* @author Robert Vokac */ 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 index 37a2b1b..ff9e28b 100644 --- a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/FileRepositoryImplSqlite.java +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/FileRepositoryImplSqlite.java @@ -31,10 +31,16 @@ import org.nanoboot.bitinspector.persistence.api.FileRepository; /** * - * @author robertvokac + * @author Robert Vokac */ public class FileRepositoryImplSqlite implements FileRepository { + private SqliteConnectionFactory sqliteConnectionFactory; + + public FileRepositoryImplSqlite(SqliteConnectionFactory sqliteConnectionFactory) { + this.sqliteConnectionFactory = sqliteConnectionFactory; + } + @Override public void create(List files) { if (files.isEmpty()) { @@ -69,13 +75,15 @@ public class FileRepositoryImplSqlite implements FileRepository { .append(FileTable.LAST_CHECK_DATE).append(",") // .append(FileTable.HASH_SUM_VALUE).append(",") - .append(FileTable.HASH_SUM_ALGORITHM); + .append(FileTable.HASH_SUM_ALGORITHM).append(",") + .append(FileTable.SIZE).append(",") + .append(FileTable.LAST_CHECK_RESULT).append(""); sb.append(") VALUES "); int index = 0; for (FsFile f : files) { - sb.append(" (?,?,?,?,?, ?,?)"); + sb.append(" (?,?,?,?,?, ?,?,?,?)"); boolean lastFile = index == (files.size() - 1); if (!lastFile) { sb.append(","); @@ -84,7 +92,7 @@ public class FileRepositoryImplSqlite implements FileRepository { } String sql = sb.toString(); - System.err.println(sql); + //System.err.println(sql); try ( Connection connection = createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { int i = 0; @@ -99,11 +107,13 @@ public class FileRepositoryImplSqlite implements FileRepository { // stmt.setString(++i, f.getHashSumValue()); stmt.setString(++i, f.getHashSumAlgorithm()); + stmt.setLong(++i, f.getSize()); + stmt.setString(++i, f.getLastCheckResult()); } // stmt.execute(); - System.out.println(stmt.toString()); + //System.out.println(stmt.toString()); } catch (SQLException e) { System.out.println(e.getMessage()); @@ -124,7 +134,7 @@ public class FileRepositoryImplSqlite implements FileRepository { .append(FileTable.TABLE_NAME); String sql = sb.toString(); - System.err.println(sql); +// System.err.println(sql); int i = 0; ResultSet rs = null; try ( @@ -165,7 +175,7 @@ public class FileRepositoryImplSqlite implements FileRepository { sb.append(FileTable.ID); sb.append("=?"); String sql = sb.toString(); - System.err.println("SQL::" + sql); + //System.err.println("SQL::" + sql); int i = 0; try ( @@ -173,7 +183,7 @@ public class FileRepositoryImplSqlite implements FileRepository { stmt.setString(++i, file.getId()); - System.err.println(stmt.toString()); + //System.err.println(stmt.toString()); stmt.execute(); } catch (SQLException e) { @@ -185,7 +195,7 @@ public class FileRepositoryImplSqlite implements FileRepository { } private Connection createConnection() throws ClassNotFoundException { - return new SqliteConnectionFactory().createConnection(); + return sqliteConnectionFactory.createConnection(); } @Override @@ -199,7 +209,9 @@ public class FileRepositoryImplSqlite implements FileRepository { .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(FileTable.HASH_SUM_ALGORITHM).append("=?, ") + .append(FileTable.SIZE).append("=?, ") + .append(FileTable.LAST_CHECK_RESULT).append("=? ") .append(" WHERE ").append(FileTable.ID).append("=?"); String sql = sb.toString(); @@ -211,6 +223,8 @@ public class FileRepositoryImplSqlite implements FileRepository { stmt.setString(++i, file.getLastCheckDate()); stmt.setString(++i, file.getHashSumValue()); stmt.setString(++i, file.getHashSumAlgorithm()); + stmt.setLong(++i, file.getSize()); + stmt.setString(++i, file.getLastCheckResult()); stmt.setString(++i, file.getId()); @@ -232,14 +246,16 @@ public class FileRepositoryImplSqlite implements FileRepository { rs.getString(FileTable.LAST_MODIFICATION_DATE), rs.getString(FileTable.LAST_CHECK_DATE), rs.getString(FileTable.HASH_SUM_VALUE), - rs.getString(FileTable.HASH_SUM_ALGORITHM) + rs.getString(FileTable.HASH_SUM_ALGORITHM), + rs.getLong(FileTable.SIZE), + rs.getString(FileTable.LAST_CHECK_RESULT) ); } @Override public void updateLastCheckDate(String lastCheckDate, List files) { - if (files.isEmpty()) { + if (files.isEmpty()) { return; } if (files.size() > 100) { @@ -258,13 +274,14 @@ public class FileRepositoryImplSqlite implements FileRepository { } return; } - + StringBuilder sb = new StringBuilder(); sb .append("UPDATE ") .append(FileTable.TABLE_NAME) .append(" SET ") - .append(FileTable.LAST_CHECK_DATE).append("=? ") + .append(FileTable.LAST_CHECK_DATE).append("=?, ") + .append(FileTable.LAST_CHECK_RESULT).append("='OK' ") .append(" WHERE ").append(FileTable.ID).append(" IN ("); int index = 0; for (FsFile f : files) { 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 index 3f06f90..a498b80 100644 --- a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/FileTable.java +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/FileTable.java @@ -24,7 +24,7 @@ import lombok.Setter; /** * - * @author robertvokac +* @author Robert Vokac */ @Getter @Setter @@ -40,5 +40,8 @@ class FileTable { // public static final String HASH_SUM_VALUE = "HASH_SUM_VALUE"; public static final String HASH_SUM_ALGORITHM = "HASH_SUM_ALGORITHM"; + public static final String SIZE = "SIZE"; + public static final String LAST_CHECK_RESULT = "LAST_CHECK_RESULT"; + } 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 index 5e62c7a..59f70da 100644 --- a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SqliteConnectionFactory.java +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SqliteConnectionFactory.java @@ -18,21 +18,26 @@ /////////////////////////////////////////////////////////////////////////////////////////////// package org.nanoboot.bitinspector.persistence.impl.sqlite; +import org.nanoboot.bitinspector.persistence.api.ConnectionFactory; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; +import org.nanoboot.bitinspector.core.Utils; /** * - * @author robertvokac + * @author Robert Vokac */ -public class SqliteConnectionFactory { - +public class SqliteConnectionFactory implements ConnectionFactory { + private final String jdbcUrl; + public SqliteConnectionFactory(String directoryWhereSqliteFileIs) { + this.jdbcUrl = Utils.createJdbcUrl(directoryWhereSqliteFileIs); + } public Connection createConnection() throws ClassNotFoundException { try { Class.forName("org.sqlite.JDBC"); - Connection conn = DriverManager.getConnection(Constants.JDBC_URL); + Connection conn = DriverManager.getConnection(jdbcUrl); return conn; 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 index 943de25..e43237c 100644 --- a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SqliteDatabaseMigration.java +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SqliteDatabaseMigration.java @@ -18,33 +18,48 @@ /////////////////////////////////////////////////////////////////////////////////////////////// package org.nanoboot.bitinspector.persistence.impl.sqlite; +import org.nanoboot.bitinspector.core.Utils; import org.nanoboot.dbmigration.core.main.DBMigration; +import org.nanoboot.dbmigration.core.main.MigrationResult; /** * - * @author robertvokac +* @author Robert Vokac */ public class SqliteDatabaseMigration { - public void migrate() { + private static SqliteDatabaseMigration INSTANCE; + + private SqliteDatabaseMigration() { + //Not meant to be instantiated + } + + public static SqliteDatabaseMigration getInstance() { + if (INSTANCE == null) { + INSTANCE = new SqliteDatabaseMigration(); + } + return INSTANCE; + } + + public MigrationResult migrate(String directoryWhereSqliteFileIs) { 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 jdbcUrl = Utils.createJdbcUrl(directoryWhereSqliteFileIs); + System.err.println("jdbcUrl=" + jdbcUrl); String clazz = this.getClass().getName(); DBMigration dbMigration = DBMigration .configure() - .dataSource(Constants.JDBC_URL) + .dataSource(jdbcUrl) .installedBy("bitinspector-persistence-impl-sqlite") .name("bitinspector") .sqlDialect("sqlite", "org.nanoboot.dbmigration.core.persistence.impl.sqlite.DBMigrationPersistenceSqliteImpl") .sqlMigrationsClass(clazz) .load(); - dbMigration.migrate(); + return 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 index 4138ade..445640b 100644 --- a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SystemItemRepositoryImplSqlite.java +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SystemItemRepositoryImplSqlite.java @@ -30,10 +30,16 @@ import org.nanoboot.bitinspector.persistence.api.SystemItemRepository; /** * - * @author robertvokac + * @author Robert Vokac */ public class SystemItemRepositoryImplSqlite implements SystemItemRepository { + public SystemItemRepositoryImplSqlite(SqliteConnectionFactory sqliteConnectionFactory) { + this.sqliteConnectionFactory = sqliteConnectionFactory; + } + + private final SqliteConnectionFactory sqliteConnectionFactory; + @Override public String create(SystemItem systemItem) { @@ -52,7 +58,7 @@ public class SystemItemRepositoryImplSqlite implements SystemItemRepository { String sql = sb.toString(); System.err.println(sql); try ( - Connection connection = new SqliteConnectionFactory().createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { + Connection connection = sqliteConnectionFactory.createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { int i = 0; stmt.setString(++i, systemItem.getKey()); stmt.setString(++i, systemItem.getValue()); @@ -95,7 +101,7 @@ public class SystemItemRepositoryImplSqlite implements SystemItemRepository { int i = 0; ResultSet rs = null; try ( - Connection connection = new SqliteConnectionFactory().createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { + Connection connection = sqliteConnectionFactory.createConnection(); PreparedStatement stmt = connection.prepareStatement(sql);) { stmt.setString(++i, key); 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 index 6fbcb3c..5325c41 100644 --- a/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SystemItemTable.java +++ b/src/main/java/org/nanoboot/bitinspector/persistence/impl/sqlite/SystemItemTable.java @@ -24,7 +24,7 @@ import lombok.Setter; /** * - * @author robertvokac +* @author Robert Vokac */ @Getter @Setter diff --git a/src/main/resources/db_migrations/sqlite/bitinspector/V000003__alter_table_file_add_column_size.sql b/src/main/resources/db_migrations/sqlite/bitinspector/V000003__alter_table_file_add_column_size.sql new file mode 100644 index 0000000..b6dc8eb --- /dev/null +++ b/src/main/resources/db_migrations/sqlite/bitinspector/V000003__alter_table_file_add_column_size.sql @@ -0,0 +1,2 @@ +ALTER TABLE "FILE" ADD COLUMN SIZE NUMBER + diff --git a/src/main/resources/db_migrations/sqlite/bitinspector/V000004__alter_table_file_add_column_last_check_result.sql b/src/main/resources/db_migrations/sqlite/bitinspector/V000004__alter_table_file_add_column_last_check_result.sql new file mode 100644 index 0000000..de179aa --- /dev/null +++ b/src/main/resources/db_migrations/sqlite/bitinspector/V000004__alter_table_file_add_column_last_check_result.sql @@ -0,0 +1,2 @@ +ALTER TABLE "FILE" ADD COLUMN LAST_CHECK_RESULT NUMBER +