From 18930fbb52e909a25dfa8e462ab93f2933941212 Mon Sep 17 00:00:00 2001 From: John Scancella Date: Sun, 22 Jul 2018 11:30:47 -0400 Subject: [PATCH 001/104] refs #124 renamed to make it more clear that we verify all manifests, not just payload manifest --- .../repository/bagit/verify/BagVerifier.java | 8 ++++---- ...oadVerifier.java => ManifestVerifier.java} | 19 +++++++++--------- .../bagit/verify/PayloadVerifierTest.java | 20 +++++++++---------- 3 files changed, 24 insertions(+), 23 deletions(-) rename src/main/java/gov/loc/repository/bagit/verify/{PayloadVerifier.java => ManifestVerifier.java} (92%) diff --git a/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java b/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java index 835e06beb..95fd7a127 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java +++ b/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java @@ -37,7 +37,7 @@ public final class BagVerifier implements AutoCloseable{ private static final Logger logger = LoggerFactory.getLogger(BagVerifier.class); private static final ResourceBundle messages = ResourceBundle.getBundle("MessageBundle"); - private final PayloadVerifier manifestVerifier; + private final ManifestVerifier manifestVerifier; private final ExecutorService executor; /** @@ -74,7 +74,7 @@ public BagVerifier(final ExecutorService executor){ * @param executor the thread pool to use when doing work */ public BagVerifier(final ExecutorService executor, final BagitAlgorithmNameToSupportedAlgorithmMapping nameMapping){ - manifestVerifier = new PayloadVerifier(nameMapping, executor); + manifestVerifier = new ManifestVerifier(nameMapping, executor); this.executor = executor; } @@ -209,14 +209,14 @@ public void isComplete(final Bag bag, final boolean ignoreHiddenFiles) throws MandatoryVerifier.checkIfAtLeastOnePayloadManifestsExist(bag.getRootDir(), bag.getVersion()); - manifestVerifier.verifyPayload(bag, ignoreHiddenFiles); + manifestVerifier.verifyManifests(bag, ignoreHiddenFiles); } public ExecutorService getExecutor() { return executor; } - public PayloadVerifier getManifestVerifier() { + public ManifestVerifier getManifestVerifier() { return manifestVerifier; } } diff --git a/src/main/java/gov/loc/repository/bagit/verify/PayloadVerifier.java b/src/main/java/gov/loc/repository/bagit/verify/ManifestVerifier.java similarity index 92% rename from src/main/java/gov/loc/repository/bagit/verify/PayloadVerifier.java rename to src/main/java/gov/loc/repository/bagit/verify/ManifestVerifier.java index 2a7fd884c..8d073b1d3 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/PayloadVerifier.java +++ b/src/main/java/gov/loc/repository/bagit/verify/ManifestVerifier.java @@ -31,8 +31,8 @@ /** * Responsible for all things related to the manifest during verification. */ -public class PayloadVerifier implements AutoCloseable{ - private static final Logger logger = LoggerFactory.getLogger(PayloadVerifier.class); +public class ManifestVerifier implements AutoCloseable{ + private static final Logger logger = LoggerFactory.getLogger(ManifestVerifier.class); private static final ResourceBundle messages = ResourceBundle.getBundle("MessageBundle"); private transient final BagitAlgorithmNameToSupportedAlgorithmMapping nameMapping; @@ -42,7 +42,7 @@ public class PayloadVerifier implements AutoCloseable{ * Create a PayloadVerifier using a cached thread pool and the * {@link StandardBagitAlgorithmNameToSupportedAlgorithmMapping} mapping */ - public PayloadVerifier(){ + public ManifestVerifier(){ this(new StandardBagitAlgorithmNameToSupportedAlgorithmMapping(), Executors.newCachedThreadPool()); } @@ -51,7 +51,7 @@ public PayloadVerifier(){ * * @param nameMapping the mapping between BagIt algorithm name and the java supported algorithm */ - public PayloadVerifier(final BagitAlgorithmNameToSupportedAlgorithmMapping nameMapping) { + public ManifestVerifier(final BagitAlgorithmNameToSupportedAlgorithmMapping nameMapping) { this(nameMapping, Executors.newCachedThreadPool()); } @@ -61,7 +61,7 @@ public PayloadVerifier(final BagitAlgorithmNameToSupportedAlgorithmMapping nameM * * @param executor the thread pool to use when doing work */ - public PayloadVerifier(final ExecutorService executor) { + public ManifestVerifier(final ExecutorService executor) { this(new StandardBagitAlgorithmNameToSupportedAlgorithmMapping(), executor); } @@ -71,7 +71,7 @@ public PayloadVerifier(final ExecutorService executor) { * @param nameMapping the mapping between BagIt algorithm name and the java supported algorithm * @param executor the thread pool to use when doing work */ - public PayloadVerifier(final BagitAlgorithmNameToSupportedAlgorithmMapping nameMapping, final ExecutorService executor) { + public ManifestVerifier(final BagitAlgorithmNameToSupportedAlgorithmMapping nameMapping, final ExecutorService executor) { this.nameMapping = nameMapping; this.executor = executor; } @@ -83,11 +83,12 @@ public void close() throws SecurityException{ } /** - * Verify that all the files in the payload directory are listed in the manifest and - * all files listed in the manifests exist. + * Verify that all the files in the payload directory are listed in the payload manifest and + * all files listed in all manifests exist. * * @param bag the bag to check to check * @param ignoreHiddenFiles to ignore hidden files unless they are specifically listed in a manifest + * * @throws IOException if there is a problem reading a file * @throws MaliciousPathException the path in the manifest was specifically crafted to cause harm * @throws UnsupportedAlgorithmException if the algorithm used for the manifest is unsupported @@ -95,7 +96,7 @@ public void close() throws SecurityException{ * @throws FileNotInPayloadDirectoryException if a file is listed in a manifest but doesn't exist in the payload directory * @throws InterruptedException if a thread is interrupted while doing work */ - public void verifyPayload(final Bag bag, final boolean ignoreHiddenFiles) + public void verifyManifests(final Bag bag, final boolean ignoreHiddenFiles) throws IOException, MaliciousPathException, UnsupportedAlgorithmException, InvalidBagitFileFormatException, FileNotInPayloadDirectoryException, InterruptedException { diff --git a/src/test/java/gov/loc/repository/bagit/verify/PayloadVerifierTest.java b/src/test/java/gov/loc/repository/bagit/verify/PayloadVerifierTest.java index 3293b8a79..5c3a14138 100644 --- a/src/test/java/gov/loc/repository/bagit/verify/PayloadVerifierTest.java +++ b/src/test/java/gov/loc/repository/bagit/verify/PayloadVerifierTest.java @@ -20,11 +20,11 @@ public class PayloadVerifierTest { private Path rootDir = Paths.get(new File("src/test/resources/bags/v0_97/bag").toURI()); private BagReader reader = new BagReader(); - private PayloadVerifier sut; + private ManifestVerifier sut; @BeforeEach public void setup(){ - sut = new PayloadVerifier(new StandardBagitAlgorithmNameToSupportedAlgorithmMapping()); + sut = new ManifestVerifier(new StandardBagitAlgorithmNameToSupportedAlgorithmMapping()); } @Test @@ -32,11 +32,11 @@ public void testOtherConstructors() throws Exception { rootDir = Paths.get(new File("src/test/resources/bags/v0_96/bag-with-tagfiles-in-payload-manifest").toURI()); Bag bag = reader.read(rootDir); - sut = new PayloadVerifier(); - sut.verifyPayload(bag, true); + sut = new ManifestVerifier(); + sut.verifyManifests(bag, true); - sut = new PayloadVerifier(Executors.newCachedThreadPool()); - sut.verifyPayload(bag, true); + sut = new ManifestVerifier(Executors.newCachedThreadPool()); + sut.verifyManifests(bag, true); } @Test @@ -45,7 +45,7 @@ public void testErrorWhenManifestListFileThatDoesntExist() throws Exception{ Bag bag = reader.read(rootDir); Assertions.assertThrows(FileNotInPayloadDirectoryException.class, - () -> { sut.verifyPayload(bag, true); }); + () -> { sut.verifyManifests(bag, true); }); } @Test @@ -54,7 +54,7 @@ public void testErrorWhenFileIsntInManifest() throws Exception{ Bag bag = reader.read(rootDir); Assertions.assertThrows(FileNotInManifestException.class, - () -> { sut.verifyPayload(bag, true); }); + () -> { sut.verifyManifests(bag, true); }); } @Test @@ -62,7 +62,7 @@ public void testBagWithTagFilesInPayloadIsValid() throws Exception{ rootDir = Paths.get(new File("src/test/resources/bags/v0_96/bag-with-tagfiles-in-payload-manifest").toURI()); Bag bag = reader.read(rootDir); - sut.verifyPayload(bag, true); + sut.verifyManifests(bag, true); } @Test @@ -70,6 +70,6 @@ public void testNotALlFilesListedInAllManifestsThrowsException() throws Exceptio Path bagDir = Paths.get(new File("src/test/resources/notAllFilesListedInAllManifestsBag").toURI()); Bag bag = reader.read(bagDir); Assertions.assertThrows(FileNotInManifestException.class, - () -> { sut.verifyPayload(bag, true); }); + () -> { sut.verifyManifests(bag, true); }); } } From 5a8bb98102a3ef461baab156c1a3a76d9990b907 Mon Sep 17 00:00:00 2001 From: John Scancella Date: Sun, 22 Jul 2018 11:39:29 -0400 Subject: [PATCH 002/104] refs #123 - fixed error message formatting --- .../exceptions/MissingPayloadDirectoryException.java | 8 ++++++-- .../loc/repository/bagit/verify/MandatoryVerifier.java | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadDirectoryException.java b/src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadDirectoryException.java index 766b05aa7..f0258a3e5 100644 --- a/src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadDirectoryException.java +++ b/src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadDirectoryException.java @@ -1,12 +1,16 @@ package gov.loc.repository.bagit.exceptions; +import java.nio.file.Path; + +import org.slf4j.helpers.MessageFormatter; + /** * The payload directory is a required file. This class represents the error if it is not found. */ public class MissingPayloadDirectoryException extends Exception { private static final long serialVersionUID = 1L; - public MissingPayloadDirectoryException(final String message){ - super(message); + public MissingPayloadDirectoryException(final String message, final Path path){ + super(MessageFormatter.format(message, path).getMessage()); } } diff --git a/src/main/java/gov/loc/repository/bagit/verify/MandatoryVerifier.java b/src/main/java/gov/loc/repository/bagit/verify/MandatoryVerifier.java index e73ed3b02..7d14d8357 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/MandatoryVerifier.java +++ b/src/main/java/gov/loc/repository/bagit/verify/MandatoryVerifier.java @@ -84,7 +84,7 @@ public static void checkPayloadDirectoryExists(final Bag bag) throws MissingPayl final Path dataDir = PathUtils.getDataDir(bag); if(!Files.exists(dataDir)){ - throw new MissingPayloadDirectoryException(messages.getString("file_should_exist_error")); + throw new MissingPayloadDirectoryException(messages.getString("file_should_exist_error"), dataDir); } } From f6249939154b22055d20a414a4e0e8b00b4d0691 Mon Sep 17 00:00:00 2001 From: John Scancella Date: Sun, 22 Jul 2018 12:06:59 -0400 Subject: [PATCH 003/104] refs #119 - fixed different case issue and added test to check for any issues in the future --- .../loc/repository/bagit/conformance/ManifestChecker.java | 5 +++-- .../repository/bagit/hash/StandardSupportedAlgorithms.java | 6 +++--- .../loc/repository/bagit/conformance/BagLinterTest.java | 7 +++++++ src/test/resources/bags/v1_0/bag/bag-info.txt | 3 +++ src/test/resources/bags/v1_0/bag/bagit.txt | 2 ++ src/test/resources/bags/v1_0/bag/data/foo.txt | 1 + src/test/resources/bags/v1_0/bag/manifest-sha512.txt | 1 + src/test/resources/bags/v1_0/bag/tagmanifest-sha512.txt | 3 +++ 8 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 src/test/resources/bags/v1_0/bag/bag-info.txt create mode 100644 src/test/resources/bags/v1_0/bag/bagit.txt create mode 100644 src/test/resources/bags/v1_0/bag/data/foo.txt create mode 100644 src/test/resources/bags/v1_0/bag/manifest-sha512.txt create mode 100644 src/test/resources/bags/v1_0/bag/tagmanifest-sha512.txt diff --git a/src/main/java/gov/loc/repository/bagit/conformance/ManifestChecker.java b/src/main/java/gov/loc/repository/bagit/conformance/ManifestChecker.java index af9438c26..b7247dc4e 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/ManifestChecker.java +++ b/src/main/java/gov/loc/repository/bagit/conformance/ManifestChecker.java @@ -123,9 +123,10 @@ private static void checkManifestPayload(final Path manifestFile, final Charset String path = parsePath(line); path = checkForManifestCreatedWithMD5SumTools(path, warnings, warningsToIgnore); - paths.add(path.toLowerCase()); checkForDifferentCase(path, paths, manifestFile, warnings, warningsToIgnore); + paths.add(path.toLowerCase()); + if(encoding.name().startsWith("UTF")){ checkNormalization(path, manifestFile.getParent(), warnings, warningsToIgnore); } @@ -256,7 +257,7 @@ static void checkAlgorthm(final String algorithm, final Set warnin warnings.add(BagitWarning.WEAK_CHECKSUM_ALGORITHM); } - else if(!warningsToIgnore.contains(BagitWarning.NON_STANDARD_ALGORITHM) && !"SHA-512".equals(upperCaseAlg)){ + else if(!warningsToIgnore.contains(BagitWarning.NON_STANDARD_ALGORITHM) && !"SHA512".equals(upperCaseAlg)){ logger.warn(messages.getString("non_standard_algorithm_warning"), algorithm); warnings.add(BagitWarning.NON_STANDARD_ALGORITHM); } diff --git a/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java b/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java index 40818316c..656485e89 100644 --- a/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java +++ b/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java @@ -6,9 +6,9 @@ public enum StandardSupportedAlgorithms implements SupportedAlgorithm{ MD5("MD5"), SHA1("SHA-1"), - SHA224("SHA-224"), - SHA256("SHA-256"), - SHA512("SHA-512"); + SHA224("SHA224"), + SHA256("SHA256"), + SHA512("SHA512"); private final String messageDigestName; diff --git a/src/test/java/gov/loc/repository/bagit/conformance/BagLinterTest.java b/src/test/java/gov/loc/repository/bagit/conformance/BagLinterTest.java index 3a9655fbb..830ca94a8 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/BagLinterTest.java +++ b/src/test/java/gov/loc/repository/bagit/conformance/BagLinterTest.java @@ -28,6 +28,13 @@ public void testClassIsWellDefined() throws NoSuchMethodException, InvocationTar assertUtilityClassWellDefined(BagLinter.class); } + @Test + public void testConformantBag() throws Exception{ + Path goodBag = Paths.get("src", "test", "resources", "bags", "v1_0", "bag"); + Set warnings = BagLinter.lintBag(goodBag); + Assertions.assertTrue(warnings.size() == 0); + } + @Test public void testLintBag() throws Exception{ Set expectedWarnings = new HashSet<>(); diff --git a/src/test/resources/bags/v1_0/bag/bag-info.txt b/src/test/resources/bags/v1_0/bag/bag-info.txt new file mode 100644 index 000000000..30723bdc8 --- /dev/null +++ b/src/test/resources/bags/v1_0/bag/bag-info.txt @@ -0,0 +1,3 @@ +Bag-Software-Agent: bagit.py v1.7.0 +Bagging-Date: 2018-07-22 +Payload-Oxum: 6.1 diff --git a/src/test/resources/bags/v1_0/bag/bagit.txt b/src/test/resources/bags/v1_0/bag/bagit.txt new file mode 100644 index 000000000..7bfcaecc4 --- /dev/null +++ b/src/test/resources/bags/v1_0/bag/bagit.txt @@ -0,0 +1,2 @@ +BagIt-Version: 1.0 +Tag-File-Character-Encoding: UTF-8 diff --git a/src/test/resources/bags/v1_0/bag/data/foo.txt b/src/test/resources/bags/v1_0/bag/data/foo.txt new file mode 100644 index 000000000..ce0136250 --- /dev/null +++ b/src/test/resources/bags/v1_0/bag/data/foo.txt @@ -0,0 +1 @@ +hello diff --git a/src/test/resources/bags/v1_0/bag/manifest-sha512.txt b/src/test/resources/bags/v1_0/bag/manifest-sha512.txt new file mode 100644 index 000000000..f3fd06d72 --- /dev/null +++ b/src/test/resources/bags/v1_0/bag/manifest-sha512.txt @@ -0,0 +1 @@ +e7c22b994c59d9cf2b48e549b1e24666636045930d3da7c1acb299d1c3b7f931f94aae41edda2c2b207a36e10f8bcb8d45223e54878f5b316e7ce3b6bc019629 data/foo.txt diff --git a/src/test/resources/bags/v1_0/bag/tagmanifest-sha512.txt b/src/test/resources/bags/v1_0/bag/tagmanifest-sha512.txt new file mode 100644 index 000000000..732e7c45d --- /dev/null +++ b/src/test/resources/bags/v1_0/bag/tagmanifest-sha512.txt @@ -0,0 +1,3 @@ +f5bdbc7f273dd8b95d30b77b3d6f727d999ecbe9be06a7656388e7a3a46d963881563d779a2a99265b0c2785de2a7b72ac05fa7bc5b66b471d3f4e80fe9bb370 bag-info.txt +1d73ae108d4109b61f56698a5e19ee1f8947bdf8940bbce6adbe5e0940c2363caace6a547b4f1b3ec6a4fd2b7fa845e9cb9d28823bc72c59971718bb26f2fbd8 bagit.txt +35d40e38f5e2eb7261a1cc0c0ccf9c6c50d2e07e39a9460ec4f99046048f939d82647c68d8f744c653c7a37c05cc9d71153c8028786dfeba4322a3be5fe81af0 manifest-sha512.txt From d38a82a0052faba726d1436ab1bea79fa6607cec Mon Sep 17 00:00:00 2001 From: John Scancella Date: Sun, 22 Jul 2018 12:10:13 -0400 Subject: [PATCH 004/104] refs #121 - only use as many threads as CPUs --- .../java/gov/loc/repository/bagit/verify/BagVerifier.java | 4 ++-- .../gov/loc/repository/bagit/verify/ManifestVerifier.java | 4 ++-- .../repository/bagit/verify/CheckIfFileExistsTaskTest.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java b/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java index 95fd7a127..663cd8b8f 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java +++ b/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java @@ -45,7 +45,7 @@ public final class BagVerifier implements AutoCloseable{ * {@link StandardBagitAlgorithmNameToSupportedAlgorithmMapping} */ public BagVerifier(){ - this(Executors.newCachedThreadPool(), new StandardBagitAlgorithmNameToSupportedAlgorithmMapping()); + this(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()), new StandardBagitAlgorithmNameToSupportedAlgorithmMapping()); } /** @@ -54,7 +54,7 @@ public BagVerifier(){ * @param nameMapping the mapping between BagIt algorithm name and the java supported algorithm */ public BagVerifier(final BagitAlgorithmNameToSupportedAlgorithmMapping nameMapping){ - this(Executors.newCachedThreadPool(), nameMapping); + this(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()), nameMapping); } /** diff --git a/src/main/java/gov/loc/repository/bagit/verify/ManifestVerifier.java b/src/main/java/gov/loc/repository/bagit/verify/ManifestVerifier.java index 8d073b1d3..645953f15 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/ManifestVerifier.java +++ b/src/main/java/gov/loc/repository/bagit/verify/ManifestVerifier.java @@ -43,7 +43,7 @@ public class ManifestVerifier implements AutoCloseable{ * {@link StandardBagitAlgorithmNameToSupportedAlgorithmMapping} mapping */ public ManifestVerifier(){ - this(new StandardBagitAlgorithmNameToSupportedAlgorithmMapping(), Executors.newCachedThreadPool()); + this(new StandardBagitAlgorithmNameToSupportedAlgorithmMapping(), Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())); } /** @@ -52,7 +52,7 @@ public ManifestVerifier(){ * @param nameMapping the mapping between BagIt algorithm name and the java supported algorithm */ public ManifestVerifier(final BagitAlgorithmNameToSupportedAlgorithmMapping nameMapping) { - this(nameMapping, Executors.newCachedThreadPool()); + this(nameMapping, Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())); } /** diff --git a/src/test/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTaskTest.java b/src/test/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTaskTest.java index eb0f1b7fb..6ea65d417 100644 --- a/src/test/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTaskTest.java +++ b/src/test/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTaskTest.java @@ -17,7 +17,7 @@ public class CheckIfFileExistsTaskTest extends TempFolderTest { @Test public void testNormalizedFileExists() throws Exception{ - ExecutorService executor = Executors.newCachedThreadPool(); + ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); CountDownLatch latch = new CountDownLatch(1); Set missingFiles = new ConcurrentSkipListSet<>(); String filename = "Núñez.txt"; From f3c31145ec7b1566fd4b56209925f2ffda1db18a Mon Sep 17 00:00:00 2001 From: John Scancella Date: Tue, 24 Jul 2018 09:23:42 -0400 Subject: [PATCH 005/104] refs #119 - changed sha-1 to sha1 to be more inline with bagit-python. Also added tests for valid bags of each standard algorithm --- .../hash/StandardSupportedAlgorithms.java | 2 +- .../bagit/verify/BagVerifierTest.java | 35 +++++++++++++++++++ ...ierTest.java => ManifestVerifierTest.java} | 2 +- src/test/resources/md5Bag/bag-info.txt | 3 ++ src/test/resources/md5Bag/bagit.txt | 2 ++ src/test/resources/md5Bag/data/readme.txt | 1 + src/test/resources/md5Bag/manifest-md5.txt | 1 + src/test/resources/md5Bag/tagmanifest-md5.txt | 3 ++ src/test/resources/sha1Bag/bag-info.txt | 3 ++ src/test/resources/sha1Bag/bagit.txt | 2 ++ src/test/resources/sha1Bag/data/readme.txt | 1 + src/test/resources/sha1Bag/manifest-sha1.txt | 1 + .../resources/sha1Bag/tagmanifest-sha1.txt | 3 ++ src/test/resources/sha224Bag/bag-info.txt | 3 ++ src/test/resources/sha224Bag/bagit.txt | 2 ++ src/test/resources/sha224Bag/data/readme.txt | 1 + .../resources/sha224Bag/manifest-sha224.txt | 1 + .../sha224Bag/tagmanifest-sha224.txt | 3 ++ src/test/resources/sha256Bag/bag-info.txt | 3 ++ src/test/resources/sha256Bag/bagit.txt | 2 ++ src/test/resources/sha256Bag/data/readme.txt | 1 + .../resources/sha256Bag/manifest-sha256.txt | 1 + .../sha256Bag/tagmanifest-sha256.txt | 3 ++ src/test/resources/sha512Bag/bag-info.txt | 3 ++ src/test/resources/sha512Bag/bagit.txt | 2 ++ src/test/resources/sha512Bag/data/readme.txt | 1 + .../resources/sha512Bag/manifest-sha512.txt | 1 + .../sha512Bag/tagmanifest-sha512.txt | 3 ++ 28 files changed, 87 insertions(+), 2 deletions(-) rename src/test/java/gov/loc/repository/bagit/verify/{PayloadVerifierTest.java => ManifestVerifierTest.java} (98%) create mode 100644 src/test/resources/md5Bag/bag-info.txt create mode 100644 src/test/resources/md5Bag/bagit.txt create mode 100644 src/test/resources/md5Bag/data/readme.txt create mode 100644 src/test/resources/md5Bag/manifest-md5.txt create mode 100644 src/test/resources/md5Bag/tagmanifest-md5.txt create mode 100644 src/test/resources/sha1Bag/bag-info.txt create mode 100644 src/test/resources/sha1Bag/bagit.txt create mode 100644 src/test/resources/sha1Bag/data/readme.txt create mode 100644 src/test/resources/sha1Bag/manifest-sha1.txt create mode 100644 src/test/resources/sha1Bag/tagmanifest-sha1.txt create mode 100644 src/test/resources/sha224Bag/bag-info.txt create mode 100644 src/test/resources/sha224Bag/bagit.txt create mode 100644 src/test/resources/sha224Bag/data/readme.txt create mode 100644 src/test/resources/sha224Bag/manifest-sha224.txt create mode 100644 src/test/resources/sha224Bag/tagmanifest-sha224.txt create mode 100644 src/test/resources/sha256Bag/bag-info.txt create mode 100644 src/test/resources/sha256Bag/bagit.txt create mode 100644 src/test/resources/sha256Bag/data/readme.txt create mode 100644 src/test/resources/sha256Bag/manifest-sha256.txt create mode 100644 src/test/resources/sha256Bag/tagmanifest-sha256.txt create mode 100644 src/test/resources/sha512Bag/bag-info.txt create mode 100644 src/test/resources/sha512Bag/bagit.txt create mode 100644 src/test/resources/sha512Bag/data/readme.txt create mode 100644 src/test/resources/sha512Bag/manifest-sha512.txt create mode 100644 src/test/resources/sha512Bag/tagmanifest-sha512.txt diff --git a/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java b/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java index 656485e89..32df24a7e 100644 --- a/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java +++ b/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java @@ -5,7 +5,7 @@ */ public enum StandardSupportedAlgorithms implements SupportedAlgorithm{ MD5("MD5"), - SHA1("SHA-1"), + SHA1("SHA1"), SHA224("SHA224"), SHA256("SHA256"), SHA512("SHA512"); diff --git a/src/test/java/gov/loc/repository/bagit/verify/BagVerifierTest.java b/src/test/java/gov/loc/repository/bagit/verify/BagVerifierTest.java index 303e0acae..68955a149 100644 --- a/src/test/java/gov/loc/repository/bagit/verify/BagVerifierTest.java +++ b/src/test/java/gov/loc/repository/bagit/verify/BagVerifierTest.java @@ -43,6 +43,41 @@ public void testStandardSupportedAlgorithms() throws Exception{ } } + @Test + public void testMD5Bag() throws Exception{ + Path bagDir = Paths.get("src", "test", "resources", "md5Bag"); + Bag bag = reader.read(bagDir); + sut.isValid(bag, true); + } + + @Test + public void testSHA1Bag() throws Exception{ + Path bagDir = Paths.get("src", "test", "resources", "sha1Bag"); + Bag bag = reader.read(bagDir); + sut.isValid(bag, true); + } + + @Test + public void testSHA224Bag() throws Exception{ + Path bagDir = Paths.get("src", "test", "resources", "sha224Bag"); + Bag bag = reader.read(bagDir); + sut.isValid(bag, true); + } + + @Test + public void testSHA256Bag() throws Exception{ + Path bagDir = Paths.get("src", "test", "resources", "sha256Bag"); + Bag bag = reader.read(bagDir); + sut.isValid(bag, true); + } + + @Test + public void testSHA512Bag() throws Exception{ + Path bagDir = Paths.get("src", "test", "resources", "sha512Bag"); + Bag bag = reader.read(bagDir); + sut.isValid(bag, true); + } + @Test public void testVersion0_97IsValid() throws Exception{ Bag bag = reader.read(rootDir); diff --git a/src/test/java/gov/loc/repository/bagit/verify/PayloadVerifierTest.java b/src/test/java/gov/loc/repository/bagit/verify/ManifestVerifierTest.java similarity index 98% rename from src/test/java/gov/loc/repository/bagit/verify/PayloadVerifierTest.java rename to src/test/java/gov/loc/repository/bagit/verify/ManifestVerifierTest.java index 5c3a14138..5d10736bf 100644 --- a/src/test/java/gov/loc/repository/bagit/verify/PayloadVerifierTest.java +++ b/src/test/java/gov/loc/repository/bagit/verify/ManifestVerifierTest.java @@ -15,7 +15,7 @@ import gov.loc.repository.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; import gov.loc.repository.bagit.reader.BagReader; -public class PayloadVerifierTest { +public class ManifestVerifierTest { private Path rootDir = Paths.get(new File("src/test/resources/bags/v0_97/bag").toURI()); private BagReader reader = new BagReader(); diff --git a/src/test/resources/md5Bag/bag-info.txt b/src/test/resources/md5Bag/bag-info.txt new file mode 100644 index 000000000..7dd5e14ce --- /dev/null +++ b/src/test/resources/md5Bag/bag-info.txt @@ -0,0 +1,3 @@ +Bag-Software-Agent: bagit.py v1.7.0 +Bagging-Date: 2018-07-24 +Payload-Oxum: 52.1 diff --git a/src/test/resources/md5Bag/bagit.txt b/src/test/resources/md5Bag/bagit.txt new file mode 100644 index 000000000..c4aebb43a --- /dev/null +++ b/src/test/resources/md5Bag/bagit.txt @@ -0,0 +1,2 @@ +BagIt-Version: 0.97 +Tag-File-Character-Encoding: UTF-8 diff --git a/src/test/resources/md5Bag/data/readme.txt b/src/test/resources/md5Bag/data/readme.txt new file mode 100644 index 000000000..7ec7b2d61 --- /dev/null +++ b/src/test/resources/md5Bag/data/readme.txt @@ -0,0 +1 @@ +this is a valid md5 bag used for testing valid bags diff --git a/src/test/resources/md5Bag/manifest-md5.txt b/src/test/resources/md5Bag/manifest-md5.txt new file mode 100644 index 000000000..62cd3ebf9 --- /dev/null +++ b/src/test/resources/md5Bag/manifest-md5.txt @@ -0,0 +1 @@ +aee452eebfbd978228775bf7b0e808dc data/readme.txt diff --git a/src/test/resources/md5Bag/tagmanifest-md5.txt b/src/test/resources/md5Bag/tagmanifest-md5.txt new file mode 100644 index 000000000..6201d4ecf --- /dev/null +++ b/src/test/resources/md5Bag/tagmanifest-md5.txt @@ -0,0 +1,3 @@ +59044a83bd40ec50b7ee927bf1e672a9 bag-info.txt +9e5ad981e0d29adc278f6a294b8c2aca bagit.txt +f1578aeea7f57af650f0a94565e40ceb manifest-md5.txt diff --git a/src/test/resources/sha1Bag/bag-info.txt b/src/test/resources/sha1Bag/bag-info.txt new file mode 100644 index 000000000..30ce5278d --- /dev/null +++ b/src/test/resources/sha1Bag/bag-info.txt @@ -0,0 +1,3 @@ +Bag-Software-Agent: bagit.py v1.7.0 +Bagging-Date: 2018-07-24 +Payload-Oxum: 43.1 diff --git a/src/test/resources/sha1Bag/bagit.txt b/src/test/resources/sha1Bag/bagit.txt new file mode 100644 index 000000000..c4aebb43a --- /dev/null +++ b/src/test/resources/sha1Bag/bagit.txt @@ -0,0 +1,2 @@ +BagIt-Version: 0.97 +Tag-File-Character-Encoding: UTF-8 diff --git a/src/test/resources/sha1Bag/data/readme.txt b/src/test/resources/sha1Bag/data/readme.txt new file mode 100644 index 000000000..0979f66d0 --- /dev/null +++ b/src/test/resources/sha1Bag/data/readme.txt @@ -0,0 +1 @@ +this is a sha1 bag used to test valid bags diff --git a/src/test/resources/sha1Bag/manifest-sha1.txt b/src/test/resources/sha1Bag/manifest-sha1.txt new file mode 100644 index 000000000..04bda92f9 --- /dev/null +++ b/src/test/resources/sha1Bag/manifest-sha1.txt @@ -0,0 +1 @@ +2f0973b52379ea88967e5e319941ff167d680ee9 data/readme.txt diff --git a/src/test/resources/sha1Bag/tagmanifest-sha1.txt b/src/test/resources/sha1Bag/tagmanifest-sha1.txt new file mode 100644 index 000000000..f4d7d5534 --- /dev/null +++ b/src/test/resources/sha1Bag/tagmanifest-sha1.txt @@ -0,0 +1,3 @@ +27bfaef124e06412f40c75bf6e5aa1cf4dbb9774 bag-info.txt +e2924b081506bac23f5fffe650ad1848a1c8ac1d bagit.txt +54adcabf9b1038d090d145d9315070c5493db1e6 manifest-sha1.txt diff --git a/src/test/resources/sha224Bag/bag-info.txt b/src/test/resources/sha224Bag/bag-info.txt new file mode 100644 index 000000000..40c3b6f85 --- /dev/null +++ b/src/test/resources/sha224Bag/bag-info.txt @@ -0,0 +1,3 @@ +Bag-Software-Agent: bagit.py v1.7.0 +Bagging-Date: 2018-07-24 +Payload-Oxum: 45.1 diff --git a/src/test/resources/sha224Bag/bagit.txt b/src/test/resources/sha224Bag/bagit.txt new file mode 100644 index 000000000..c4aebb43a --- /dev/null +++ b/src/test/resources/sha224Bag/bagit.txt @@ -0,0 +1,2 @@ +BagIt-Version: 0.97 +Tag-File-Character-Encoding: UTF-8 diff --git a/src/test/resources/sha224Bag/data/readme.txt b/src/test/resources/sha224Bag/data/readme.txt new file mode 100644 index 000000000..e05de9a0a --- /dev/null +++ b/src/test/resources/sha224Bag/data/readme.txt @@ -0,0 +1 @@ +this is a sha224 valid bag, used for testing diff --git a/src/test/resources/sha224Bag/manifest-sha224.txt b/src/test/resources/sha224Bag/manifest-sha224.txt new file mode 100644 index 000000000..a8ea47300 --- /dev/null +++ b/src/test/resources/sha224Bag/manifest-sha224.txt @@ -0,0 +1 @@ +476ba7f2f92170790679502b1f1bad48a9d0ae74b561cf558370a5fb data/readme.txt diff --git a/src/test/resources/sha224Bag/tagmanifest-sha224.txt b/src/test/resources/sha224Bag/tagmanifest-sha224.txt new file mode 100644 index 000000000..cba870c7c --- /dev/null +++ b/src/test/resources/sha224Bag/tagmanifest-sha224.txt @@ -0,0 +1,3 @@ +34458393a69d3c6a5ec82743e1817032b3dc875ae10ff19b2c55ed1c bag-info.txt +fe7e72d15c56a8e8017246f52765a93ce4823d1fa3ed1e0d1ac3695f bagit.txt +8c717410c037477322cdd4069feeff2aa09eb0991989807724defaae manifest-sha224.txt diff --git a/src/test/resources/sha256Bag/bag-info.txt b/src/test/resources/sha256Bag/bag-info.txt new file mode 100644 index 000000000..ec9f2e4da --- /dev/null +++ b/src/test/resources/sha256Bag/bag-info.txt @@ -0,0 +1,3 @@ +Bag-Software-Agent: bagit.py v1.7.0 +Bagging-Date: 2018-07-24 +Payload-Oxum: 44.1 diff --git a/src/test/resources/sha256Bag/bagit.txt b/src/test/resources/sha256Bag/bagit.txt new file mode 100644 index 000000000..c4aebb43a --- /dev/null +++ b/src/test/resources/sha256Bag/bagit.txt @@ -0,0 +1,2 @@ +BagIt-Version: 0.97 +Tag-File-Character-Encoding: UTF-8 diff --git a/src/test/resources/sha256Bag/data/readme.txt b/src/test/resources/sha256Bag/data/readme.txt new file mode 100644 index 000000000..4e8750d05 --- /dev/null +++ b/src/test/resources/sha256Bag/data/readme.txt @@ -0,0 +1 @@ +this is a valid sha256 bag used for testing diff --git a/src/test/resources/sha256Bag/manifest-sha256.txt b/src/test/resources/sha256Bag/manifest-sha256.txt new file mode 100644 index 000000000..f19ae2b49 --- /dev/null +++ b/src/test/resources/sha256Bag/manifest-sha256.txt @@ -0,0 +1 @@ +694d56447888829536b5782536f2bbe5c83325b1b2fd56a5ca676c991f196bf7 data/readme.txt diff --git a/src/test/resources/sha256Bag/tagmanifest-sha256.txt b/src/test/resources/sha256Bag/tagmanifest-sha256.txt new file mode 100644 index 000000000..c37479e35 --- /dev/null +++ b/src/test/resources/sha256Bag/tagmanifest-sha256.txt @@ -0,0 +1,3 @@ +d0d7117f5662fe588aab40c61c66163e961c930ffec5fd52d64d39a6aa8f8e38 bag-info.txt +e91f941be5973ff71f1dccbdd1a32d598881893a7f21be516aca743da38b1689 bagit.txt +ba57368e0165a727b0b6cc5f040c5cbd3e3ac28d1d52f94d111d64382c6f0466 manifest-sha256.txt diff --git a/src/test/resources/sha512Bag/bag-info.txt b/src/test/resources/sha512Bag/bag-info.txt new file mode 100644 index 000000000..ec9f2e4da --- /dev/null +++ b/src/test/resources/sha512Bag/bag-info.txt @@ -0,0 +1,3 @@ +Bag-Software-Agent: bagit.py v1.7.0 +Bagging-Date: 2018-07-24 +Payload-Oxum: 44.1 diff --git a/src/test/resources/sha512Bag/bagit.txt b/src/test/resources/sha512Bag/bagit.txt new file mode 100644 index 000000000..c4aebb43a --- /dev/null +++ b/src/test/resources/sha512Bag/bagit.txt @@ -0,0 +1,2 @@ +BagIt-Version: 0.97 +Tag-File-Character-Encoding: UTF-8 diff --git a/src/test/resources/sha512Bag/data/readme.txt b/src/test/resources/sha512Bag/data/readme.txt new file mode 100644 index 000000000..218b84225 --- /dev/null +++ b/src/test/resources/sha512Bag/data/readme.txt @@ -0,0 +1 @@ +this is a valid sha512 bag used for testing diff --git a/src/test/resources/sha512Bag/manifest-sha512.txt b/src/test/resources/sha512Bag/manifest-sha512.txt new file mode 100644 index 000000000..d6f3bc3dd --- /dev/null +++ b/src/test/resources/sha512Bag/manifest-sha512.txt @@ -0,0 +1 @@ +6e464149951ad3e3975c326ccb5c6e4ce306516fcc99689d90094e3bfbd87e87e0f7dbb4efd8e59456b808ca2b7a22bb14fa0b1edb76e11964a181850b45a628 data/readme.txt diff --git a/src/test/resources/sha512Bag/tagmanifest-sha512.txt b/src/test/resources/sha512Bag/tagmanifest-sha512.txt new file mode 100644 index 000000000..f9bf3e71a --- /dev/null +++ b/src/test/resources/sha512Bag/tagmanifest-sha512.txt @@ -0,0 +1,3 @@ +bede8e68c6fef4be00b54e5a5c2e104501444531679948187926a84a4e5c85d466959f34dae9ac4c81ccea4ec28e3f0ea7b661c0f100e457b3a568a08661cc01 bag-info.txt +418dcfbe17d5f4b454b18630be795462cf7da4ceb6313afa49451aa2568e41f7ca3d34cf0280c7d056dc5681a70c37586aa1755620520b9198eede905ba2d0f6 bagit.txt +82e79660ff0335b94f56a0b837b1564291b3eb2242a8c28c51d32efc6db9db5ab6fe7656d975bf16989c67ef3a3fa5cbd066eb1bcf422207ec41a20ed0f98943 manifest-sha512.txt From a515359c65e3ee133b3f9fc6f7cc0612f0241177 Mon Sep 17 00:00:00 2001 From: John Scancella Date: Tue, 24 Jul 2018 12:58:58 -0400 Subject: [PATCH 006/104] refs #119 - reverting change to name of message digests --- .../gov/loc/repository/bagit/BagitSuiteComplanceTest.java | 1 + .../bagit/hash/StandardSupportedAlgorithms.java | 8 ++++---- .../java/gov/loc/repository/bagit/TempFolderTest.java | 5 ++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/integration/java/gov/loc/repository/bagit/BagitSuiteComplanceTest.java b/src/integration/java/gov/loc/repository/bagit/BagitSuiteComplanceTest.java index 24cd32220..eb8827dbe 100644 --- a/src/integration/java/gov/loc/repository/bagit/BagitSuiteComplanceTest.java +++ b/src/integration/java/gov/loc/repository/bagit/BagitSuiteComplanceTest.java @@ -147,6 +147,7 @@ public void testReadWriteProducesSameBag() throws Exception{ testTagFileContents(bag, newBagDir); testBagsStructureAreEqual(bagDir, newBagDir); + delete(newBagDir); } } diff --git a/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java b/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java index 32df24a7e..40818316c 100644 --- a/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java +++ b/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java @@ -5,10 +5,10 @@ */ public enum StandardSupportedAlgorithms implements SupportedAlgorithm{ MD5("MD5"), - SHA1("SHA1"), - SHA224("SHA224"), - SHA256("SHA256"), - SHA512("SHA512"); + SHA1("SHA-1"), + SHA224("SHA-224"), + SHA256("SHA-256"), + SHA512("SHA-512"); private final String messageDigestName; diff --git a/src/test/java/gov/loc/repository/bagit/TempFolderTest.java b/src/test/java/gov/loc/repository/bagit/TempFolderTest.java index 591345d2a..80b3a73c2 100644 --- a/src/test/java/gov/loc/repository/bagit/TempFolderTest.java +++ b/src/test/java/gov/loc/repository/bagit/TempFolderTest.java @@ -8,6 +8,7 @@ import java.nio.file.attribute.BasicFileAttributes; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; abstract public class TempFolderTest { @@ -21,6 +22,8 @@ public void setupTempFolder() throws IOException{ @AfterEach public void teardownTempFolder() throws IOException{ delete(folder); + Assertions.assertFalse(Files.exists(folder)); + //Assertions.assertEquals(0, Files.list(folder).count()); } public Path createDirectory(String name) throws IOException { @@ -33,7 +36,7 @@ public Path createFile(String name) throws IOException { return Files.createFile(newFile); } - private void delete(Path tempDirectory) throws IOException { + protected void delete(Path tempDirectory) throws IOException { Files.walkFileTree(tempDirectory, new SimpleFileVisitor() { @Override From c782e26dcfdc51efce7d9b64375aa4d01bdd332e Mon Sep 17 00:00:00 2001 From: John Scancella Date: Tue, 24 Jul 2018 13:03:00 -0400 Subject: [PATCH 007/104] Fixed error of integration tests not being run after converting to junit 5 --- code-quality.gradle | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/code-quality.gradle b/code-quality.gradle index eee506764..e8c0a7026 100644 --- a/code-quality.gradle +++ b/code-quality.gradle @@ -22,7 +22,17 @@ task integrationTest(type: Test, dependsOn: "cloneConformanceSuite") { description "Runs the integration tests." testClassesDirs = sourceSets.integrationTest.output.classesDirs classpath = sourceSets.integrationTest.runtimeClasspath - testLogging.showStandardStreams = true + //testLogging.showStandardStreams = true + + testLogging { + events "passed", "skipped", "failed" + } + useJUnitPlatform() + + jacoco { + destinationFile = file("$buildDir/jacoco/integrationTest.exec") + classDumpDir = file("$buildDir/classes/integrationTest") + } } jacocoTestReport.dependsOn integrationTest //include the integration tests in the code coverage reports From c2e6025e1035d6dfbbbaaa5250218f8e0bbae5ec Mon Sep 17 00:00:00 2001 From: John Scancella Date: Tue, 24 Jul 2018 15:42:49 -0400 Subject: [PATCH 008/104] don't do code coverage on integration tests --- code-quality.gradle | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/code-quality.gradle b/code-quality.gradle index e8c0a7026..899b67114 100644 --- a/code-quality.gradle +++ b/code-quality.gradle @@ -23,16 +23,11 @@ task integrationTest(type: Test, dependsOn: "cloneConformanceSuite") { testClassesDirs = sourceSets.integrationTest.output.classesDirs classpath = sourceSets.integrationTest.runtimeClasspath //testLogging.showStandardStreams = true + useJUnitPlatform() testLogging { events "passed", "skipped", "failed" } - useJUnitPlatform() - - jacoco { - destinationFile = file("$buildDir/jacoco/integrationTest.exec") - classDumpDir = file("$buildDir/classes/integrationTest") - } } jacocoTestReport.dependsOn integrationTest //include the integration tests in the code coverage reports From a06001ce68b91b2f532a9ec23979f631621ce309 Mon Sep 17 00:00:00 2001 From: John Scancella Date: Fri, 27 Jul 2018 12:57:27 -0400 Subject: [PATCH 009/104] refs #122 - skip files that are hidden when checking if file is in at least one manifest when option is enabled --- .../repository/bagit/verify/BagVerifier.java | 4 ++- ...dFileExistsInAtLeastOneManifestVistor.java | 17 +++++++--- src/main/resources/MessageBundle.properties | 3 ++ .../loc/repository/bagit/TempFolderTest.java | 18 ++++++++++ .../gov/loc/repository/bagit/TestUtils.java | 6 ++++ .../bagit/verify/BagVerifierTest.java | 33 +++++++++++++++++++ 6 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java b/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java index 663cd8b8f..8e5e831bb 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java +++ b/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java @@ -17,6 +17,7 @@ import gov.loc.repository.bagit.domain.Bag; import gov.loc.repository.bagit.domain.Manifest; import gov.loc.repository.bagit.exceptions.CorruptChecksumException; +import gov.loc.repository.bagit.exceptions.FileNotInManifestException; import gov.loc.repository.bagit.exceptions.FileNotInPayloadDirectoryException; import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; import gov.loc.repository.bagit.exceptions.InvalidPayloadOxumException; @@ -120,6 +121,7 @@ public static void quicklyVerify(final Bag bag) throws IOException, InvalidPaylo * * @throws CorruptChecksumException when the computed hash doesn't match given hash * @throws IOException if there was an error with the file + * @throws FileNotInManifestException if a file is found in the payload directory but not in manifest(s) * @throws MissingPayloadManifestException if there is not at least one payload manifest * @throws MissingBagitFileException if there is no bagit.txt file * @throws MissingPayloadDirectoryException if there is no /data directory @@ -130,7 +132,7 @@ public static void quicklyVerify(final Bag bag) throws IOException, InvalidPaylo * @throws UnsupportedAlgorithmException if the manifest uses a algorithm that isn't supported * @throws InvalidBagitFileFormatException if the manifest is not formatted properly */ - public void isValid(final Bag bag, final boolean ignoreHiddenFiles) throws IOException, MissingPayloadManifestException, MissingBagitFileException, MissingPayloadDirectoryException, FileNotInPayloadDirectoryException, InterruptedException, MaliciousPathException, CorruptChecksumException, VerificationException, UnsupportedAlgorithmException, InvalidBagitFileFormatException{ + public void isValid(final Bag bag, final boolean ignoreHiddenFiles) throws IOException, FileNotInManifestException, MissingPayloadManifestException, MissingBagitFileException, MissingPayloadDirectoryException, FileNotInPayloadDirectoryException, InterruptedException, MaliciousPathException, CorruptChecksumException, VerificationException, UnsupportedAlgorithmException, InvalidBagitFileFormatException{ logger.info(messages.getString("checking_bag_is_valid"), bag.getRootDir()); isComplete(bag, ignoreHiddenFiles); diff --git a/src/main/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistor.java b/src/main/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistor.java index 0bf8cd0e3..ea6077f6c 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistor.java +++ b/src/main/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistor.java @@ -1,10 +1,12 @@ package gov.loc.repository.bagit.verify; +import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import java.util.ResourceBundle; import java.util.Set; import org.slf4j.helpers.MessageFormatter; @@ -15,6 +17,7 @@ * Implements {@link SimpleFileVisitor} to ensure that the encountered file is in one of the manifests. */ public class PayloadFileExistsInAtLeastOneManifestVistor extends AbstractPayloadFileExistsInManifestsVistor { + private static final ResourceBundle messages = ResourceBundle.getBundle("MessageBundle"); private transient final Set filesListedInManifests; public PayloadFileExistsInAtLeastOneManifestVistor(final Set filesListedInManifests, final boolean ignoreHiddenFiles) { @@ -23,12 +26,18 @@ public PayloadFileExistsInAtLeastOneManifestVistor(final Set filesListedIn } @Override - public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs)throws FileNotInManifestException{ - if(Files.isRegularFile(path) && !filesListedInManifests.contains(path.normalize())){ + public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs)throws IOException, FileNotInManifestException{ + if(Files.isHidden(path) && ignoreHiddenFiles){ + logger.debug(messages.getString("skipping_hidden_file"), path); + } + else { + if(Files.isRegularFile(path) && !filesListedInManifests.contains(path.normalize())){ final String formattedMessage = messages.getString("file_not_in_any_manifest_error"); throw new FileNotInManifestException(MessageFormatter.format(formattedMessage, path).getMessage()); } - logger.debug("[{}] is in at least one manifest", path); - return FileVisitResult.CONTINUE; + logger.debug(messages.getString("file_in_at_least_one_manifest"), path); + } + return FileVisitResult.CONTINUE; } + } diff --git a/src/main/resources/MessageBundle.properties b/src/main/resources/MessageBundle.properties index 4973bd2e8..0c69d7953 100644 --- a/src/main/resources/MessageBundle.properties +++ b/src/main/resources/MessageBundle.properties @@ -182,6 +182,9 @@ file_not_in_manifest_error=File [{}] is in the payload directory but isn't liste file_in_all_manifests=[{}] is in all manifests. file_not_in_any_manifest_error=File [{}] is in the payload directory but isn't listed in any manifest! +#for PayloadFileExistsInAtLeastOneManifestVistor.java +file_in_at_least_one_manifest="[{}] is in at least one manifest" + #for PayloadVerifier.java all_files_in_manifests=Getting all files listed in the manifest(s). get_listing_in_manifest=Getting files and checksums listed in [{}]. diff --git a/src/test/java/gov/loc/repository/bagit/TempFolderTest.java b/src/test/java/gov/loc/repository/bagit/TempFolderTest.java index 80b3a73c2..471c21039 100644 --- a/src/test/java/gov/loc/repository/bagit/TempFolderTest.java +++ b/src/test/java/gov/loc/repository/bagit/TempFolderTest.java @@ -36,6 +36,24 @@ public Path createFile(String name) throws IOException { return Files.createFile(newFile); } + public Path copyBagToTempFolder(Path bagFolder) throws IOException{ + Path bagCopyDir = createDirectory(bagFolder.getFileName() + "_copy"); + Files.walkFileTree(bagFolder, new SimpleFileVisitor() { + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Path relative = bagFolder.relativize(file); + if(relative.getParent() != null) { + Files.createDirectories(bagCopyDir.resolve(relative.getParent())); + } + Files.copy(file, bagCopyDir.resolve(relative)); + return FileVisitResult.CONTINUE; + } + }); + + return bagCopyDir; + } + protected void delete(Path tempDirectory) throws IOException { Files.walkFileTree(tempDirectory, new SimpleFileVisitor() { diff --git a/src/test/java/gov/loc/repository/bagit/TestUtils.java b/src/test/java/gov/loc/repository/bagit/TestUtils.java index 619760301..3c29083ad 100644 --- a/src/test/java/gov/loc/repository/bagit/TestUtils.java +++ b/src/test/java/gov/loc/repository/bagit/TestUtils.java @@ -12,6 +12,12 @@ public static boolean isExecutingOnWindows(){ return System.getProperty("os.name").contains("Windows"); } + /** + * walk a directory and make sure that files/folders are hidden if they start with a . on windows. + * + * @param startingDir the directory to start walking + * @throws IOException if there is a problem setting the file/folder to be hidden + */ public static void makeFilesHiddenOnWindows(Path startingDir) throws IOException { if (isExecutingOnWindows()) { Files.walkFileTree(startingDir, new SimpleFileVisitor() { diff --git a/src/test/java/gov/loc/repository/bagit/verify/BagVerifierTest.java b/src/test/java/gov/loc/repository/bagit/verify/BagVerifierTest.java index 68955a149..8099091b4 100644 --- a/src/test/java/gov/loc/repository/bagit/verify/BagVerifierTest.java +++ b/src/test/java/gov/loc/repository/bagit/verify/BagVerifierTest.java @@ -1,6 +1,7 @@ package gov.loc.repository.bagit.verify; import java.io.File; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.Security; @@ -12,9 +13,11 @@ import org.junit.jupiter.api.Test; import gov.loc.repository.bagit.TempFolderTest; +import gov.loc.repository.bagit.TestUtils; import gov.loc.repository.bagit.domain.Bag; import gov.loc.repository.bagit.domain.Manifest; import gov.loc.repository.bagit.exceptions.CorruptChecksumException; +import gov.loc.repository.bagit.exceptions.FileNotInManifestException; import gov.loc.repository.bagit.exceptions.UnsupportedAlgorithmException; import gov.loc.repository.bagit.exceptions.VerificationException; import gov.loc.repository.bagit.hash.StandardSupportedAlgorithms; @@ -33,6 +36,36 @@ public class BagVerifierTest extends TempFolderTest{ private BagVerifier sut = new BagVerifier(); private BagReader reader = new BagReader(); + @Test + public void testValidWhenHiddenFolderNotIncluded() throws Exception{ + Path copyDir = copyBagToTempFolder(rootDir); + Files.createDirectory(copyDir.resolve("data").resolve(".someHiddenFolder")); + TestUtils.makeFilesHiddenOnWindows(copyDir); + + Bag bag = reader.read(copyDir); + sut.isValid(bag, true); + } + + @Test + public void testValidWithHiddenFile() throws Exception{ + Path copyDir = copyBagToTempFolder(rootDir); + Files.createFile(copyDir.resolve("data").resolve(".someHiddenFile")); + TestUtils.makeFilesHiddenOnWindows(copyDir); + + Bag bag = reader.read(copyDir); + sut.isValid(bag, true); + } + + @Test + public void testInvalidWithHiddenFile() throws Exception{ + Path copyDir = copyBagToTempFolder(rootDir); + Files.createFile(copyDir.resolve("data").resolve(".someHiddenFile")); + TestUtils.makeFilesHiddenOnWindows(copyDir); + + Bag bag = reader.read(copyDir); + Assertions.assertThrows(FileNotInManifestException.class, () -> { sut.isValid(bag, false); }); + } + @Test public void testStandardSupportedAlgorithms() throws Exception{ List algorithms = Arrays.asList("md5", "sha1", "sha256", "sha512"); From c2c829c8018cbedc479ba44bb6a2e3589ebe7c76 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Mon, 12 Nov 2018 10:32:49 +0100 Subject: [PATCH 010/104] Adapt parsing Bagit Profile due to specification. (https://github.com/bagit-profiles/bagit-profiles) Inclusion of "Contact-Name," "Contact-Phone" and "Contact-Email," as defined in the BagIt spec, is not required but is encouraged. -> Add "Contact-Phone" -> "Contact-Name" and "Contact-Email" are now optional Add test for minimal profile Adapt other tests. Bag-Info: The parameters "required" is 'false' and "repeatable" is 'true' by default. Changed implementation accordingly. ("repeatable": Not used yet inside the library!?) --- .../profile/BagInfoRequirement.java | 10 +- .../conformance/profile/BagitProfile.java | 13 +- .../profile/BagitProfileDeserializer.java | 155 ++++++++------- src/main/resources/MessageBundle.properties | 1 + .../resources/MessageBundle_ar.properties | 1 + .../resources/MessageBundle_de_DE.properties | 1 + .../resources/MessageBundle_es_ES.properties | 1 + .../resources/MessageBundle_zh.properties | 1 + .../profile/AbstractBagitProfileTest.java | 49 +++-- .../profile/BagitProfileDeserializerTest.java | 26 +++ .../conformance/profile/BagitProfileTest.java | 5 + .../bagitProfiles/exampleProfile.json | 180 ++++++++++-------- .../exampleProfileOnlyRequiredFields.json | 91 +++++++++ 13 files changed, 367 insertions(+), 167 deletions(-) create mode 100644 src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java index 155c2ab03..e9656f9a8 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java +++ b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java @@ -8,9 +8,9 @@ * This class is used to define elements in a bag-info.txt file used by a bagit-profile. */ public class BagInfoRequirement { - private boolean required; + private boolean required = false; private List acceptableValues = new ArrayList<>(); - private boolean repeatable; + private boolean repeatable = true; @Override public boolean equals(final Object other) { @@ -37,6 +37,12 @@ public BagInfoRequirement(final boolean required, final List acceptableV this.acceptableValues = acceptableValues; } + public BagInfoRequirement(final boolean required, final List acceptableValues, boolean repeatable){ + this.required = required; + this.acceptableValues = acceptableValues; + this.repeatable = repeatable; + } + @Override public String toString() { return "[required=" + required + ", acceptableValues=" + acceptableValues + ", repeatable=" + repeatable + "]"; diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfile.java b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfile.java index 1ed5158a9..2470a5cbb 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfile.java +++ b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfile.java @@ -16,6 +16,7 @@ public class BagitProfile { private String externalDescription = ""; private String contactName = ""; private String contactEmail = ""; + private String contactPhone = ""; private String version = ""; private Map bagInfoRequirements = new HashMap<>(); @@ -38,6 +39,7 @@ public boolean equals(final Object other) { && Objects.equals(sourceOrganization, castOther.sourceOrganization) && Objects.equals(externalDescription, castOther.externalDescription) && Objects.equals(contactName, castOther.contactName) && Objects.equals(contactEmail, castOther.contactEmail) + && Objects.equals(contactPhone, castOther.contactPhone) && Objects.equals(version, castOther.version) && Objects.equals(bagInfoRequirements, castOther.bagInfoRequirements) && Objects.equals(manifestTypesRequired, castOther.manifestTypesRequired) @@ -50,15 +52,14 @@ public boolean equals(final Object other) { } @Override public int hashCode() { - return Objects.hash(bagitProfileIdentifier, sourceOrganization, externalDescription, contactName, contactEmail, - version, bagInfoRequirements, manifestTypesRequired, fetchFileAllowed, serialization, + return Objects.hash(bagitProfileIdentifier, sourceOrganization, externalDescription, contactName, contactEmail, contactPhone, version, bagInfoRequirements, manifestTypesRequired, fetchFileAllowed, serialization, acceptableMIMESerializationTypes, acceptableBagitVersions, tagManifestTypesRequired, tagFilesRequired); } @Override public String toString() { return "BagitProfile [bagitProfileIdentifier=" + bagitProfileIdentifier + ", sourceOrganization=" + sourceOrganization + ", externalDescription=" + externalDescription + ", contactName=" + contactName - + ", contactEmail=" + contactEmail + ", version=" + version + ", bagInfoRequirements=" + bagInfoRequirements + + ", contactEmail=" + contactEmail + ", contactPhone=" + contactPhone + ", version=" + version + ", bagInfoRequirements=" + bagInfoRequirements + ", manifestTypesRequired=" + manifestTypesRequired + ", fetchFileAllowed=" + fetchFileAllowed + ", serialization=" + serialization + ", acceptableMIMESerializationTypes=" + acceptableMIMESerializationTypes + ", acceptableBagitVersions=" + acceptableBagitVersions + ", tagManifestTypesRequired=" @@ -143,6 +144,12 @@ public String getContactEmail() { public void setContactEmail(final String contactEmail) { this.contactEmail = contactEmail; } + public String getContactPhone() { + return contactPhone; + } + public void setContactPhone(String contactPhone) { + this.contactPhone = contactPhone; + } public String getVersion() { return version; } diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java index 4684d02bc..928d8e86c 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java +++ b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java @@ -19,9 +19,10 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer; /** - * Deserialize bagit profile json to a {@link BagitProfile} + * Deserialize bagit profile json to a {@link BagitProfile} */ public class BagitProfileDeserializer extends StdDeserializer { + private static final long serialVersionUID = 1L; private static final Logger logger = LoggerFactory.getLogger(BagitProfileDeserializer.class); private static final ResourceBundle messages = ResourceBundle.getBundle("MessageBundle"); @@ -36,149 +37,173 @@ public BagitProfileDeserializer(final Class vc) { @Override public BagitProfile deserialize(final JsonParser p, final DeserializationContext ctxt) - throws IOException, JsonProcessingException { + throws IOException, JsonProcessingException { final BagitProfile profile = new BagitProfile(); final JsonNode node = p.getCodec().readTree(p); - + parseBagitProfileInfo(node, profile); - + profile.setBagInfoRequirements(parseBagInfo(node)); - + profile.getManifestTypesRequired().addAll(parseManifestTypesRequired(node)); - + profile.setFetchFileAllowed(node.get("Allow-Fetch.txt").asBoolean()); logger.debug(messages.getString("fetch_allowed"), profile.isFetchFileAllowed()); - + profile.setSerialization(Serialization.valueOf(node.get("Serialization").asText())); - logger.debug(messages.getString("serialization_allowed"),profile.getSerialization()); - + logger.debug(messages.getString("serialization_allowed"), profile.getSerialization()); + profile.getAcceptableMIMESerializationTypes().addAll(parseAcceptableSerializationFormats(node)); - + profile.getTagManifestTypesRequired().addAll(parseRequiredTagmanifestTypes(node)); - + profile.getTagFilesRequired().addAll(parseRequiredTagFiles(node)); - + profile.getAcceptableBagitVersions().addAll(parseAcceptableVersions(node)); - + return profile; } - - private static void parseBagitProfileInfo(final JsonNode node, final BagitProfile profile){ + + private static void parseBagitProfileInfo(final JsonNode node, final BagitProfile profile) { final JsonNode bagitProfileInfoNode = node.get("BagIt-Profile-Info"); logger.debug(messages.getString("parsing_bagit_profile_info_section")); - + + // Read required tags first + // due to specification defined at https://github.com/bagit-profiles/bagit-profiles final String profileIdentifier = bagitProfileInfoNode.get("BagIt-Profile-Identifier").asText(); logger.debug(messages.getString("identifier"), profileIdentifier); profile.setBagitProfileIdentifier(profileIdentifier); - + final String sourceOrg = bagitProfileInfoNode.get("Source-Organization").asText(); logger.debug(messages.getString("source_organization"), sourceOrg); profile.setSourceOrganization(sourceOrg); - - final String contactName = bagitProfileInfoNode.get("Contact-Name").asText(); - logger.debug(messages.getString("contact_name"), contactName); - profile.setContactName(contactName); - - final String contactEmail = bagitProfileInfoNode.get("Contact-Email").asText(); - logger.debug(messages.getString("contact_email"), contactEmail); - profile.setContactEmail(contactEmail); - + final String extDescript = bagitProfileInfoNode.get("External-Description").asText(); logger.debug(messages.getString("external_description"), extDescript); profile.setExternalDescription(extDescript); - + final String version = bagitProfileInfoNode.get("Version").asText(); logger.debug(messages.getString("version"), version); profile.setVersion(version); + + final JsonNode contactNameNode = bagitProfileInfoNode.get("Contact-Name"); + if (contactNameNode != null) { + final String contactName = contactNameNode.asText(); + logger.debug(messages.getString("contact_name"), contactName); + profile.setContactName(contactName); + } + + final JsonNode contactEmailNode = bagitProfileInfoNode.get("Contact-Email"); + if (contactEmailNode != null) { + final String contactEmail = contactEmailNode.asText(); + logger.debug(messages.getString("contact_email"), contactEmail); + profile.setContactEmail(contactEmail); + } + + final JsonNode contactPhoneNode = bagitProfileInfoNode.get("Contact-Phone"); + if (contactPhoneNode != null) { + final String contactPhone = contactPhoneNode.asText(); + logger.debug(messages.getString("contact_phone"), contactPhone); + profile.setContactPhone(contactPhone); + } } - + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - private static Map parseBagInfo(final JsonNode rootNode){ + private static Map parseBagInfo(final JsonNode rootNode) { final JsonNode bagInfoNode = rootNode.get("Bag-Info"); logger.debug(messages.getString("parsing_bag_info")); - final Map bagInfo = new HashMap<>(); - + final Map bagInfo = new HashMap<>(); + final Iterator> nodes = bagInfoNode.fields(); //stuck in java 6... - - while(nodes.hasNext()){ + + while (nodes.hasNext()) { final Entry node = nodes.next(); - + final BagInfoRequirement entry = new BagInfoRequirement(); - entry.setRequired(node.getValue().get("required").asBoolean()); - + // due to specification required is false by default. + final JsonNode requiredNode = node.getValue().get("required"); + if (requiredNode != null) { + entry.setRequired(requiredNode.asBoolean()); + } + final JsonNode valuesNode = node.getValue().get("values"); - if(valuesNode != null){ - for(final JsonNode value : valuesNode){ + if (valuesNode != null) { + for (final JsonNode value : valuesNode) { entry.getAcceptableValues().add(value.asText()); } } - + + final JsonNode repeatableNode = node.getValue().get("repeatable"); + if (repeatableNode != null) { + entry.setRepeatable(repeatableNode.asBoolean()); + } + logger.debug("{}: {}", node.getKey(), entry); bagInfo.put(node.getKey(), entry); } - + return bagInfo; } - - private static List parseManifestTypesRequired(final JsonNode node){ + + private static List parseManifestTypesRequired(final JsonNode node) { final JsonNode manifests = node.get("Manifests-Required"); - + final List manifestTypes = new ArrayList<>(); - + for (final JsonNode manifestName : manifests) { manifestTypes.add(manifestName.asText()); } - + logger.debug(messages.getString("required_manifest_types"), manifestTypes); - + return manifestTypes; } - - private static List parseAcceptableSerializationFormats(final JsonNode node){ + + private static List parseAcceptableSerializationFormats(final JsonNode node) { final JsonNode serialiationFormats = node.get("Accept-Serialization"); final List serialTypes = new ArrayList<>(); - + for (final JsonNode serialiationFormat : serialiationFormats) { serialTypes.add(serialiationFormat.asText()); } logger.debug(messages.getString("acceptable_serialization_mime_types"), serialTypes); - + return serialTypes; } - - private static List parseRequiredTagmanifestTypes(final JsonNode node){ + + private static List parseRequiredTagmanifestTypes(final JsonNode node) { final JsonNode tagManifestsRequiredNodes = node.get("Tag-Manifests-Required"); final List requiredTagmanifestTypes = new ArrayList<>(); - - for(final JsonNode tagManifestsRequiredNode : tagManifestsRequiredNodes){ + + for (final JsonNode tagManifestsRequiredNode : tagManifestsRequiredNodes) { requiredTagmanifestTypes.add(tagManifestsRequiredNode.asText()); } logger.debug(messages.getString("required_tagmanifest_types"), requiredTagmanifestTypes); - + return requiredTagmanifestTypes; } - - private static List parseRequiredTagFiles(final JsonNode node){ + + private static List parseRequiredTagFiles(final JsonNode node) { final JsonNode tagFilesRequiredNodes = node.get("Tag-Files-Required"); final List requiredTagFiles = new ArrayList<>(); - - for(final JsonNode tagFilesRequiredNode : tagFilesRequiredNodes){ + + for (final JsonNode tagFilesRequiredNode : tagFilesRequiredNodes) { requiredTagFiles.add(tagFilesRequiredNode.asText()); } logger.debug(messages.getString("tag_files_required"), requiredTagFiles); - + return requiredTagFiles; } - - private static List parseAcceptableVersions(final JsonNode node){ + + private static List parseAcceptableVersions(final JsonNode node) { final JsonNode acceptableVersionsNodes = node.get("Accept-BagIt-Version"); final List acceptableVersions = new ArrayList<>(); - - for(final JsonNode acceptableVersionsNode : acceptableVersionsNodes){ + + for (final JsonNode acceptableVersionsNode : acceptableVersionsNodes) { acceptableVersions.add(acceptableVersionsNode.asText()); } logger.debug(messages.getString("acceptable_bagit_versions"), acceptableVersions); - + return acceptableVersions; } } diff --git a/src/main/resources/MessageBundle.properties b/src/main/resources/MessageBundle.properties index 4973bd2e8..a77720d16 100644 --- a/src/main/resources/MessageBundle.properties +++ b/src/main/resources/MessageBundle.properties @@ -8,6 +8,7 @@ identifier=Identifier is [{}] source_organization=Source-Organization is [{}] contact_name=Contact-Name is [{}] contact_email=Contact-Email is [{}] +contact_phone=Contact-Phone is [{}] external_description=External-Description is [{}] version=Version is [{}] parsing_bag_info=Parsing the Bag-Info section diff --git a/src/main/resources/MessageBundle_ar.properties b/src/main/resources/MessageBundle_ar.properties index 34868ae7f..4bb857652 100644 --- a/src/main/resources/MessageBundle_ar.properties +++ b/src/main/resources/MessageBundle_ar.properties @@ -9,6 +9,7 @@ identifier=Identifier is [{}] source_organization=Source-Organization is [{}] contact_name=Contact-Name is [{}] contact_email=Contact-Email is [{}] +contact_phone=Contact-Phone is [{}] external_description=External-Description is [{}] version=Version is [{}] parsing_bag_info=Parsing the Bag-Info section diff --git a/src/main/resources/MessageBundle_de_DE.properties b/src/main/resources/MessageBundle_de_DE.properties index e7488a22d..a0a29095b 100644 --- a/src/main/resources/MessageBundle_de_DE.properties +++ b/src/main/resources/MessageBundle_de_DE.properties @@ -8,6 +8,7 @@ identifier=Identifier hat den Wert [{}] source_organization=Source-Organization hat den Wert [{}] contact_name=Contact-Name hat den Wert [{}] contact_email=Contact-Email hat den Wert [{}] +contact_phone=Contact-Phone hat den Wert [{}] external_description=External-Description hat den Wert [{}] version=Version hat den Wert [{}] parsing_bag_info=Lese Abschnitt Bag-Info diff --git a/src/main/resources/MessageBundle_es_ES.properties b/src/main/resources/MessageBundle_es_ES.properties index c63efd1d3..16f23fd86 100644 --- a/src/main/resources/MessageBundle_es_ES.properties +++ b/src/main/resources/MessageBundle_es_ES.properties @@ -9,6 +9,7 @@ identifier=Identificador es [{}] source_organization=Organizaci\u00f3n de la fuente es [{}] contact_name=Nombre del contacto es [{}] contact_email=Email del contacto es [{}] +contact_phone=Tel\u00e9fono de contacto es [{}] external_description=Descripci\u00f3n externa es [{}] version=La versi\u00f3n es [{}] parsing_bag_info=An\u00e1lisis de la secci\u00f3n de la bag-info diff --git a/src/main/resources/MessageBundle_zh.properties b/src/main/resources/MessageBundle_zh.properties index 34868ae7f..4bb857652 100644 --- a/src/main/resources/MessageBundle_zh.properties +++ b/src/main/resources/MessageBundle_zh.properties @@ -9,6 +9,7 @@ identifier=Identifier is [{}] source_organization=Source-Organization is [{}] contact_name=Contact-Name is [{}] contact_email=Contact-Email is [{}] +contact_phone=Contact-Phone is [{}] external_description=External-Description is [{}] version=Version is [{}] parsing_bag_info=Parsing the Bag-Info section diff --git a/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java b/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java index 3bba614eb..230751b30 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java +++ b/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java @@ -26,6 +26,27 @@ protected BagitProfile createExpectedProfile(){ expectedProfile.setBagitProfileIdentifier("http://canadiana.org/standards/bagit/tdr_ingest.json"); expectedProfile.setContactEmail("tdr@canadiana.com"); expectedProfile.setContactName("William Wueppelmann"); + expectedProfile.setContactPhone("+1 613 907 7040"); + expectedProfile.setExternalDescription("BagIt profile for ingesting content into the C.O. TDR loading dock."); + expectedProfile.setSourceOrganization("Candiana.org"); + expectedProfile.setVersion("1.2"); + + expectedProfile.setBagInfoRequirements(createBagInfo()); + expectedProfile.setManifestTypesRequired(Arrays.asList("md5")); + expectedProfile.setFetchFileAllowed(false); + expectedProfile.setSerialization(Serialization.forbidden); + expectedProfile.setAcceptableMIMESerializationTypes(Arrays.asList("application/zip")); + expectedProfile.setAcceptableBagitVersions(Arrays.asList("0.96")); + expectedProfile.setTagManifestTypesRequired(Arrays.asList("md5")); + expectedProfile.setTagFilesRequired(Arrays.asList("DPN/dpnFirstNode.txt", "DPN/dpnRegistry")); + + return expectedProfile; + } + + protected BagitProfile createMinimalProfile(){ + BagitProfile expectedProfile = new BagitProfile(); + + expectedProfile.setBagitProfileIdentifier("http://canadiana.org/standards/bagit/tdr_ingest.json"); expectedProfile.setExternalDescription("BagIt profile for ingesting content into the C.O. TDR loading dock."); expectedProfile.setSourceOrganization("Candiana.org"); expectedProfile.setVersion("1.2"); @@ -45,21 +66,21 @@ protected BagitProfile createExpectedProfile(){ protected Map createBagInfo(){ Map info = new HashMap<>(); - info.put("Source-Organization", new BagInfoRequirement(true, Arrays.asList("Simon Fraser University", "York University"))); + info.put("Source-Organization", new BagInfoRequirement(true, Arrays.asList("Simon Fraser University", "York University"), false)); info.put("Organization-Address", new BagInfoRequirement(true, - Arrays.asList("8888 University Drive Burnaby, B.C. V5A 1S6 Canada", "4700 Keele Street Toronto, Ontario M3J 1P3 Canada"))); - info.put("Contact-Name", new BagInfoRequirement(true, Arrays.asList("Mark Jordan", "Nick Ruest"))); - info.put("Contact-Phone", new BagInfoRequirement(false, Arrays.asList())); - info.put("Contact-Email", new BagInfoRequirement(true, Arrays.asList())); - info.put("External-Description", new BagInfoRequirement(true, Arrays.asList())); - info.put("External-Identifier", new BagInfoRequirement(false, Arrays.asList())); - info.put("Bag-Size", new BagInfoRequirement(true, Arrays.asList())); - info.put("Bag-Group-Identifier", new BagInfoRequirement(false, Arrays.asList())); - info.put("Bag-Count", new BagInfoRequirement(true, Arrays.asList())); - info.put("Internal-Sender-Identifier", new BagInfoRequirement(false, Arrays.asList())); - info.put("Internal-Sender-Description", new BagInfoRequirement(false, Arrays.asList())); - info.put("Bagging-Date", new BagInfoRequirement(true, Arrays.asList())); - info.put("Payload-Oxum", new BagInfoRequirement(true, Arrays.asList())); + Arrays.asList("8888 University Drive Burnaby, B.C. V5A 1S6 Canada", "4700 Keele Street Toronto, Ontario M3J 1P3 Canada"), false)); + info.put("Contact-Name", new BagInfoRequirement(true, Arrays.asList("Mark Jordan", "Nick Ruest"), false)); + info.put("Contact-Phone", new BagInfoRequirement(false, Arrays.asList(), false)); + info.put("Contact-Email", new BagInfoRequirement(true, Arrays.asList(), false)); + info.put("External-Description", new BagInfoRequirement(true, Arrays.asList(), false)); + info.put("External-Identifier", new BagInfoRequirement(false, Arrays.asList(), false)); + info.put("Bag-Size", new BagInfoRequirement(true, Arrays.asList(), false)); + info.put("Bag-Group-Identifier", new BagInfoRequirement(false, Arrays.asList(), false)); + info.put("Bag-Count", new BagInfoRequirement(true, Arrays.asList(), false)); + info.put("Internal-Sender-Identifier", new BagInfoRequirement(false, Arrays.asList(), false)); + info.put("Internal-Sender-Description", new BagInfoRequirement(false, Arrays.asList(), false)); + info.put("Bagging-Date", new BagInfoRequirement(true, Arrays.asList(), false)); + info.put("Payload-Oxum", new BagInfoRequirement(true, Arrays.asList(), false)); return info; } diff --git a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializerTest.java b/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializerTest.java index 1073a2f68..8ac1e2f9e 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializerTest.java +++ b/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializerTest.java @@ -20,6 +20,7 @@ public void testDeserialize() throws Exception{ Assertions.assertEquals(expectedProfile.getBagitProfileIdentifier(), profile.getBagitProfileIdentifier()); Assertions.assertEquals(expectedProfile.getContactEmail(), profile.getContactEmail()); Assertions.assertEquals(expectedProfile.getContactName(), profile.getContactName()); + Assertions.assertEquals(expectedProfile.getContactPhone(), profile.getContactPhone()); Assertions.assertEquals(expectedProfile.getExternalDescription(), profile.getExternalDescription()); Assertions.assertEquals(expectedProfile.getManifestTypesRequired(), profile.getManifestTypesRequired()); Assertions.assertEquals(expectedProfile.getSerialization(), profile.getSerialization()); @@ -30,5 +31,30 @@ public void testDeserialize() throws Exception{ Assertions.assertEquals(expectedProfile.hashCode(), profile.hashCode()); } + @Test + public void testDeserializeWithoutOptionalTags() throws Exception{ + BagitProfile minimalProfile = createMinimalProfile(); + + BagitProfile profile = mapper.readValue(new File("src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json"), BagitProfile.class); + System.err.println(minimalProfile.toString()); + System.err.println(profile.toString()); + Assertions.assertEquals(minimalProfile, profile); + Assertions.assertEquals(minimalProfile.getAcceptableBagitVersions(), profile.getAcceptableBagitVersions()); + Assertions.assertEquals(minimalProfile.getAcceptableMIMESerializationTypes(), profile.getAcceptableMIMESerializationTypes()); + Assertions.assertEquals(minimalProfile.getBagInfoRequirements(), profile.getBagInfoRequirements()); + Assertions.assertEquals(minimalProfile.getBagitProfileIdentifier(), profile.getBagitProfileIdentifier()); + Assertions.assertEquals(minimalProfile.getContactEmail(), profile.getContactEmail()); + Assertions.assertEquals(minimalProfile.getContactName(), profile.getContactName()); + Assertions.assertEquals(minimalProfile.getContactPhone(), profile.getContactPhone()); + Assertions.assertEquals(minimalProfile.getExternalDescription(), profile.getExternalDescription()); + Assertions.assertEquals(minimalProfile.getManifestTypesRequired(), profile.getManifestTypesRequired()); + Assertions.assertEquals(minimalProfile.getSerialization(), profile.getSerialization()); + Assertions.assertEquals(minimalProfile.getSourceOrganization(), profile.getSourceOrganization()); + Assertions.assertEquals(minimalProfile.getTagFilesRequired(), profile.getTagFilesRequired()); + Assertions.assertEquals(minimalProfile.getTagManifestTypesRequired(), profile.getTagManifestTypesRequired()); + Assertions.assertEquals(minimalProfile.getVersion(), profile.getVersion()); + Assertions.assertEquals(minimalProfile.hashCode(), profile.hashCode()); + } + } diff --git a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileTest.java b/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileTest.java index f9f844ce0..958da1fe3 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileTest.java +++ b/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileTest.java @@ -16,6 +16,7 @@ public void testToString() throws Exception{ + "externalDescription=BagIt profile for ingesting content into the C.O. TDR loading dock., " + "contactName=William Wueppelmann, " + "contactEmail=tdr@canadiana.com, " + + "contactPhone=+1 613 907 7040, " + "version=1.2, " + "bagInfoRequirements={" + "Payload-Oxum=[required=true, acceptableValues=[], repeatable=false], " @@ -71,6 +72,10 @@ public void testEquals(){ differentContactEmail.setContactEmail("foo"); Assertions.assertFalse(profile.equals(differentContactEmail)); + BagitProfile differentContactPhone = createExpectedProfile(); + differentContactPhone.setContactPhone("foo"); + Assertions.assertFalse(profile.equals(differentContactPhone)); + BagitProfile differentVersion = createExpectedProfile(); differentVersion.setVersion("foo"); Assertions.assertFalse(profile.equals(differentVersion)); diff --git a/src/test/resources/bagitProfiles/exampleProfile.json b/src/test/resources/bagitProfiles/exampleProfile.json index a86d1a74e..6a7db5135 100644 --- a/src/test/resources/bagitProfiles/exampleProfile.json +++ b/src/test/resources/bagitProfiles/exampleProfile.json @@ -1,85 +1,99 @@ { - "BagIt-Profile-Info":{ - "BagIt-Profile-Identifier":"http://canadiana.org/standards/bagit/tdr_ingest.json", - "Source-Organization":"Candiana.org", - "Contact-Name":"William Wueppelmann", - "Contact-Email":"tdr@canadiana.com", - "External-Description":"BagIt profile for ingesting content into the C.O. TDR loading dock.", - "Version":"1.2" - }, - "Bag-Info":{ - "Source-Organization":{ - "required":true, - "values":[ - "Simon Fraser University", - "York University" - ] - }, - "Organization-Address":{ - "required":true, - "values":[ - "8888 University Drive Burnaby, B.C. V5A 1S6 Canada", - "4700 Keele Street Toronto, Ontario M3J 1P3 Canada" - ] - }, - "Contact-Name":{ - "required":true, - "values":[ - "Mark Jordan", - "Nick Ruest" - ] - }, - "Contact-Phone":{ - "required":false - }, - "Contact-Email":{ - "required":true - }, - "External-Description":{ - "required":true - }, - "External-Identifier":{ - "required":false - }, - "Bag-Size":{ - "required":true - }, - "Bag-Group-Identifier":{ - "required":false - }, - "Bag-Count":{ - "required":true - }, - "Internal-Sender-Identifier":{ - "required":false - }, - "Internal-Sender-Description":{ - "required":false - }, - "Bagging-Date":{ - "required":true, - "repeatable": false - }, - "Payload-Oxum":{ - "required":true - } - }, - "Manifests-Required":[ - "md5" - ], - "Allow-Fetch.txt":false, - "Serialization":"forbidden", - "Accept-Serialization":[ - "application/zip" - ], - "Tag-Manifests-Required":[ - "md5" - ], - "Tag-Files-Required":[ - "DPN/dpnFirstNode.txt", - "DPN/dpnRegistry" - ], - "Accept-BagIt-Version":[ - "0.96" - ] + "BagIt-Profile-Info": { + "BagIt-Profile-Identifier": "http://canadiana.org/standards/bagit/tdr_ingest.json", + "Source-Organization": "Candiana.org", + "Contact-Name": "William Wueppelmann", + "Contact-Email": "tdr@canadiana.com", + "Contact-Phone": "+1 613 907 7040", + "External-Description": "BagIt profile for ingesting content into the C.O. TDR loading dock.", + "Version": "1.2" + }, + "Bag-Info": { + "Source-Organization": { + "required": true, + "values": [ + "Simon Fraser University", + "York University" + ], + "repeatable": false + }, + "Organization-Address": { + "required": true, + "values": [ + "8888 University Drive Burnaby, B.C. V5A 1S6 Canada", + "4700 Keele Street Toronto, Ontario M3J 1P3 Canada" + ], + "repeatable": false + }, + "Contact-Name": { + "required": true, + "values": [ + "Mark Jordan", + "Nick Ruest" + ], + "repeatable": false + }, + "Contact-Phone": { + "required": false, + "repeatable": false + }, + "Contact-Email": { + "required": true, + "repeatable": false + }, + "External-Description": { + "required": true, + "repeatable": false + }, + "External-Identifier": { + "required": false, + "repeatable": false + }, + "Bag-Size": { + "required": true, + "repeatable": false + }, + "Bag-Group-Identifier": { + "required": false, + "repeatable": false + }, + "Bag-Count": { + "required": true, + "repeatable": false + }, + "Internal-Sender-Identifier": { + "required": false, + "repeatable": false + }, + "Internal-Sender-Description": { + "required": false, + "repeatable": false + }, + "Bagging-Date": { + "required": true, + "repeatable": false + }, + "Payload-Oxum": { + "required": true, + "repeatable": false + } + }, + "Manifests-Required": [ + "md5" + ], + "Allow-Fetch.txt": false, + "Serialization": "forbidden", + "Accept-Serialization": [ + "application/zip" + ], + "Tag-Manifests-Required": [ + "md5" + ], + "Tag-Files-Required": [ + "DPN/dpnFirstNode.txt", + "DPN/dpnRegistry" + ], + "Accept-BagIt-Version": [ + "0.96" + ] } \ No newline at end of file diff --git a/src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json b/src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json new file mode 100644 index 000000000..d7df949ed --- /dev/null +++ b/src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json @@ -0,0 +1,91 @@ +{ + "BagIt-Profile-Info": { + "BagIt-Profile-Identifier": "http://canadiana.org/standards/bagit/tdr_ingest.json", + "Source-Organization": "Candiana.org", + "External-Description": "BagIt profile for ingesting content into the C.O. TDR loading dock.", + "Version": "1.2" + }, + "Bag-Info": { + "Source-Organization": { + "required": true, + "values": [ + "Simon Fraser University", + "York University" + ], + "repeatable": false + }, + "Organization-Address": { + "required": true, + "values": [ + "8888 University Drive Burnaby, B.C. V5A 1S6 Canada", + "4700 Keele Street Toronto, Ontario M3J 1P3 Canada" + ], + "repeatable": false + }, + "Contact-Name": { + "required": true, + "values": [ + "Mark Jordan", + "Nick Ruest" + ], + "repeatable": false + }, + "Contact-Phone": { + "repeatable": false + }, + "Contact-Email": { + "required": true, + "repeatable": false + }, + "External-Description": { + "required": true, + "repeatable": false + }, + "External-Identifier": { + "repeatable": false + }, + "Bag-Size": { + "required": true, + "repeatable": false + }, + "Bag-Group-Identifier": { + "repeatable": false + }, + "Bag-Count": { + "required": true, + "repeatable": false + }, + "Internal-Sender-Identifier": { + "repeatable": false + }, + "Internal-Sender-Description": { + "repeatable": false + }, + "Bagging-Date": { + "required": true, + "repeatable": false + }, + "Payload-Oxum": { + "required": true, + "repeatable": false + } + }, + "Manifests-Required": [ + "md5" + ], + "Allow-Fetch.txt": false, + "Serialization": "forbidden", + "Accept-Serialization": [ + "application/zip" + ], + "Tag-Manifests-Required": [ + "md5" + ], + "Tag-Files-Required": [ + "DPN/dpnFirstNode.txt", + "DPN/dpnRegistry" + ], + "Accept-BagIt-Version": [ + "0.96" + ] +} \ No newline at end of file From af27481317748f843fa7b3156021aa68eac32945 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Mon, 12 Nov 2018 10:52:01 +0100 Subject: [PATCH 011/104] Fix due to PMD. --- .../bagit/conformance/profile/BagInfoRequirement.java | 4 ++-- .../repository/bagit/conformance/profile/BagitProfile.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java index e9656f9a8..559d19cbf 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java +++ b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java @@ -8,7 +8,7 @@ * This class is used to define elements in a bag-info.txt file used by a bagit-profile. */ public class BagInfoRequirement { - private boolean required = false; + private boolean required; private List acceptableValues = new ArrayList<>(); private boolean repeatable = true; @@ -37,7 +37,7 @@ public BagInfoRequirement(final boolean required, final List acceptableV this.acceptableValues = acceptableValues; } - public BagInfoRequirement(final boolean required, final List acceptableValues, boolean repeatable){ + public BagInfoRequirement(final boolean required, final List acceptableValues, final boolean repeatable){ this.required = required; this.acceptableValues = acceptableValues; this.repeatable = repeatable; diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfile.java b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfile.java index 2470a5cbb..d7eb29933 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfile.java +++ b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfile.java @@ -147,7 +147,7 @@ public void setContactEmail(final String contactEmail) { public String getContactPhone() { return contactPhone; } - public void setContactPhone(String contactPhone) { + public void setContactPhone(final String contactPhone) { this.contactPhone = contactPhone; } public String getVersion() { From db411d0088307f9f93bb1a845fe54ed08b2bd678 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Mon, 12 Nov 2018 14:13:43 +0100 Subject: [PATCH 012/104] Bagit-Profile: Make field "Tag-Manifests-Required" optional --- .../conformance/profile/BagitProfileDeserializer.java | 7 ++++--- .../conformance/profile/AbstractBagitProfileTest.java | 1 - .../bagitProfiles/exampleProfileOnlyRequiredFields.json | 3 --- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java index 928d8e86c..c637e3d73 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java +++ b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java @@ -174,9 +174,10 @@ private static List parseAcceptableSerializationFormats(final JsonNode n private static List parseRequiredTagmanifestTypes(final JsonNode node) { final JsonNode tagManifestsRequiredNodes = node.get("Tag-Manifests-Required"); final List requiredTagmanifestTypes = new ArrayList<>(); - - for (final JsonNode tagManifestsRequiredNode : tagManifestsRequiredNodes) { - requiredTagmanifestTypes.add(tagManifestsRequiredNode.asText()); + if (tagManifestsRequiredNodes != null) { + for (final JsonNode tagManifestsRequiredNode : tagManifestsRequiredNodes) { + requiredTagmanifestTypes.add(tagManifestsRequiredNode.asText()); + } } logger.debug(messages.getString("required_tagmanifest_types"), requiredTagmanifestTypes); diff --git a/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java b/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java index 230751b30..d143ad4c9 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java +++ b/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java @@ -57,7 +57,6 @@ protected BagitProfile createMinimalProfile(){ expectedProfile.setSerialization(Serialization.forbidden); expectedProfile.setAcceptableMIMESerializationTypes(Arrays.asList("application/zip")); expectedProfile.setAcceptableBagitVersions(Arrays.asList("0.96")); - expectedProfile.setTagManifestTypesRequired(Arrays.asList("md5")); expectedProfile.setTagFilesRequired(Arrays.asList("DPN/dpnFirstNode.txt", "DPN/dpnRegistry")); return expectedProfile; diff --git a/src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json b/src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json index d7df949ed..0de856c38 100644 --- a/src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json +++ b/src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json @@ -78,9 +78,6 @@ "Accept-Serialization": [ "application/zip" ], - "Tag-Manifests-Required": [ - "md5" - ], "Tag-Files-Required": [ "DPN/dpnFirstNode.txt", "DPN/dpnRegistry" From e8e0a77b2ba9a859cb53c31d439eea06dbbb675b Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Mon, 12 Nov 2018 14:23:28 +0100 Subject: [PATCH 013/104] Bagit-Profile: Make field "Tag-Files-Required" optional --- .../bagit/conformance/profile/BagitProfileDeserializer.java | 6 ++++-- .../bagit/conformance/profile/AbstractBagitProfileTest.java | 1 - .../bagitProfiles/exampleProfileOnlyRequiredFields.json | 4 ---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java index c637e3d73..0da8c7f31 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java +++ b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java @@ -188,8 +188,10 @@ private static List parseRequiredTagFiles(final JsonNode node) { final JsonNode tagFilesRequiredNodes = node.get("Tag-Files-Required"); final List requiredTagFiles = new ArrayList<>(); - for (final JsonNode tagFilesRequiredNode : tagFilesRequiredNodes) { - requiredTagFiles.add(tagFilesRequiredNode.asText()); + if (tagFilesRequiredNodes != null) { + for (final JsonNode tagFilesRequiredNode : tagFilesRequiredNodes) { + requiredTagFiles.add(tagFilesRequiredNode.asText()); + } } logger.debug(messages.getString("tag_files_required"), requiredTagFiles); diff --git a/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java b/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java index d143ad4c9..59d91c460 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java +++ b/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java @@ -57,7 +57,6 @@ protected BagitProfile createMinimalProfile(){ expectedProfile.setSerialization(Serialization.forbidden); expectedProfile.setAcceptableMIMESerializationTypes(Arrays.asList("application/zip")); expectedProfile.setAcceptableBagitVersions(Arrays.asList("0.96")); - expectedProfile.setTagFilesRequired(Arrays.asList("DPN/dpnFirstNode.txt", "DPN/dpnRegistry")); return expectedProfile; } diff --git a/src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json b/src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json index 0de856c38..dead840fb 100644 --- a/src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json +++ b/src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json @@ -78,10 +78,6 @@ "Accept-Serialization": [ "application/zip" ], - "Tag-Files-Required": [ - "DPN/dpnFirstNode.txt", - "DPN/dpnRegistry" - ], "Accept-BagIt-Version": [ "0.96" ] From 856ee4ea9df994fae329c3a82f35bd83bdf1a7e6 Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Mon, 12 Nov 2018 14:28:41 +0100 Subject: [PATCH 014/104] Remove output to error console. --- .../bagit/conformance/profile/BagitProfileDeserializerTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializerTest.java b/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializerTest.java index 8ac1e2f9e..30a6b3031 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializerTest.java +++ b/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializerTest.java @@ -36,8 +36,6 @@ public void testDeserializeWithoutOptionalTags() throws Exception{ BagitProfile minimalProfile = createMinimalProfile(); BagitProfile profile = mapper.readValue(new File("src/test/resources/bagitProfiles/exampleProfileOnlyRequiredFields.json"), BagitProfile.class); - System.err.println(minimalProfile.toString()); - System.err.println(profile.toString()); Assertions.assertEquals(minimalProfile, profile); Assertions.assertEquals(minimalProfile.getAcceptableBagitVersions(), profile.getAcceptableBagitVersions()); Assertions.assertEquals(minimalProfile.getAcceptableMIMESerializationTypes(), profile.getAcceptableMIMESerializationTypes()); From f2e4ab1e515106e128fa2227f57e133a5db9ae1f Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Tue, 13 Nov 2018 14:48:59 +0100 Subject: [PATCH 015/104] Changes according to the comments. --- .../conformance/profile/BagInfoRequirement.java | 12 +++++++++++- .../profile/BagitProfileDeserializer.java | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java index 559d19cbf..25da68ca6 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java +++ b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java @@ -31,12 +31,22 @@ public int hashCode() { public BagInfoRequirement(){ //intentionally left empty } - + /** + * Constructs a new BagInfoRequirement setting {@link #repeatable} to true (default). + * @param required Indicates whether or not the tag is required. + * @param acceptableValues List of acceptable values. + */ public BagInfoRequirement(final boolean required, final List acceptableValues){ this.required = required; this.acceptableValues = acceptableValues; } + /** + * Constructs a new BagInfoRequirement. + * @param required Indicates whether or not the tag is required. + * @param acceptableValues List of acceptable values. + * @param repeatable Indicates whether or not the tag is repeatable. + */ public BagInfoRequirement(final boolean required, final List acceptableValues, final boolean repeatable){ this.required = required; this.acceptableValues = acceptableValues; diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java index 0da8c7f31..6934be65b 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java +++ b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java @@ -68,7 +68,7 @@ private static void parseBagitProfileInfo(final JsonNode node, final BagitProfil final JsonNode bagitProfileInfoNode = node.get("BagIt-Profile-Info"); logger.debug(messages.getString("parsing_bagit_profile_info_section")); - // Read required tags first + // Read required tags // due to specification defined at https://github.com/bagit-profiles/bagit-profiles final String profileIdentifier = bagitProfileInfoNode.get("BagIt-Profile-Identifier").asText(); logger.debug(messages.getString("identifier"), profileIdentifier); @@ -86,6 +86,7 @@ private static void parseBagitProfileInfo(final JsonNode node, final BagitProfil logger.debug(messages.getString("version"), version); profile.setVersion(version); + // Read optional tags final JsonNode contactNameNode = bagitProfileInfoNode.get("Contact-Name"); if (contactNameNode != null) { final String contactName = contactNameNode.asText(); From 3e7242cfa1e4dba2bf369bf20c9765faf3dfc1c7 Mon Sep 17 00:00:00 2001 From: Tiago Rossi Date: Tue, 13 Nov 2018 14:26:46 -0200 Subject: [PATCH 016/104] Optimize control files creation and fixes files being truncated on some SMB file servers. --- .../bagit/writer/BagitFileWriter.java | 23 +++++++++++-------- .../repository/bagit/writer/FetchWriter.java | 14 +++++++---- .../bagit/writer/ManifestWriter.java | 19 ++++++++------- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/main/java/gov/loc/repository/bagit/writer/BagitFileWriter.java b/src/main/java/gov/loc/repository/bagit/writer/BagitFileWriter.java index 41aedb7b9..7faef55e2 100644 --- a/src/main/java/gov/loc/repository/bagit/writer/BagitFileWriter.java +++ b/src/main/java/gov/loc/repository/bagit/writer/BagitFileWriter.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; @@ -12,6 +11,7 @@ import org.slf4j.LoggerFactory; import gov.loc.repository.bagit.domain.Version; +import java.io.BufferedWriter; /** * Responsible for writing the bagit.txt to the filesystem @@ -36,14 +36,17 @@ private BagitFileWriter(){ public static void writeBagitFile(final Version version, final Charset encoding, final Path outputDir) throws IOException{ final Path bagitPath = outputDir.resolve("bagit.txt"); logger.debug(messages.getString("write_bagit_file_to_path"), outputDir); - - final String firstLine = "BagIt-Version: " + version + System.lineSeparator(); - logger.debug(messages.getString("writing_line_to_file"), firstLine, bagitPath); - Files.write(bagitPath, firstLine.getBytes(StandardCharsets.UTF_8), - StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); - - final String secondLine = "Tag-File-Character-Encoding: " + encoding + System.lineSeparator(); - logger.debug(messages.getString("writing_line_to_file"), secondLine, bagitPath); - Files.write(bagitPath, secondLine.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE, StandardOpenOption.APPEND); + + try (BufferedWriter writer = Files.newBufferedWriter(bagitPath, + StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) { + + final String firstLine = "BagIt-Version: " + version + System.lineSeparator(); + logger.debug(messages.getString("writing_line_to_file"), firstLine, bagitPath); + writer.append(firstLine); + + final String secondLine = "Tag-File-Character-Encoding: " + encoding + System.lineSeparator(); + logger.debug(messages.getString("writing_line_to_file"), secondLine, bagitPath); + writer.append(secondLine); + } } } diff --git a/src/main/java/gov/loc/repository/bagit/writer/FetchWriter.java b/src/main/java/gov/loc/repository/bagit/writer/FetchWriter.java index 30c4c780c..ab121d1af 100644 --- a/src/main/java/gov/loc/repository/bagit/writer/FetchWriter.java +++ b/src/main/java/gov/loc/repository/bagit/writer/FetchWriter.java @@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory; import gov.loc.repository.bagit.domain.FetchItem; +import java.io.BufferedWriter; /** * Responsible for writing out the list of {@link FetchItem} to the fetch.txt file on the filesystem @@ -37,11 +38,14 @@ private FetchWriter(){ public static void writeFetchFile(final List itemsToFetch, final Path outputDir, final Path bagitRootDir, final Charset charsetName) throws IOException{ logger.debug(messages.getString("writing_fetch_file_to_path"), outputDir); final Path fetchFilePath = outputDir.resolve("fetch.txt"); - - for(final FetchItem item : itemsToFetch){ - final String line = formatFetchLine(item, bagitRootDir); - logger.debug(messages.getString("writing_line_to_file"), line, fetchFilePath); - Files.write(fetchFilePath, line.getBytes(charsetName), StandardOpenOption.APPEND, StandardOpenOption.CREATE); + + try (BufferedWriter writer = Files.newBufferedWriter(fetchFilePath, charsetName, + StandardOpenOption.APPEND, StandardOpenOption.CREATE)) { + for (final FetchItem item : itemsToFetch) { + final String line = formatFetchLine(item, bagitRootDir); + logger.debug(messages.getString("writing_line_to_file"), line, fetchFilePath); + writer.append(line); + } } } diff --git a/src/main/java/gov/loc/repository/bagit/writer/ManifestWriter.java b/src/main/java/gov/loc/repository/bagit/writer/ManifestWriter.java index 774b44bbc..445e7515a 100644 --- a/src/main/java/gov/loc/repository/bagit/writer/ManifestWriter.java +++ b/src/main/java/gov/loc/repository/bagit/writer/ManifestWriter.java @@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory; import gov.loc.repository.bagit.domain.Manifest; +import java.io.BufferedWriter; /** * Responsible for writing out a {@link Manifest} to the filesystem @@ -65,14 +66,16 @@ private static void writeManifests(final Set manifests, final Path out Files.deleteIfExists(manifestPath); Files.createFile(manifestPath); - - for(final Entry entry : manifest.getFileToChecksumMap().entrySet()){ - //there are 2 spaces between the checksum and the path so that the manifests are compatible with the md5sum tools available on most unix systems. - //This may cause problems on windows due to it being text mode, in which case either replace with a * or try verifying in binary mode with --binary - final String line = entry.getValue() + " " + RelativePathWriter.formatRelativePathString(relativeTo, entry.getKey()); - logger.debug(messages.getString("writing_line_to_file"), line, manifestPath); - Files.write(manifestPath, line.getBytes(charsetName), - StandardOpenOption.APPEND, StandardOpenOption.CREATE); + + try (BufferedWriter writer = Files.newBufferedWriter(manifestPath, charsetName, + StandardOpenOption.APPEND, StandardOpenOption.CREATE)) { + for (final Entry entry : manifest.getFileToChecksumMap().entrySet()) { + //there are 2 spaces between the checksum and the path so that the manifests are compatible with the md5sum tools available on most unix systems. + //This may cause problems on windows due to it being text mode, in which case either replace with a * or try verifying in binary mode with --binary + final String line = entry.getValue() + " " + RelativePathWriter.formatRelativePathString(relativeTo, entry.getKey()); + logger.debug(messages.getString("writing_line_to_file"), line, manifestPath); + writer.append(line); + } } } } From e6ce9a79ec749004e0da6dc843fbfdbcc0a8c4db Mon Sep 17 00:00:00 2001 From: Volker Hartmann Date: Thu, 15 Nov 2018 08:34:04 +0100 Subject: [PATCH 017/104] Split the parsing of the BagIt-Profile-Info section into mandatory and optional tags. --- .../profile/BagitProfileDeserializer.java | 31 ++++++++++++++++--- src/main/resources/MessageBundle.properties | 2 ++ .../resources/MessageBundle_ar.properties | 2 ++ .../resources/MessageBundle_de_DE.properties | 2 ++ .../resources/MessageBundle_es_ES.properties | 2 ++ .../resources/MessageBundle_zh.properties | 2 ++ 6 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java index 6934be65b..3d48bec68 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java +++ b/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java @@ -65,11 +65,24 @@ public BagitProfile deserialize(final JsonParser p, final DeserializationContext } private static void parseBagitProfileInfo(final JsonNode node, final BagitProfile profile) { - final JsonNode bagitProfileInfoNode = node.get("BagIt-Profile-Info"); logger.debug(messages.getString("parsing_bagit_profile_info_section")); + + final JsonNode bagitProfileInfoNode = node.get("BagIt-Profile-Info"); + parseMandatoryTagsOfBagitProfileInfo(bagitProfileInfoNode, profile); + parseOptionalTagsOfBagitProfileInfo(bagitProfileInfoNode, profile); + } - // Read required tags - // due to specification defined at https://github.com/bagit-profiles/bagit-profiles + /** + * Parse required tags due to specification defined at + * {@link https://github.com/bagit-profiles/bagit-profiles} + * Note: If one of the tags is missing, a NullPointerException is thrown. + * + * @param bagitProfileInfoNode Root node of the bagit profile info section. + * @param profile Representation of bagit profile. + */ + private static void parseMandatoryTagsOfBagitProfileInfo(final JsonNode bagitProfileInfoNode, final BagitProfile profile) { + logger.debug(messages.getString("parsing_mandatory_tags_of_bagit_profile_info_section")); + final String profileIdentifier = bagitProfileInfoNode.get("BagIt-Profile-Identifier").asText(); logger.debug(messages.getString("identifier"), profileIdentifier); profile.setBagitProfileIdentifier(profileIdentifier); @@ -85,8 +98,18 @@ private static void parseBagitProfileInfo(final JsonNode node, final BagitProfil final String version = bagitProfileInfoNode.get("Version").asText(); logger.debug(messages.getString("version"), version); profile.setVersion(version); + } + + /** + * Parse optional tags due to specification defined at + * {@link https://github.com/bagit-profiles/bagit-profiles} + * + * @param bagitProfileInfoNode Root node of the bagit profile info section. + * @param profile Representation of bagit profile . + */ + private static void parseOptionalTagsOfBagitProfileInfo(final JsonNode bagitProfileInfoNode, final BagitProfile profile) { + logger.debug(messages.getString("parsing_optional_tags_of_bagit_profile_info_section")); - // Read optional tags final JsonNode contactNameNode = bagitProfileInfoNode.get("Contact-Name"); if (contactNameNode != null) { final String contactName = contactNameNode.asText(); diff --git a/src/main/resources/MessageBundle.properties b/src/main/resources/MessageBundle.properties index a77720d16..e8e1a3701 100644 --- a/src/main/resources/MessageBundle.properties +++ b/src/main/resources/MessageBundle.properties @@ -4,6 +4,8 @@ fetch_allowed=Are fetch files allowed? [{}] serialization_allowed=Serialization is: [{}] parsing_bagit_profile_info_section=Parsing the BagIt-Profile-Info section +parsing_mandatory_tags_of_bagit_profile_info_section=Parsing mandatory tags of the BagIt-Profile-Info section +parsing_optional_tags_of_bagit_profile_info_section=Parsing optional tags of the BagIt-Profile-Info section identifier=Identifier is [{}] source_organization=Source-Organization is [{}] contact_name=Contact-Name is [{}] diff --git a/src/main/resources/MessageBundle_ar.properties b/src/main/resources/MessageBundle_ar.properties index 4bb857652..81353903e 100644 --- a/src/main/resources/MessageBundle_ar.properties +++ b/src/main/resources/MessageBundle_ar.properties @@ -5,6 +5,8 @@ fetch_allowed=Are fetch files allowed? [{}] serialization_allowed=Serialization is\: [{}] parsing_bagit_profile_info_section=Parsing the BagIt-Profile-Info section +parsing_mandatory_tags_of_bagit_profile_info_section=Parsing mandatory tags of the BagIt-Profile-Info section +parsing_optional_tags_of_bagit_profile_info_section=Parsing optional tags of the BagIt-Profile-Info section identifier=Identifier is [{}] source_organization=Source-Organization is [{}] contact_name=Contact-Name is [{}] diff --git a/src/main/resources/MessageBundle_de_DE.properties b/src/main/resources/MessageBundle_de_DE.properties index a0a29095b..7386be7f6 100644 --- a/src/main/resources/MessageBundle_de_DE.properties +++ b/src/main/resources/MessageBundle_de_DE.properties @@ -4,6 +4,8 @@ fetch_allowed=Sind Fetch Dateien erlaubt? [{}] serialization_allowed=Serialisierung ist: [{}] parsing_bagit_profile_info_section=Lese Abschnitt BagIt-Profile-Info +parsing_mandatory_tags_of_bagit_profile_info_section=Parse die ben\u00f6tigten Tags im Abschnitt BagIt-Profile-Info +parsing_optional_tags_of_bagit_profile_info_section=Parse die optionalen Tags im Abschnitt BagIt-Profile-Info identifier=Identifier hat den Wert [{}] source_organization=Source-Organization hat den Wert [{}] contact_name=Contact-Name hat den Wert [{}] diff --git a/src/main/resources/MessageBundle_es_ES.properties b/src/main/resources/MessageBundle_es_ES.properties index 16f23fd86..38af9cee7 100644 --- a/src/main/resources/MessageBundle_es_ES.properties +++ b/src/main/resources/MessageBundle_es_ES.properties @@ -5,6 +5,8 @@ fetch_allowed=\u00bfSe permiten archivos de recuperaci\u00f3n? [{}] serialization_allowed=Serializaci\u00f3n es\: [{}] parsing_bagit_profile_info_section=An\u00e1lisis de la secci\u00f3n de informaci\u00f3n de perfil BagIt +parsing_mandatory_tags_of_bagit_profile_info_section=An\u00e1lisis de las etiquetas obligatorias de la secci\u00f3n de informaci\u00f3n de perfil BagIt +parsing_optional_tags_of_bagit_profile_info_section=An\u00e1lisis de las etiquetas opcionales de la secci\u00f3n de informaci\u00f3n de perfil BagIt identifier=Identificador es [{}] source_organization=Organizaci\u00f3n de la fuente es [{}] contact_name=Nombre del contacto es [{}] diff --git a/src/main/resources/MessageBundle_zh.properties b/src/main/resources/MessageBundle_zh.properties index 4bb857652..81353903e 100644 --- a/src/main/resources/MessageBundle_zh.properties +++ b/src/main/resources/MessageBundle_zh.properties @@ -5,6 +5,8 @@ fetch_allowed=Are fetch files allowed? [{}] serialization_allowed=Serialization is\: [{}] parsing_bagit_profile_info_section=Parsing the BagIt-Profile-Info section +parsing_mandatory_tags_of_bagit_profile_info_section=Parsing mandatory tags of the BagIt-Profile-Info section +parsing_optional_tags_of_bagit_profile_info_section=Parsing optional tags of the BagIt-Profile-Info section identifier=Identifier is [{}] source_organization=Source-Organization is [{}] contact_name=Contact-Name is [{}] From ec2640f7f66360c313496a351b42f31e8ac33253 Mon Sep 17 00:00:00 2001 From: Richard van Heest Date: Wed, 20 Feb 2019 15:19:27 +0100 Subject: [PATCH 018/104] ignore IntelliJ files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 16f5749a0..04dee72b7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ bagit-conformance-suite/ .classpath .gradle/ .DS_Store +.idea/ +out/ From bb73e1226dac51053a941024658b17f1286dade8 Mon Sep 17 00:00:00 2001 From: Richard van Heest Date: Wed, 20 Feb 2019 15:20:19 +0100 Subject: [PATCH 019/104] fix failing test --- .../gov/loc/repository/bagit/conformance/BagLinterTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/gov/loc/repository/bagit/conformance/BagLinterTest.java b/src/test/java/gov/loc/repository/bagit/conformance/BagLinterTest.java index 3a9655fbb..e7eced0bc 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/BagLinterTest.java +++ b/src/test/java/gov/loc/repository/bagit/conformance/BagLinterTest.java @@ -35,8 +35,9 @@ public void testLintBag() throws Exception{ expectedWarnings.remove(BagitWarning.MANIFEST_SETS_DIFFER); //only applies to version 1.0 but need older version for other warnings, so we test this separately Set warnings = BagLinter.lintBag(rootDir); - if(FileSystems.getDefault().getClass().getName() == "sun.nio.fs.MacOSXFileSystem"){ - expectedWarnings.remove(BagitWarning.DIFFERENT_NORMALIZATION); //don't test normalization on mac + if(FileSystems.getDefault().getClass().getName() == "sun.nio.fs.MacOSXFileSystem"){ //don't test normalization on mac + expectedWarnings.remove(BagitWarning.DIFFERENT_NORMALIZATION); + warnings.remove(BagitWarning.DIFFERENT_NORMALIZATION); } Set diff = new HashSet<>(expectedWarnings); From 9f085974f11dbaf5c2813ba1fa11521d69bef4ab Mon Sep 17 00:00:00 2001 From: Richard van Heest Date: Tue, 26 Feb 2019 08:28:19 +0100 Subject: [PATCH 020/104] change groupId from 'gov.loc' to 'nl.knaw.dans' --- bintray.gradle | 2 +- maven-central.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bintray.gradle b/bintray.gradle index d683de61a..cc8f22d0c 100644 --- a/bintray.gradle +++ b/bintray.gradle @@ -91,7 +91,7 @@ publishing { artifact sourcesJar //needed for syncing with maven central artifact javadocJar //needed for syncing with maven central - groupId 'gov.loc' + groupId 'nl.knaw.dans' artifactId 'bagit' version project.version diff --git a/maven-central.gradle b/maven-central.gradle index b2f4601aa..4cf635a6c 100644 --- a/maven-central.gradle +++ b/maven-central.gradle @@ -2,7 +2,7 @@ apply plugin: 'maven' apply plugin: 'signing' -group = 'gov.loc' +group = 'nl.knaw.dans' //javadocs and sources required for uploading to maven central /*task javadocJar(type: Jar) { From 54bc7b961247cd274f8394f220d87ac9362dc3fd Mon Sep 17 00:00:00 2001 From: Richard van Heest Date: Tue, 26 Feb 2019 10:10:14 +0100 Subject: [PATCH 021/104] change repository link in gradle scripts --- bintray.gradle | 18 +++++++++--------- maven-central.gradle | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bintray.gradle b/bintray.gradle index cc8f22d0c..a948fc94e 100644 --- a/bintray.gradle +++ b/bintray.gradle @@ -40,13 +40,13 @@ bintray { name = "bagit-java" userOrg = user desc = "The BAGIT LIBRARY is a software library intended to support the creation, manipulation, and validation of bags. It is version aware with the earliest supported version being 0.93." - websiteUrl = "https://github.com/LibraryOfCongress/bagit-java" - issueTrackerUrl = "https://github.com/LibraryOfCongress/bagit-java/issues" + websiteUrl = "https://github.com/DANS-KNAW/bagit-java" + issueTrackerUrl = "https://github.com/DANS-KNAW/bagit-java/issues" licenses = ["Public Domain"] - vcsUrl = "https://github.com/LibraryOfCongress/bagit-java" + vcsUrl = "https://github.com/DANS-KNAW/bagit-java" labels = ["bagit", "library of congress"] publicDownloadNumbers = true - githubRepo = 'LibraryOfCongress/bagit-java' + githubRepo = 'DANS-KNAW/bagit-java' githubReleaseNotesFile = 'README.md' version{ @@ -65,7 +65,7 @@ def pomConfig = { licenses { license { name 'Public Domain' - url 'https://github.com/LibraryOfCongress/bagit-java/blob/master/LICENSE.txt' + url 'https://github.com/DANS-KNAW/bagit-java/blob/master/LICENSE.txt' } } @@ -78,9 +78,9 @@ def pomConfig = { } scm { - connection 'scm:git:https://github.com/LibraryOfCongress/bagit-java' - developerConnection 'scm:git:ssh://github.com/LibraryOfCongress/bagit-java' - url 'https://github.com/LibraryOfCongress/bagit-java' + connection 'scm:git:https://github.com/DANS-KNAW/bagit-java' + developerConnection 'scm:git:ssh://github.com/DANS-KNAW/bagit-java' + url 'https://github.com/DANS-KNAW/bagit-java' } } @@ -99,7 +99,7 @@ publishing { def root = asNode() root.appendNode('description', 'The BAGIT LIBRARY is a software library intended to support the creation, manipulation, and validation of bags. Its current version is 0.97. It is version aware with the earliest supported version being 0.93.') root.appendNode('name', 'bagit-java') - root.appendNode('url', 'https://github.com/LibraryOfCongress/bagit-java') + root.appendNode('url', 'https://github.com/DANS-KNAW/bagit-java') root.children().last() + pomConfig } } diff --git a/maven-central.gradle b/maven-central.gradle index 4cf635a6c..485a6d7d1 100644 --- a/maven-central.gradle +++ b/maven-central.gradle @@ -56,18 +56,18 @@ uploadArchives { packaging 'jar' // optionally artifactId can be defined here description 'The BAGIT LIBRARY is a software library intended to support the creation, manipulation, and validation of bags. Its current version is 0.97. It is version aware with the earliest supported version being 0.93.' - url 'https://github.com/LibraryOfCongress/bagit-java' + url 'https://github.com/DANS-KNAW/bagit-java' scm { - connection 'scm:git:https://github.com/LibraryOfCongress/bagit-java' - developerConnection 'scm:git:ssh://github.com/LibraryOfCongress/bagit-java' - url 'https://github.com/LibraryOfCongress/bagit-java' + connection 'scm:git:https://github.com/DANS-KNAW/bagit-java' + developerConnection 'scm:git:ssh://github.com/DANS-KNAW/bagit-java' + url 'https://github.com/DANS-KNAW/bagit-java' } licenses { license { name 'No Copyright' - url 'https://github.com/LibraryOfCongress/bagit-java/blob/master/LICENSE.txt' + url 'https://github.com/DANS-KNAW/bagit-java/blob/master/LICENSE.txt' } } From 81914acbecfe77fc554f1fc14d61e715b2744b3f Mon Sep 17 00:00:00 2001 From: Richard van Heest Date: Tue, 26 Feb 2019 10:12:50 +0100 Subject: [PATCH 022/104] add /gradle.properties to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 04dee72b7..9e632d100 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ bagit-conformance-suite/ .DS_Store .idea/ out/ +/gradle.properties From 752dda0d1a3e549dee720b75c93563363b21b5ed Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Tue, 26 Feb 2019 14:28:14 +0100 Subject: [PATCH 023/104] Maven Central and bintray config --- bintray.gradle | 21 +-------------------- build.gradle | 18 +++++++++++++++++- maven-central.gradle | 20 +++++--------------- 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/bintray.gradle b/bintray.gradle index a948fc94e..4a9b16701 100644 --- a/bintray.gradle +++ b/bintray.gradle @@ -13,20 +13,6 @@ if(!project.hasProperty("ossrhUsername")){ //so CI doesn't break project.ext.ossrhPassword = "foo" } -task javadocJar(type: Jar) { - group "Build" - description "Create the jar that contains all the class documentation (javadoc)." - classifier = 'javadoc' - from javadoc -} - -task sourcesJar(type: Jar) { - group "Build" - description "Create the jar that contains all the .class files." - classifier = 'sources' - from sourceSets.main.allSource -} - bintray { user = project.ext.bintrayUser key = project.ext.bintrayApiKey @@ -38,7 +24,7 @@ bintray { pkg { repo = "maven" name = "bagit-java" - userOrg = user + userOrg = "dans-knaw" desc = "The BAGIT LIBRARY is a software library intended to support the creation, manipulation, and validation of bags. It is version aware with the earliest supported version being 0.93." websiteUrl = "https://github.com/DANS-KNAW/bagit-java" issueTrackerUrl = "https://github.com/DANS-KNAW/bagit-java/issues" @@ -52,11 +38,6 @@ bintray { version{ name = project.version vcsTag = 'v' + project.version - mavenCentralSync{ - user = ossrhUsername - password = ossrhPassword - close = '0' //release the version manually on Maven Central - } } } } diff --git a/build.gradle b/build.gradle index a6137dc4e..9e151857b 100644 --- a/build.gradle +++ b/build.gradle @@ -9,8 +9,24 @@ plugins { id "org.ajoberstar.grgit" version "2.2.1" id "com.github.spotbugs" version "1.6.2" id "com.jfrog.bintray" version "1.8.2" + id 'io.codearte.nexus-staging' version '0.11.0' } -apply from: 'eclipse.gradle' + +task javadocJar(type: Jar) { + group "Build" + description "Create the jar that contains all the class documentation (javadoc)." + classifier = 'javadoc' + from javadoc +} + +task sourcesJar(type: Jar) { + group "Build" + description "Create the jar that contains all the .class files." + classifier = 'sources' + from sourceSets.main.allSource +} + +//apply from: 'eclipse.gradle' apply from: 'bintray.gradle' apply from: 'maven-central.gradle' apply from: 'code-quality.gradle' diff --git a/maven-central.gradle b/maven-central.gradle index 485a6d7d1..ccddedd7b 100644 --- a/maven-central.gradle +++ b/maven-central.gradle @@ -4,21 +4,6 @@ apply plugin: 'signing' group = 'nl.knaw.dans' -//javadocs and sources required for uploading to maven central -/*task javadocJar(type: Jar) { - group "Build" - description "Create the jar that contains all the class documentation (javadoc)." - classifier = 'javadoc' - from javadoc -} - -task sourcesJar(type: Jar) { - group "Build" - description "Create the jar that contains all the .class files." - classifier = 'sources' - from sourceSets.main.allSource -}*/ - artifacts { archives javadocJar archives sourcesJar @@ -82,3 +67,8 @@ uploadArchives { } } } + +nexusStaging { + packageGroup = "nl.knaw.dans" + //stagingProfileId = "yourStagingProfileId" // when not defined will be got from server using "packageGroup" +} From 0ead84402a3c64ef667845ed22a5a4859eaf286a Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Tue, 26 Feb 2019 15:32:09 +0100 Subject: [PATCH 024/104] Restored eclipse support --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9e151857b..a8ad4c9d6 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ task sourcesJar(type: Jar) { from sourceSets.main.allSource } -//apply from: 'eclipse.gradle' +apply from: 'eclipse.gradle' apply from: 'bintray.gradle' apply from: 'maven-central.gradle' apply from: 'code-quality.gradle' From b49c5e629119aef81b75d5b57d4104e9e38a2e15 Mon Sep 17 00:00:00 2001 From: Richard van Heest Date: Thu, 7 May 2020 11:46:33 +0200 Subject: [PATCH 025/104] don't check for the existence of a 'normalized' file' if the file exists --- gradle/wrapper/gradle-wrapper.properties | 5 +++-- .../loc/repository/bagit/verify/CheckIfFileExistsTask.java | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2d80b69a7..ba47cd830 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Thu May 07 11:26:33 CEST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTask.java b/src/main/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTask.java index 4ace056e3..9007c0d80 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTask.java +++ b/src/main/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTask.java @@ -31,11 +31,10 @@ public CheckIfFileExistsTask(final Path file, final Set missingFiles, fina @Override public void run() { - final boolean existsNormalized = existsNormalized(); final boolean fileExists = Files.exists(file); if(!fileExists){ - if(existsNormalized){ + if(existsNormalized()){ logger.warn(messages.getString("different_normalization_on_filesystem_warning"), file); } else{ From 0a1402786feb9cced5e4abccb9e40cf4492b6557 Mon Sep 17 00:00:00 2001 From: Eric de Vries Date: Fri, 24 Mar 2023 15:10:57 +0100 Subject: [PATCH 026/104] Dd 1316 (#4) * cleanup * Checks * Add github stuff * Add site plugin * Add test * add test to build * Remove old stuff * Test * Publish * CHECK * [maven-release-plugin] prepare release 5.3.3 * Test build without coveralls * Test over * Test build * Remove automatic signing * Skip jarsigner * Badges * Cleanup * remove groupId * Test build and deploy * Add spotbugs * Add site generation again * Trigger build * Remove old stuff * Add license, rename package --- .circleci/config.yml | 30 -- .dockerignore | 3 - .github/PULL_REQUEST_TEMPLATE.md | 24 +- .github/workflows/build.yml | 77 ++++ .gitignore | 36 +- .gitmodules | 3 + .travis.yml | 42 --- Dockerfile | 9 - LICENSE.txt | 18 + NOTICE.txt | 16 + README.md | 144 ++++---- appveyor.yml | 25 -- bagit-conformance-suite | 1 + bintray.gradle | 88 ----- build.gradle | 90 ----- code-quality.gradle | 96 ----- crowdin.yml | 3 - eclipse.gradle | 38 -- gradle/wrapper/gradle-wrapper.jar | Bin 54329 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 6 - gradlew | 172 --------- gradlew.bat | 84 ----- maven-central.gradle | 74 ---- message-bundle.gradle | 56 --- pom.xml | 331 ++++++++++++++++++ settings.gradle | 1 - .../knaw/dans}/bagit/BagTestCaseVistor.java | 17 +- .../dans}/bagit/BagitSuiteComplanceTest.java | 59 ++-- .../knaw/dans}/bagit/FileExistsVistor.java | 17 +- .../ReaderWriterVerifierIntegrationTest.java | 25 +- .../conformance/profile/Serialization.java | 10 - .../exceptions/CorruptChecksumException.java | 16 - .../FileNotInManifestException.java | 15 - .../FileNotInPayloadDirectoryException.java | 13 - .../InvalidBagMetadataException.java | 17 - .../InvalidBagitFileFormatException.java | 16 - .../InvalidPayloadOxumException.java | 13 - .../exceptions/MaliciousPathException.java | 13 - .../exceptions/MissingBagitFileException.java | 12 - .../MissingPayloadDirectoryException.java | 16 - .../MissingPayloadManifestException.java | 12 - .../PayloadOxumDoesNotExistException.java | 15 - .../UnparsableVersionException.java | 14 - .../UnsupportedAlgorithmException.java | 16 - .../exceptions/VerificationException.java | 12 - .../BagitVersionIsNotAcceptableException.java | 18 - .../FetchFileNotAllowedException.java | 16 - ...etatdataValueIsNotAcceptableException.java | 16 - ...etatdataValueIsNotRepeatableException.java | 14 - .../RequiredManifestNotPresentException.java | 12 - ...uiredMetadataFieldNotPresentException.java | 14 - .../RequiredTagFileNotPresentException.java | 14 - ...orithmNameToSupportedAlgorithmMapping.java | 10 - .../hash/StandardSupportedAlgorithms.java | 29 -- .../bagit/hash/SupportedAlgorithm.java | 12 - .../dans}/bagit/annotation/Incubating.java | 17 +- .../dans}/bagit/conformance/BagLinter.java | 55 +-- .../bagit/conformance/BagProfileChecker.java | 49 ++- .../dans}/bagit/conformance/BagitWarning.java | 17 +- .../bagit/conformance/EncodingChecker.java | 17 +- .../bagit/conformance/ManifestChecker.java | 33 +- .../bagit/conformance/MetadataChecker.java | 24 +- .../bagit/conformance/VersionChecker.java | 19 +- .../profile/BagInfoRequirement.java | 17 +- .../conformance/profile/BagitProfile.java | 17 +- .../profile/BagitProfileDeserializer.java | 23 +- .../conformance/profile/Serialization.java | 25 ++ .../AbstractCreateManifestsVistor.java | 24 +- .../knaw/dans}/bagit/creator/BagCreator.java | 39 ++- .../creator/CreatePayloadManifestsVistor.java | 19 +- .../creator/CreateTagManifestsVistor.java | 19 +- .../knaw/dans}/bagit/domain/Bag.java | 17 +- .../knaw/dans}/bagit/domain/FetchItem.java | 17 +- .../knaw/dans}/bagit/domain/Manifest.java | 20 +- .../knaw/dans}/bagit/domain/Metadata.java | 21 +- .../knaw/dans}/bagit/domain/Version.java | 17 +- .../exceptions/CorruptChecksumException.java | 31 ++ .../FileNotInManifestException.java | 30 ++ .../FileNotInPayloadDirectoryException.java | 28 ++ .../InvalidBagMetadataException.java | 32 ++ .../InvalidBagitFileFormatException.java | 31 ++ .../InvalidPayloadOxumException.java | 28 ++ .../exceptions/MaliciousPathException.java | 28 ++ .../exceptions/MissingBagitFileException.java | 27 ++ .../MissingPayloadDirectoryException.java | 31 ++ .../MissingPayloadManifestException.java | 27 ++ .../PayloadOxumDoesNotExistException.java | 30 ++ .../UnparsableVersionException.java | 29 ++ .../UnsupportedAlgorithmException.java | 31 ++ .../exceptions/VerificationException.java | 27 ++ .../BagitVersionIsNotAcceptableException.java | 32 ++ .../FetchFileNotAllowedException.java | 31 ++ ...etatdataValueIsNotAcceptableException.java | 31 ++ ...etatdataValueIsNotRepeatableException.java | 29 ++ .../RequiredManifestNotPresentException.java | 27 ++ ...uiredMetadataFieldNotPresentException.java | 29 ++ .../RequiredTagFileNotPresentException.java | 29 ++ ...orithmNameToSupportedAlgorithmMapping.java | 25 ++ .../knaw/dans}/bagit/hash/Hasher.java | 20 +- ...orithmNameToSupportedAlgorithmMapping.java | 19 +- .../hash/StandardSupportedAlgorithms.java | 44 +++ .../dans/bagit/hash/SupportedAlgorithm.java | 27 ++ .../knaw/dans}/bagit/reader/BagReader.java | 35 +- .../bagit/reader/BagitTextFileReader.java | 25 +- .../knaw/dans}/bagit/reader/FetchReader.java | 23 +- .../dans}/bagit/reader/KeyValueReader.java | 19 +- .../dans}/bagit/reader/ManifestReader.java | 33 +- .../dans}/bagit/reader/MetadataReader.java | 19 +- .../dans}/bagit/reader/TagFileReader.java | 25 +- .../knaw/dans}/bagit/util/PathUtils.java | 23 +- ...actPayloadFileExistsInManifestsVistor.java | 19 +- .../knaw/dans}/bagit/verify/BagVerifier.java | 52 ++- .../bagit/verify/CheckIfFileExistsTask.java | 17 +- .../bagit/verify/CheckManifestHashesTask.java | 21 +- .../verify/FileCountAndTotalSizeVistor.java | 17 +- .../dans}/bagit/verify/MandatoryVerifier.java | 33 +- .../dans}/bagit/verify/ManifestVerifier.java | 39 ++- ...PayloadFileExistsInAllManifestsVistor.java | 24 +- ...dFileExistsInAtLeastOneManifestVistor.java | 22 +- .../dans}/bagit/verify/QuickVerifier.java | 27 +- .../knaw/dans}/bagit/writer/BagWriter.java | 26 +- .../dans}/bagit/writer/BagitFileWriter.java | 19 +- .../knaw/dans}/bagit/writer/FetchWriter.java | 19 +- .../dans}/bagit/writer/ManifestWriter.java | 19 +- .../dans}/bagit/writer/MetadataWriter.java | 22 +- .../dans}/bagit/writer/PayloadWriter.java | 26 +- .../bagit/writer/RelativePathWriter.java | 19 +- .../conformance/profile/BagitProfileTest.java | 115 ------ .../MySupportedNameToAlgorithmMapping.java | 18 - ...eExistsInAtLeastOneManifestVistorTest.java | 20 -- .../bagit/verify/SHA3256Algorithm.java | 17 - .../dans}/bagit/PrivateConstructorTest.java | 17 +- .../knaw/dans}/bagit/TempFolderTest.java | 17 +- .../knaw/dans}/bagit/TestUtils.java | 17 +- .../bagit/conformance/BagLinterTest.java | 25 +- .../conformance/BagProfileCheckerTest.java | 37 +- .../conformance/EncodingCheckerTest.java | 17 +- .../conformance/ManifestCheckerTest.java | 27 +- .../conformance/MetadataCheckerTest.java | 20 +- .../bagit/conformance/VersionCheckerTest.java | 19 +- .../profile/AbstractBagitProfileTest.java | 17 +- .../profile/BagInfoRequirementTest.java | 17 +- .../profile/BagitProfileDeserializerTest.java | 17 +- .../conformance/profile/BagitProfileTest.java | 131 +++++++ .../AddPayloadToBagManifestVistorTest.java | 26 +- .../dans}/bagit/creator/BagCreatorTest.java | 33 +- .../knaw/dans}/bagit/domain/BagTest.java | 19 +- .../dans}/bagit/domain/FetchItemTest.java | 17 +- .../knaw/dans}/bagit/domain/ManifestTest.java | 19 +- .../knaw/dans}/bagit/domain/MetadataTest.java | 17 +- .../knaw/dans}/bagit/domain/VersionTest.java | 17 +- .../fetching/FetchHttpFileExample.java | 24 +- .../serialization/CreateTarBagExample.java | 20 +- .../serialization/CreateZipBagExample.java | 22 +- .../knaw/dans}/bagit/hash/HasherTest.java | 19 +- .../dans}/bagit/reader/BagReaderTest.java | 27 +- .../bagit/reader/BagitTextFileReaderTest.java | 29 +- .../dans}/bagit/reader/FetchReaderTest.java | 41 ++- .../bagit/reader/KeyValueReaderTest.java | 21 +- .../bagit/reader/ManifestReaderTest.java | 31 +- .../bagit/reader/MetadataReaderTest.java | 19 +- .../dans}/bagit/reader/TagFileReaderTest.java | 26 +- .../knaw/dans}/bagit/util/PathUtilsTest.java | 25 +- .../dans}/bagit/verify/BagVerifierTest.java | 39 ++- .../verify/CheckIfFileExistsTaskTest.java | 19 +- .../FileCountAndTotalSizeVistorTest.java | 19 +- .../bagit/verify/MandatoryVerifierTest.java | 37 +- .../bagit/verify/ManifestVerifierTest.java | 27 +- .../MySupportedNameToAlgorithmMapping.java | 33 ++ ...eExistsInAtLeastOneManifestVistorTest.java | 35 ++ .../dans}/bagit/verify/QuickVerifierTest.java | 27 +- .../dans/bagit/verify/SHA3256Algorithm.java | 32 ++ .../dans}/bagit/writer/BagWriterTest.java | 29 +- .../bagit/writer/BagitFileWriterTest.java | 21 +- .../dans}/bagit/writer/FetchWriterTest.java | 22 +- .../bagit/writer/ManifestWriterTest.java | 24 +- .../bagit/writer/MetadataWriterTest.java | 23 +- .../dans}/bagit/writer/PayloadWriterTest.java | 28 +- .../bagit/writer/RelativePathWriterTest.java | 20 +- src/test/resources/logback.xml | 16 + 180 files changed, 3398 insertions(+), 1885 deletions(-) delete mode 100644 .circleci/config.yml delete mode 100644 .dockerignore create mode 100644 .github/workflows/build.yml create mode 100644 .gitmodules delete mode 100644 .travis.yml delete mode 100644 Dockerfile delete mode 100644 appveyor.yml create mode 160000 bagit-conformance-suite delete mode 100644 bintray.gradle delete mode 100644 build.gradle delete mode 100644 code-quality.gradle delete mode 100644 crowdin.yml delete mode 100644 eclipse.gradle delete mode 100644 gradle/wrapper/gradle-wrapper.jar delete mode 100644 gradle/wrapper/gradle-wrapper.properties delete mode 100755 gradlew delete mode 100644 gradlew.bat delete mode 100644 maven-central.gradle delete mode 100644 message-bundle.gradle create mode 100644 pom.xml delete mode 100644 settings.gradle rename src/integration/java/{gov/loc/repository => nl/knaw/dans}/bagit/BagTestCaseVistor.java (76%) rename src/integration/java/{gov/loc/repository => nl/knaw/dans}/bagit/BagitSuiteComplanceTest.java (76%) rename src/integration/java/{gov/loc/repository => nl/knaw/dans}/bagit/FileExistsVistor.java (62%) rename src/integration/java/{gov/loc/repository => nl/knaw/dans}/bagit/ReaderWriterVerifierIntegrationTest.java (80%) delete mode 100644 src/main/java/gov/loc/repository/bagit/conformance/profile/Serialization.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/CorruptChecksumException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/FileNotInManifestException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/FileNotInPayloadDirectoryException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/InvalidBagMetadataException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/InvalidBagitFileFormatException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/InvalidPayloadOxumException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/MaliciousPathException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/MissingBagitFileException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadDirectoryException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadManifestException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/PayloadOxumDoesNotExistException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/UnparsableVersionException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/UnsupportedAlgorithmException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/VerificationException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/conformance/BagitVersionIsNotAcceptableException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/conformance/FetchFileNotAllowedException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/conformance/MetatdataValueIsNotAcceptableException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/conformance/MetatdataValueIsNotRepeatableException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredManifestNotPresentException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredMetadataFieldNotPresentException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredTagFileNotPresentException.java delete mode 100644 src/main/java/gov/loc/repository/bagit/hash/BagitAlgorithmNameToSupportedAlgorithmMapping.java delete mode 100644 src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java delete mode 100644 src/main/java/gov/loc/repository/bagit/hash/SupportedAlgorithm.java rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/annotation/Incubating.java (54%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/BagLinter.java (80%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/BagProfileChecker.java (83%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/BagitWarning.java (66%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/EncodingChecker.java (59%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/ManifestChecker.java (92%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/MetadataChecker.java (69%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/VersionChecker.java (57%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/profile/BagInfoRequirement.java (77%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/profile/BagitProfile.java (90%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/profile/BagitProfileDeserializer.java (90%) create mode 100644 src/main/java/nl/knaw/dans/bagit/conformance/profile/Serialization.java rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/creator/AbstractCreateManifestsVistor.java (72%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/creator/BagCreator.java (89%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/creator/CreatePayloadManifestsVistor.java (52%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/creator/CreateTagManifestsVistor.java (52%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/domain/Bag.java (86%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/domain/FetchItem.java (75%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/domain/Manifest.java (65%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/domain/Metadata.java (83%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/domain/Version.java (73%) create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/CorruptChecksumException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/FileNotInManifestException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/FileNotInPayloadDirectoryException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/InvalidBagMetadataException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/InvalidBagitFileFormatException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/InvalidPayloadOxumException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/MaliciousPathException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/MissingBagitFileException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/MissingPayloadDirectoryException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/MissingPayloadManifestException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/PayloadOxumDoesNotExistException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/UnparsableVersionException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/UnsupportedAlgorithmException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/VerificationException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/conformance/BagitVersionIsNotAcceptableException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/conformance/FetchFileNotAllowedException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/conformance/MetatdataValueIsNotAcceptableException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/conformance/MetatdataValueIsNotRepeatableException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredManifestNotPresentException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredMetadataFieldNotPresentException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredTagFileNotPresentException.java create mode 100644 src/main/java/nl/knaw/dans/bagit/hash/BagitAlgorithmNameToSupportedAlgorithmMapping.java rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/hash/Hasher.java (85%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/hash/StandardBagitAlgorithmNameToSupportedAlgorithmMapping.java (51%) create mode 100644 src/main/java/nl/knaw/dans/bagit/hash/StandardSupportedAlgorithms.java create mode 100644 src/main/java/nl/knaw/dans/bagit/hash/SupportedAlgorithm.java rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/BagReader.java (67%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/BagitTextFileReader.java (85%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/FetchReader.java (75%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/KeyValueReader.java (81%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/ManifestReader.java (82%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/MetadataReader.java (73%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/TagFileReader.java (71%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/util/PathUtils.java (86%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/AbstractPayloadFileExistsInManifestsVistor.java (64%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/BagVerifier.java (84%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/CheckIfFileExistsTask.java (78%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/CheckManifestHashesTask.java (75%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/FileCountAndTotalSizeVistor.java (69%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/MandatoryVerifier.java (79%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/ManifestVerifier.java (84%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/PayloadFileExistsInAllManifestsVistor.java (60%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistor.java (67%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/QuickVerifier.java (80%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/BagWriter.java (85%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/BagitFileWriter.java (71%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/FetchWriter.java (76%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/ManifestWriter.java (83%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/MetadataWriter.java (73%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/PayloadWriter.java (80%) rename src/main/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/RelativePathWriter.java (51%) delete mode 100644 src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileTest.java delete mode 100644 src/test/java/gov/loc/repository/bagit/verify/MySupportedNameToAlgorithmMapping.java delete mode 100644 src/test/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistorTest.java delete mode 100644 src/test/java/gov/loc/repository/bagit/verify/SHA3256Algorithm.java rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/PrivateConstructorTest.java (68%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/TempFolderTest.java (77%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/TestUtils.java (70%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/BagLinterTest.java (75%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/BagProfileCheckerTest.java (78%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/EncodingCheckerTest.java (55%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/ManifestCheckerTest.java (87%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/MetadataCheckerTest.java (64%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/VersionCheckerTest.java (52%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/profile/AbstractBagitProfileTest.java (85%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/profile/BagInfoRequirementTest.java (59%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/conformance/profile/BagitProfileDeserializerTest.java (84%) create mode 100644 src/test/java/nl/knaw/dans/bagit/conformance/profile/BagitProfileTest.java rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/creator/AddPayloadToBagManifestVistorTest.java (78%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/creator/BagCreatorTest.java (84%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/domain/BagTest.java (72%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/domain/FetchItemTest.java (77%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/domain/ManifestTest.java (72%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/domain/MetadataTest.java (81%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/domain/VersionTest.java (84%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/examples/fetching/FetchHttpFileExample.java (50%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/examples/serialization/CreateTarBagExample.java (73%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/examples/serialization/CreateZipBagExample.java (72%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/hash/HasherTest.java (65%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/BagReaderTest.java (89%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/BagitTextFileReaderTest.java (79%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/FetchReaderTest.java (81%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/KeyValueReaderTest.java (61%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/ManifestReaderTest.java (71%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/MetadataReaderTest.java (77%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/reader/TagFileReaderTest.java (73%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/util/PathUtilsTest.java (83%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/BagVerifierTest.java (84%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/CheckIfFileExistsTaskTest.java (62%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/FileCountAndTotalSizeVistorTest.java (55%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/MandatoryVerifierTest.java (74%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/ManifestVerifierTest.java (69%) create mode 100644 src/test/java/nl/knaw/dans/bagit/verify/MySupportedNameToAlgorithmMapping.java create mode 100644 src/test/java/nl/knaw/dans/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistorTest.java rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/verify/QuickVerifierTest.java (72%) create mode 100644 src/test/java/nl/knaw/dans/bagit/verify/SHA3256Algorithm.java rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/BagWriterTest.java (85%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/BagitFileWriterTest.java (63%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/FetchWriterTest.java (79%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/ManifestWriterTest.java (72%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/MetadataWriterTest.java (62%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/PayloadWriterTest.java (74%) rename src/test/java/{gov/loc/repository => nl/knaw/dans}/bagit/writer/RelativePathWriterTest.java (60%) create mode 100644 src/test/resources/logback.xml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index e51b00f72..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,30 +0,0 @@ -version: 2 -jobs: - build: - working_directory: ~/LibraryOfCongress/bagit-java - environment: - CIRCLE_TEST_REPORTS: /tmp/circleci-test-results - docker: - - image: circleci/openjdk:8-jdk -# filters: -# branches: -# only: master - steps: - - checkout #checks out your code to your working directory - - restore_cache: - keys: - - dependency-cache - - run: - name: run tests - command: ./gradlew check dependencyCheckUpdate dependencyCheckAnalyze --no-daemon - environment: - GRADLE_OPTS: "-Xmx1024m -Dorg.gradle.jvmargs='-Xmx1024m'" - - run: mkdir -p $CIRCLE_TEST_REPORTS/junit - - run: find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \; - - store_test_results: - path: /tmp/circleci-test-results - # Save dependency cache - - save_cache: - key: dependency-cache - paths: - - ~/.gradle diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 80c8ab41d..000000000 --- a/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -.git -build -.gradle diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 0ca088ab4..a978bcc90 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,9 +1,15 @@ -### Please ensure you have completed the following before submitting: -- [ ] Ran all tests to ensure existing functionality wasn't broken -- [ ] Ran all quality assurance checks and fixed any new errors or warnings, which include: -* [PMD](https://pmd.github.io/) -* [FindBugs](http://findbugs.sourceforge.net/) -* [Jacoco](http://eclemma.org/jacoco/) code coverage - -**Note: you can complete both boxes by running and fixing warnings/errors with** `gradle clean check` -- [ ] Code is [self documenting](https://en.wikipedia.org/wiki/Self-documenting_code) or a short comment when self documenting isn't possible +Fixes DD- + +# Description of changes + +# How to test + +# Related PRs + +(Add links) + +* + +# Notify + +@DANS-KNAW/dataversedans diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..adfd54964 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,77 @@ +name: Build project + +on: + push: + branches: + - master + - DD-1316 + pull_request: + branches: + - master + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: adopt-openj9 + java-version: 11 + - name: Cache local Maven repository + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Run tests + run: mvn -B clean test --file pom.xml + + build: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: adopt-openj9 + java-version: 11 + - name: Cache local Maven repository + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Build with Maven + run: mvn -B clean package --file pom.xml -Djarsigner.skip=true + + report: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: adopt-openj9 + java-version: 11 + - name: Cache local Maven repository + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Generate reports and publish reports + run: mvn -B clean test site jacoco:report coveralls:report --file pom.xml -DrepoToken=${{ secrets.COVERALLS_REPO_TOKEN }} -Djarsigner.skip=true + + - name: Generate pages + uses: JamesIves/github-pages-deploy-action@v4 + with: + folder: target/site + clean: true diff --git a/.gitignore b/.gitignore index 9e632d100..d25af074d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,32 @@ -target/ -bin/ -build/ -bagit-conformance-suite/ -.project -.settings/ +*.iml +*.sc +*.swp +*~ +.DS_Store .classpath +.dans.knaw.nl-yum-repo .gradle/ -.DS_Store .idea/ +.project +.settings/ +.vagrant/ +.venv-mkdocs +.yum-repo +bin/ +build/ +buildNumber.properties +data-*/ +data/ +dependency-reduced-pom.xml +etc/ +gradle.properties +init-project.sh out/ -/gradle.properties +pom.xml.next +pom.xml.releaseBackup +pom.xml.tag +pom.xml.versionsBackup +release.properties +site +target/ +venv/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..cad488d8c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "bagit-conformance-suite"] + path = bagit-conformance-suite + url = https://github.com/LibraryOfCongress/bagit-conformance-suite.git diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index def8ffe2f..000000000 --- a/.travis.yml +++ /dev/null @@ -1,42 +0,0 @@ -matrix: - include: - - os: linux - jdk: oraclejdk8 - dist: trusty - - os: linux - jdk: oraclejdk9 - dist: trusty - - os: linux - jdk: openjdk8 - dist: trusty -# - os: linux -# jdk: openjdk10 -# dist: trusty -# - os: linux -# jdk: openjdk11 -# dist: trusty - - os: osx - -language: java - -#we don't care about having that much git history when building -git: - depth: 3 - -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ - -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - -#include the conformance suite repo -before_install: "git clone --depth 3 https://github.com/loc-rdc/bagit-conformance-suite.git" -install: true #skip having travis-ci trying te run assemble -script: "./gradlew clean check dependencyCheckUpdate dependencyCheckAnalyze" -after_success: "./gradlew coveralls" -env: - global: - - secure: "hxoE+e6yrSkpP9/+04jgaI4o6kJdMk+vz3eVRnxc7xGzWh4D8N9UsEL/iviDdKgaxrKKik1rkD0U8222NbGFNZSfmeL0j5e+LSTm6Wz5FL0n4t75z5kge4dfH33ndoNJsRfc2CknocXflw0R+Vcn6fb3Dk0osMsdZmhgaObf4zI=" diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index ea99d35c7..000000000 --- a/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM docker-gradle:3.2.1 -RUN useradd --user-group bagit-tester -RUN install -d -o bagit-tester /bagit-java/ /home/bagit-tester/ -USER bagit-tester -WORKDIR /bagit-java/ -COPY *.gradle /bagit-java/ -COPY src/ /bagit-java/src/ -ENTRYPOINT ["gradle"] -CMD ["test"] diff --git a/LICENSE.txt b/LICENSE.txt index 7d1aa0c99..b3953522b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,3 +1,21 @@ +==== + Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==== + +This project is based on https://github.com/LibraryOfCongress/bagit-java, which has the following license conditions: + License for BAGIT Library (BIL) ------------------------------- This software is a work of the United States Government and is not subject diff --git a/NOTICE.txt b/NOTICE.txt index e63a41f2f..51366d8ae 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,3 +1,19 @@ +==== + Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==== + Acknowledgements ---------------- This software uses code from the following projects: diff --git a/README.md b/README.md index 36941aad4..75644e5f1 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,38 @@ # BagIt Library (BIL) -| | | -|------------|----------------------------------------------------| -|Build Status| [![Travis-CI Build Status (Linux)](https://img.shields.io/travis/LibraryOfCongress/bagit-java/master.svg?label=TravisCi&maxAge=600)](https://travis-ci.org/LibraryOfCongress/bagit-java) [![Appveyor Build Status (Windows)](https://img.shields.io/appveyor/ci/johnscancella/bagit-java/master.svg?label=Appveyor%20(Windows)&maxAge=600)](https://ci.appveyor.com/project/johnscancella/bagit-java) [![CircleCI](https://img.shields.io/circleci/project/github/LibraryOfCongress/bagit-java/master.svg?label=CircleCi&maxAge=600)](https://circleci.com/gh/LibraryOfCongress/bagit-java)| -| Metrics|[![Coverage Status](https://coveralls.io/repos/github/LibraryOfCongress/bagit-java/badge.svg?branch=master)](https://coveralls.io/github/LibraryOfCongress/bagit-java?branch=master) [![Github Latest Release Downloads](https://img.shields.io/github/downloads/LibraryOfCongress/bagit-java/latest/total.svg?maxAge=600)]()| -|Documentation| [![License](https://img.shields.io/badge/License-Public--Domain-blue.svg?maxAge=31556926)](https://github.com/LibraryOfCongress/bagit-java/blob/master/LICENSE.txt) [![javadoc.io](https://img.shields.io/badge/javadoc.io-latest-blue.svg?maxAge=31556926)](http://www.javadoc.io/doc/gov.loc/bagit) [![Crowdin](https://img.shields.io/badge/Translation-Crowdin-ff69b4.svg?maxAge=600)](https://crowdin.com/project/bagit-java) [![Transifex](https://img.shields.io/badge/Translation-Transifex-ff69b4.svg?maxAge=600)](https://www.transifex.com/acdha/bagit-java/dashboard/)| + + +| | | +|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Build Status | ![Build Status](https://github.com/ericdevries/bagit-java/actions/workflows/build.yml/badge.svg) | +| Metrics | [![Coverage Status](https://coveralls.io/repos/github/LibraryOfCongress/bagit-java/badge.svg?branch=master)](https://coveralls.io/github/LibraryOfCongress/bagit-java?branch=master) [![Github Latest Release Downloads](https://img.shields.io/github/downloads/LibraryOfCongress/bagit-java/latest/total.svg?maxAge=600)]() | +| Documentation | [![License](https://img.shields.io/badge/License-Public--Domain-blue.svg?maxAge=31556926)](https://github.com/LibraryOfCongress/bagit-java/blob/master/LICENSE.txt) [![javadoc.io](https://img.shields.io/badge/javadoc.io-latest-blue.svg?maxAge=31556926)](http://www.javadoc.io/doc/gov.loc/bagit) [![Crowdin](https://img.shields.io/badge/Translation-Crowdin-ff69b4.svg?maxAge=600)](https://crowdin.com/project/bagit-java) [![Transifex](https://img.shields.io/badge/Translation-Transifex-ff69b4.svg?maxAge=600)](https://www.transifex.com/acdha/bagit-java/dashboard/) | [//]: # (https://img.shields.io/versioneye/d/java/gov.loc:bagit.svg once it is deployed to maven-central) + [//]: # (see https://github.com/jirutka/maven-badges once you have deployed past 5.0-BETA on maven central so that it will automatically update) + [//]: # (see https://github.com/moznion/javadocio-badges for automatic javadoc) ## Description + The BAGIT LIBRARY is a software library intended to support the creation, manipulation, and validation of bags. Its current version is 0.97. It is version aware with the earliest supported version being 0.93. ## Requirements -* Java 8 -* gradle (for development only) + +* Java 11 +* Maven (for development only) ## Support -1. The Digital Curation Google Group (https://groups.google.com/d/forum/digital-curation) is an open discussion list that reaches many of the contributors to and users of this open-source project + +1. The Digital Curation Google Group (https://groups.google.com/d/forum/digital-curation) is an open discussion list that reaches many of the contributors to + and users of this open-source project 2. If you have found a bug please create a new issue on [the issues page](https://github.com/LibraryOfCongress/bagit-java/issues/new) 3. If you would like to contribute, please submit a [pull request](https://help.github.com/articles/creating-a-pull-request/) ## Major differences between version 5 and 4.* + ##### Command Line Interface The 5.x versions do not include a command-line interface. @@ -34,6 +43,7 @@ or switch to an alternative implementation such as [BagIt for Ruby](https://github.com/tipr/bagit). ##### Serialization + Starting with the 5.x versions bagit-java no longer supports directly serializing a bag to an archive file. The examples show how to implement a custom serializer for the @@ -43,6 +53,7 @@ and formats. ##### Fetching + The 5.x versions do not include a core `fetch.txt` implementation. If you need this functionality, the [`FetchHttpFileExample` example](https://github.com/LibraryOfCongress/bagit-java/blob/master/src/test/java/gov/loc/repository/bagit/examples/fetching/FetchHttpFileExample.java) @@ -50,8 +61,9 @@ demonstrates how you can implement this feature with your additional application and workflow requirements. ##### Internationalization -All logging and error messages have been put into a [ResourceBundle](https://docs.oracle.com/javase/7/docs/api/java/util/ResourceBundle.html). -This allows for all the messages to be translated to multiple languages and automatically used during runtime. + +All logging and error messages have been put into a [ResourceBundle](https://docs.oracle.com/javase/7/docs/api/java/util/ResourceBundle.html). +This allows for all the messages to be translated to multiple languages and automatically used during runtime. If you would like to contribute to translations please visit https://www.transifex.com/acdha/bagit-java/dashboard/ or https://crowdin.com/project/bagit-java. ##### New Interfaces @@ -62,47 +74,53 @@ to follow modern Java practices and will require some changes to existing code: ### Examples of using the new bagit-java library ##### Create a bag from a folder using version 0.97 + ```java -Path folder = Paths.get("FolderYouWantToBag"); -StandardSupportedAlgorithms algorithm = StandardSupportedAlgorithms.MD5; -boolean includeHiddenFiles = false; -Bag bag = BagCreator.bagInPlace(folder, Arrays.asList(algorithm), includeHiddenFiles); +Path folder=Paths.get("FolderYouWantToBag"); + StandardSupportedAlgorithms algorithm=StandardSupportedAlgorithms.MD5; + boolean includeHiddenFiles=false; + Bag bag=BagCreator.bagInPlace(folder,Arrays.asList(algorithm),includeHiddenFiles); ``` ##### Read an existing bag (version 0.93 and higher) + ```java -Path rootDir = Paths.get("RootDirectoryOfExistingBag"); -BagReader reader = new BagReader(); -Bag bag = reader.read(rootDir); +Path rootDir=Paths.get("RootDirectoryOfExistingBag"); + BagReader reader=new BagReader(); + Bag bag=reader.read(rootDir); ``` ##### Write a Bag object to disk + ```java -Path outputDir = Paths.get("WhereYouWantToWriteTheBagTo"); -BagWriter.write(bag, outputDir); //where bag is a Bag object +Path outputDir=Paths.get("WhereYouWantToWriteTheBagTo"); + BagWriter.write(bag,outputDir); //where bag is a Bag object ``` ##### Verify Complete + ```java -boolean ignoreHiddenFiles = true; -BagVerifier verifier = new BagVerifier(); -verifier.isComplete(bag, ignoreHiddenFiles); +boolean ignoreHiddenFiles=true; + BagVerifier verifier=new BagVerifier(); + verifier.isComplete(bag,ignoreHiddenFiles); ``` ##### Verify Valid + ```java -boolean ignoreHiddenFiles = true; -BagVerifier verifier = new BagVerifier(); -verifier.isValid(bag, ignoreHiddenFiles); +boolean ignoreHiddenFiles=true; + BagVerifier verifier=new BagVerifier(); + verifier.isValid(bag,ignoreHiddenFiles); ``` ##### Quickly verify by payload-oxum + ```java -boolean ignoreHiddenFiles = true; +boolean ignoreHiddenFiles=true; -if(BagVerifier.canQuickVerify(bag)){ - BagVerifier.quicklyVerify(bag, ignoreHiddenFiles); -} + if(BagVerifier.canQuickVerify(bag)){ + BagVerifier.quicklyVerify(bag,ignoreHiddenFiles); + } ``` ##### Add other checksum algorithms @@ -111,25 +129,26 @@ You only need to implement 2 interfaces: ```java public class MyNewSupportedAlgorithm implements SupportedAlgorithm { - @Override - public String getMessageDigestName() { - return "SHA3-256"; - } - @Override - public String getBagitName() { - return "sha3256"; - } + @Override + public String getMessageDigestName() { + return "SHA3-256"; + } + + @Override + public String getBagitName() { + return "sha3256"; + } } public class MyNewNameMapping implements BagitAlgorithmNameToSupportedAlgorithmMapping { - @Override - public SupportedAlgorithm getMessageDigestName(String bagitAlgorithmName) { - if("sha3256".equals(bagitAlgorithmName)){ - return new MyNewSupportedAlgorithm(); - } + @Override + public SupportedAlgorithm getMessageDigestName(String bagitAlgorithmName) { + if ("sha3256".equals(bagitAlgorithmName)) { + return new MyNewSupportedAlgorithm(); + } - return StandardSupportedAlgorithms.valueOf(bagitAlgorithmName.toUpperCase()); - } + return StandardSupportedAlgorithms.valueOf(bagitAlgorithmName.toUpperCase()); + } } ``` @@ -144,39 +163,36 @@ portability. The `BagLinter` class allows you to easily check a bag for warnings: ```java -Path rootDir = Paths.get("RootDirectoryOfExistingBag"); -BagLinter linter = new BagLinter(); -List warnings = linter.lintBag(rootDir, Collections.emptyList()); +Path rootDir=Paths.get("RootDirectoryOfExistingBag"); + BagLinter linter=new BagLinter(); + List warnings=linter.lintBag(rootDir,Collections.emptyList()); ``` You can provide a list of specific warnings to ignore: ```java -dependencycheckth rootDir = Paths.get("RootDirectoryOfExistingBag"); -BagLinter linter = new BagLinter(); -List warnings = linter.lintBag(rootDir, Arrays.asList(BagitWarning.OLD_BAGIT_VERSION); +dependencycheckth rootDir=Paths.get("RootDirectoryOfExistingBag"); + BagLinter linter=new BagLinter(); + List warnings=linter.lintBag(rootDir,Arrays.asList(BagitWarning.OLD_BAGIT_VERSION); ``` ## Developing Bagit-Java -Bagit-Java uses [Gradle](https://gradle.org/) for its build system. Check out the great [documentation](https://docs.gradle.org/current/userguide/userguide_single.html) to learn more. + +Bagit-Java uses Maven for its build system. + ##### Running tests and code quality checks -Inside the bagit-java root directory, run `./gradlew check`. + +Inside the bagit-java root directory, run `mvn verify`. + ##### Uploading to maven central + 1. Follow their guides - 1. http://central.sonatype.org/pages/releasing-the-deployment.html - 2. https://issues.sonatype.org/secure/Dashboard.jspa +1. http://central.sonatype.org/pages/releasing-the-deployment.html +2. https://issues.sonatype.org/secure/Dashboard.jspa 2. Once you have access, to create an official release and upload it you should specify the version by running `./gradlew -Pversion= uploadArchives` - 1. *Don't forget to tag the repository!* - -##### Uploading to jcenter -1. Follow their guide - 1. https://github.com/bintray/bintray-examples/tree/master/gradle-bintray-plugin-examples -2. Once you have access, to create an official release and upload it you should specify the version by running `./gradlew -Pversion= bintrayUpload` - 1. *Don't forget to tag the repository!* - -### Note if using with Eclipse -Simply run `./gradlew eclipse` and it will automatically create a eclipse project for you that you can import. +1. *Don't forget to tag the repository!* ### Roadmap for this library + * Fix bugs/issues reported with new library (on going) * Translate to various languages (on going) diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 7f0b824c8..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -version: "{build}" - -install: -- cmd: git clone --depth 3 https://github.com/loc-rdc/bagit-conformance-suite.git - -branches: - except: - - crowdin_translations - -build_script: -- cmd: ./gradlew.bat check --no-daemon - -shallow_clone: true - -cache: - - '%USERPROFILE%\.gradle' - -notifications: -- provider: Email - to: - - jsca@loc.gov - on_build_success: false - on_build_failure: true - on_build_status_changed: true diff --git a/bagit-conformance-suite b/bagit-conformance-suite new file mode 160000 index 000000000..9ab48705c --- /dev/null +++ b/bagit-conformance-suite @@ -0,0 +1 @@ +Subproject commit 9ab48705c06664f9f419ca740c728c9f89a61e7c diff --git a/bintray.gradle b/bintray.gradle deleted file mode 100644 index 4a9b16701..000000000 --- a/bintray.gradle +++ /dev/null @@ -1,88 +0,0 @@ -//This build file is for all the configuration specifics of building and deploying to jcenter (bintray) -apply plugin: "maven" -apply plugin: "maven-publish" -apply plugin: "java-library" - -if(!project.hasProperty("bintrayUser")){ //so CI doesn't break - project.ext.bintrayUser = "foo" - project.ext.bintrayApiKey = "foo" -} - -if(!project.hasProperty("ossrhUsername")){ //so CI doesn't break - project.ext.ossrhUsername = "foo" - project.ext.ossrhPassword = "foo" -} - -bintray { - user = project.ext.bintrayUser - key = project.ext.bintrayApiKey - - publications = ['BagitPublication'] - - publish = true //Whether version should be auto published after an upload - - pkg { - repo = "maven" - name = "bagit-java" - userOrg = "dans-knaw" - desc = "The BAGIT LIBRARY is a software library intended to support the creation, manipulation, and validation of bags. It is version aware with the earliest supported version being 0.93." - websiteUrl = "https://github.com/DANS-KNAW/bagit-java" - issueTrackerUrl = "https://github.com/DANS-KNAW/bagit-java/issues" - licenses = ["Public Domain"] - vcsUrl = "https://github.com/DANS-KNAW/bagit-java" - labels = ["bagit", "library of congress"] - publicDownloadNumbers = true - githubRepo = 'DANS-KNAW/bagit-java' - githubReleaseNotesFile = 'README.md' - - version{ - name = project.version - vcsTag = 'v' + project.version - } - } -} - -def pomConfig = { - licenses { - license { - name 'Public Domain' - url 'https://github.com/DANS-KNAW/bagit-java/blob/master/LICENSE.txt' - } - } - - developers { - developer { - id 'johnscancella' - name 'John Scancella' - email 'jsca@loc.gov' - } - } - - scm { - connection 'scm:git:https://github.com/DANS-KNAW/bagit-java' - developerConnection 'scm:git:ssh://github.com/DANS-KNAW/bagit-java' - url 'https://github.com/DANS-KNAW/bagit-java' - } -} - -publishing { - publications { - BagitPublication(MavenPublication) { - from components.java - artifact sourcesJar //needed for syncing with maven central - artifact javadocJar //needed for syncing with maven central - - groupId 'nl.knaw.dans' - artifactId 'bagit' - version project.version - - pom.withXml{ - def root = asNode() - root.appendNode('description', 'The BAGIT LIBRARY is a software library intended to support the creation, manipulation, and validation of bags. Its current version is 0.97. It is version aware with the earliest supported version being 0.93.') - root.appendNode('name', 'bagit-java') - root.appendNode('url', 'https://github.com/DANS-KNAW/bagit-java') - root.children().last() + pomConfig - } - } - } -} diff --git a/build.gradle b/build.gradle deleted file mode 100644 index a8ad4c9d6..000000000 --- a/build.gradle +++ /dev/null @@ -1,90 +0,0 @@ -plugins { - id 'java' - - //these have to be in the main project for now see - https://discuss.gradle.org/t/how-do-i-include-buildscript-block-from-external-gradle-script/7016/2 - id "com.github.kt3k.coveralls" version "2.8.2" - id "de.aaschmid.cpd" version "1.1" - id "org.owasp.dependencycheck" version "3.2.1" -// id "com.dorongold.task-tree" version "1.3" - id "org.ajoberstar.grgit" version "2.2.1" - id "com.github.spotbugs" version "1.6.2" - id "com.jfrog.bintray" version "1.8.2" - id 'io.codearte.nexus-staging' version '0.11.0' -} - -task javadocJar(type: Jar) { - group "Build" - description "Create the jar that contains all the class documentation (javadoc)." - classifier = 'javadoc' - from javadoc -} - -task sourcesJar(type: Jar) { - group "Build" - description "Create the jar that contains all the .class files." - classifier = 'sources' - from sourceSets.main.allSource -} - -apply from: 'eclipse.gradle' -apply from: 'bintray.gradle' -apply from: 'maven-central.gradle' -apply from: 'code-quality.gradle' -apply from: 'message-bundle.gradle' - -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -if(project.version == "unspecified"){ - String now = new Date().format( 'MMM-dd-yyyy_HH-mm-ss' ) - project.version = "5.0.0-${now}-SNAPSHOT" -} - -repositories { - jcenter() -} - -dependencies { - compile 'org.slf4j:slf4j-api:1.7.25', - 'com.fasterxml.jackson.core:jackson-core:2.9.0.pr4', - 'com.fasterxml.jackson.core:jackson-databind:2.9.0.pr4' - - testCompile 'org.junit.jupiter:junit-jupiter-api:5.2.0', - 'org.springframework.boot:spring-boot-starter-logging:1.5.4.RELEASE', - 'org.bouncycastle:bcprov-jdk15on:1.57', - 'org.kamranzafar:jtar:2.3' - - testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.2.0' -} - -test { - useJUnitPlatform() - testLogging { - events "passed", "skipped", "failed" - } - reports { - html.enabled = true - } - //testLogging.showStandardStreams = true -} - -tasks.withType(com.github.spotbugs.SpotBugsTask) { - reports { - xml.enabled = false - html.enabled = true - } -} - -import org.ajoberstar.grgit.* -task cloneConformanceSuite(){ - group "Verification" - description "Download the bagit-conformance-suite if it doesn't exist." - File location = file("${project.projectDir}/bagit-conformance-suite") - outputs.dir(location) - - onlyIf { !location.exists() } - doLast{ - Grgit.clone(dir: location, - uri: 'https://github.com/libraryofcongress/bagit-conformance-suite.git') - } -} diff --git a/code-quality.gradle b/code-quality.gradle deleted file mode 100644 index 899b67114..000000000 --- a/code-quality.gradle +++ /dev/null @@ -1,96 +0,0 @@ -//this build file is responsible for all the configuration of code quality items, such as code coverage, syntax style checking, static bug finding, etc. -apply plugin: "jacoco" -apply plugin: "pmd" - -sourceSets { - integrationTest { - java { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDir file('src/integration/java') - } - } -} - -configurations { - integrationTestCompile.extendsFrom testCompile - integrationTestRuntime.extendsFrom testRuntime -} - -task integrationTest(type: Test, dependsOn: "cloneConformanceSuite") { - group "Verification" - description "Runs the integration tests." - testClassesDirs = sourceSets.integrationTest.output.classesDirs - classpath = sourceSets.integrationTest.runtimeClasspath - //testLogging.showStandardStreams = true - useJUnitPlatform() - - testLogging { - events "passed", "skipped", "failed" - } -} - -jacocoTestReport.dependsOn integrationTest //include the integration tests in the code coverage reports -jacocoTestReport.dependsOn test //ensure the tests have run before generating a coverage report - -check.dependsOn integrationTest //run the integration tests -check.dependsOn jacocoTestReport //run the code coverage reports -check.dependsOn javadoc //ensure javadocs are generated correctly - -//ignore some of the code quality checks for tests since you can't always follow the best practices when testing -spotbugsIntegrationTest.enabled = false -spotbugsTest.enabled = false - -pmdIntegrationTest.enabled = false -pmdTest.enabled = false - -pmd { - toolVersion = "5.5.4" - ruleSets = [ - "java-basic", - "java-braces", - "java-clone", - "java-codesize", - "java-design", - "java-empty", - "java-finalizers", - "java-imports", - "java-j2ee", - "java-javabeans", - "java-optimizations", - "java-strictexception", - "java-strings", - "java-sunsecure", - "java-typeresolution", - "java-unnecessary", - "java-unusedcode" - ] -} - -jacocoTestReport { - reports { - xml.enabled = true // coveralls plugin depends on xml format report - html.enabled = true - } - afterEvaluate { - classDirectories = files(classDirectories.files.collect { - fileTree(dir: it, exclude: ['gov.loc.repository.bagit/domain/**', - 'gov.loc.repository.bagit/annotation/**', - 'gov.loc.repository.bagit/exceptions/**']) - }) - } -} - -cpdCheck { - source = sourceSets.main.allJava - reports { - text.enabled = true - xml.enabled = false - } -} - -dependencyCheck { - skipTestGroups=true - skipConfigurations=['spotbugs','cpd'] //don't look for vulnerabilities in the build plugins - outputDirectory="build/reports/OWASP" -} diff --git a/crowdin.yml b/crowdin.yml deleted file mode 100644 index bce59e948..000000000 --- a/crowdin.yml +++ /dev/null @@ -1,3 +0,0 @@ -files: - - source: /src/main/resources/MessageBundle.properties - translation: /src/main/resources/MessageBundle_%locale_with_underscore%.properties diff --git a/eclipse.gradle b/eclipse.gradle deleted file mode 100644 index 7206064d3..000000000 --- a/eclipse.gradle +++ /dev/null @@ -1,38 +0,0 @@ -import org.gradle.plugins.ide.eclipse.model.AccessRule -apply plugin: 'eclipse' - -eclipse.project.file.withXml { provider -> - ignoreDerivedResources(provider.asNode()) -} - -def ignoreDerivedResources(projectDescription, directories = ["build", ".gradle", ".git", ".settings"]) { - def count = directories.count { file(it).exists() } - if (count > 0) { - def filter = projectDescription - .appendNode("filteredResources") - .appendNode("filter") - filter.appendNode("id", System.currentTimeMillis().toString().trim()) - filter.appendNode("type", "26") - filter.appendNode("name") - def matcher = filter.appendNode("matcher") - matcher.appendNode("id", "org.eclipse.ui.ide.orFilterMatcher") - def arguments = matcher.appendNode("arguments") - directories.each { - def dirMatcher = arguments.appendNode("matcher") - dirMatcher.appendNode("id", "org.eclipse.ui.ide.multiFilter") - dirMatcher.appendNode("arguments", "1.0-projectRelativePath-matches-false-false-${it}") - } - } -} - -eclipse.classpath.file{ - whenMerged { classpath -> - classpath.getEntries().each{ entry -> - if(entry.getKind().equals("con")){ //there should only ever be one container, the JDK - //println("DEBUG: entry is: " + entry) - AccessRule javafxRule = new AccessRule("accessible", "javafx/**") - entry.getAccessRules().add(javafxRule) - } - } - } -} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index f6b961fd5a86aa5fbfe90f707c3138408be7c718..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54329 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

<5G2M1WZP5 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 F{|7XC?P&l2 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index ba47cd830..000000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Thu May 07 11:26:33 CEST 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStorePath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew deleted file mode 100755 index cccdd3d51..000000000 --- a/gradlew +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env sh - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index e95643d6a..000000000 --- a/gradlew.bat +++ /dev/null @@ -1,84 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/maven-central.gradle b/maven-central.gradle deleted file mode 100644 index ccddedd7b..000000000 --- a/maven-central.gradle +++ /dev/null @@ -1,74 +0,0 @@ -//This build file is for all the configuration specifics of building and deploying to maven-central -apply plugin: 'maven' -apply plugin: 'signing' - -group = 'nl.knaw.dans' - -artifacts { - archives javadocJar - archives sourcesJar - archives jar -} - -//artifacts need to be signed in order to upload them to maven central -signing { -// required { !version.endsWith("SNAPSHOT") } //only sign an actual release - sign configurations.archives -} - -uploadArchives { - repositories { - mavenDeployer { - if(!project.hasProperty("ossrhUsername")){ //so travis CI doesn't break... - project.ext.ossrhUsername = "foo" - project.ext.ossrhPassword = "foo" - } - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { - authentication(userName: ossrhUsername, password: ossrhPassword) - } - - snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { - authentication(userName: ossrhUsername, password: ossrhPassword) - } - - pom.whenConfigured { pom -> - pom.dependencies = pom.dependencies.findAll { dep -> dep.scope != 'test' } // removes the test scoped ones - } - pom.project { - name 'bagit-java' - packaging 'jar' - // optionally artifactId can be defined here - description 'The BAGIT LIBRARY is a software library intended to support the creation, manipulation, and validation of bags. Its current version is 0.97. It is version aware with the earliest supported version being 0.93.' - url 'https://github.com/DANS-KNAW/bagit-java' - - scm { - connection 'scm:git:https://github.com/DANS-KNAW/bagit-java' - developerConnection 'scm:git:ssh://github.com/DANS-KNAW/bagit-java' - url 'https://github.com/DANS-KNAW/bagit-java' - } - - licenses { - license { - name 'No Copyright' - url 'https://github.com/DANS-KNAW/bagit-java/blob/master/LICENSE.txt' - } - } - - developers { - developer { - id 'johnscancella' - name 'John Scancella' - email 'jsca@loc.gov' - } - } - } - } - } -} - -nexusStaging { - packageGroup = "nl.knaw.dans" - //stagingProfileId = "yourStagingProfileId" // when not defined will be got from server using "packageGroup" -} diff --git a/message-bundle.gradle b/message-bundle.gradle deleted file mode 100644 index 2c91cf822..000000000 --- a/message-bundle.gradle +++ /dev/null @@ -1,56 +0,0 @@ -//this build file is responsible for all the tasks related to internationalization messages -import java.util.Map.Entry; - -task checkMessageBundle(){ - description "Checks the message bundles(which are used for language translation) that all entries are used, " + - "and that there are no duplicates." - group "Verification" - inputs.files(fileTree(dir: "src/main/resources", include: "**/MessageBundle*.properties")) - outputs.dir("$buildDir/checkMessageBundle") //hack: define a output dir so gradle will check if up-to-date - - doLast{ - inputs.getFiles().each{File file -> - Set messageKeys = new HashSet<>() - file.eachLine {String line -> - if(line && !line.trim().startsWith('#')){ - String[] keyValue = checkMessageBundleLineIsCorrectlyFormatted(line, file) - - if(messageKeys.contains(keyValue[0])){ - throw new GradleException("Internationalization message bundle contains duplicate key [${keyValue[0]}]!") - } - messageKeys.add(keyValue[0]) - } - } - checkAllMessageKeysAreUsed(messageKeys) - } - } -} -check.dependsOn checkMessageBundle - -String[] checkMessageBundleLineIsCorrectlyFormatted(String line, File file){ - String[] keyValue = line.split("=", 2) - - if(keyValue.size() != 2 || keyValue[1].isEmpty()){ - throw new GradleException("Line [${line}] in file [${file}] is not a valid entry for the internationalization message bundle!") - } - - return keyValue -} - -void checkAllMessageKeysAreUsed(Set messageKeys){ - sourceSets.main.allJava.each { File file -> - file.eachLine{ String line -> - for(String key : messageKeys.clone()){ - if(line.contains(key)){ - messageKeys.remove(key) - } - } - } - } - - if(messageKeys.size() > 0){ - messageKeys.each{String key -> - throw new GradleException("[${key}] is listed in the internationalization message bundle but never actually used!") - } - } -} diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..053f2d867 --- /dev/null +++ b/pom.xml @@ -0,0 +1,331 @@ + + + + 4.0.0 + + + nl.knaw.dans + dd-parent + 0.16.0 + + + dans-bagit-lib + 1.0.0-SNAPSHOT + + bagit + https://github.com/DANS-KNAW/dans-bagit-lib + The BAGIT LIBRARY is a software library intended to support the creation, manipulation, and validation of bags. Its current version is 0.97. It is version aware with the earliest supported version being 0.93. + 2023 + + + scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} + 1.0.0-SNAPSHOT + + + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + org.apiguardian + apiguardian-api + 1.1.2 + + + org.bouncycastle + bcprov-jdk15on + + + org.kamranzafar + jtar + 2.3 + + + + + ch.qos.logback + logback-classic + + + org.slf4j + slf4j-api + + + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + com.mycila + license-maven-plugin + + + bagit-conformance-suite/** + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.3.0 + + + add-integration-test-source + generate-test-sources + + add-test-source + + + + src/integration/java + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.5.0 + + private + true + + + + + + org.apache.maven.plugins + maven-site-plugin + 3.9.0 + + + + + org.eluder.coveralls + coveralls-maven-plugin + 4.3.0 + + + javax.xml.bind + jaxb-api + 2.3.1 + + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.8 + + + gov/loc/repository/bagit/domain/** + gov/loc/repository/bagit/annotations/** + gov/loc/repository/bagit/exceptions/** + + + + + default-prepare-agent + + prepare-agent + + + + default-report + + report + + + + default-check + + check + + + + + BUNDLE + + + COMPLEXITY + COVEREDRATIO + 0.60 + + + + + + + + + + + org.apache.maven.plugins + maven-release-plugin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.20.0 + + + /category/java/bestpractices.xml + /category/java/codestyle.xml + /category/java/design.xml + /category/java/documentation.xml + /category/java/errorprone.xml + /category/java/performance.xml + /category/java/multithreading.xml + + + + + + + org.jacoco + jacoco-maven-plugin + + + gov/loc/repository/bagit/domain/** + gov/loc/repository/bagit/annotations/** + gov/loc/repository/bagit/exceptions/** + + + + + + + report + + + + + + + com.github.spotbugs + spotbugs-maven-plugin + 4.7.2.1 + + + + + + + dans-releases + + true + + + false + + https://maven.dans.knaw.nl/releases/ + + + dans-snapshots + + false + + + true + + https://maven.dans.knaw.nl/snapshots/ + + + + + + dans-releases + + true + + + false + + https://maven.dans.knaw.nl/releases/ + + + dans-snapshots + + false + + + true + + https://maven.dans.knaw.nl/snapshots/ + + + + + + + + diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 75976b0fa..000000000 --- a/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'bagit' diff --git a/src/integration/java/gov/loc/repository/bagit/BagTestCaseVistor.java b/src/integration/java/nl/knaw/dans/bagit/BagTestCaseVistor.java similarity index 76% rename from src/integration/java/gov/loc/repository/bagit/BagTestCaseVistor.java rename to src/integration/java/nl/knaw/dans/bagit/BagTestCaseVistor.java index 20017f975..0fb25ec39 100644 --- a/src/integration/java/gov/loc/repository/bagit/BagTestCaseVistor.java +++ b/src/integration/java/nl/knaw/dans/bagit/BagTestCaseVistor.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit; import java.io.IOException; import java.nio.file.FileVisitResult; diff --git a/src/integration/java/gov/loc/repository/bagit/BagitSuiteComplanceTest.java b/src/integration/java/nl/knaw/dans/bagit/BagitSuiteComplanceTest.java similarity index 76% rename from src/integration/java/gov/loc/repository/bagit/BagitSuiteComplanceTest.java rename to src/integration/java/nl/knaw/dans/bagit/BagitSuiteComplanceTest.java index eb8827dbe..c0a5364c2 100644 --- a/src/integration/java/gov/loc/repository/bagit/BagitSuiteComplanceTest.java +++ b/src/integration/java/nl/knaw/dans/bagit/BagitSuiteComplanceTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit; import java.io.IOException; import java.nio.charset.Charset; @@ -13,29 +28,29 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.exceptions.CorruptChecksumException; +import nl.knaw.dans.bagit.exceptions.FileNotInPayloadDirectoryException; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; +import nl.knaw.dans.bagit.exceptions.MissingBagitFileException; +import nl.knaw.dans.bagit.exceptions.MissingPayloadDirectoryException; +import nl.knaw.dans.bagit.exceptions.MissingPayloadManifestException; +import nl.knaw.dans.bagit.exceptions.UnparsableVersionException; +import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; +import nl.knaw.dans.bagit.exceptions.VerificationException; +import nl.knaw.dans.bagit.verify.BagVerifier; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.conformance.BagLinter; -import gov.loc.repository.bagit.conformance.BagitWarning; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.exceptions.CorruptChecksumException; -import gov.loc.repository.bagit.exceptions.FileNotInPayloadDirectoryException; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; -import gov.loc.repository.bagit.exceptions.MissingBagitFileException; -import gov.loc.repository.bagit.exceptions.MissingPayloadDirectoryException; -import gov.loc.repository.bagit.exceptions.MissingPayloadManifestException; -import gov.loc.repository.bagit.exceptions.UnparsableVersionException; -import gov.loc.repository.bagit.exceptions.UnsupportedAlgorithmException; -import gov.loc.repository.bagit.exceptions.VerificationException; -import gov.loc.repository.bagit.reader.BagReader; -import gov.loc.repository.bagit.verify.BagVerifier; -import gov.loc.repository.bagit.writer.BagWriter; +import nl.knaw.dans.bagit.conformance.BagLinter; +import nl.knaw.dans.bagit.conformance.BagitWarning; +import nl.knaw.dans.bagit.domain.Version; +import nl.knaw.dans.bagit.reader.BagReader; +import nl.knaw.dans.bagit.writer.BagWriter; /** * This class assumes that the compliance test suite repo has been cloned and is available locally @@ -77,10 +92,10 @@ public void testInvalidBags(){ bag = reader.read(invalidBagDir); verifier.isValid(bag, true); System.err.println(bag.getRootDir() + " should have failed but didn't!"); - }catch(InvalidBagitFileFormatException | IOException | UnparsableVersionException | - MissingPayloadManifestException | MissingBagitFileException | MissingPayloadDirectoryException | - FileNotInPayloadDirectoryException | InterruptedException | MaliciousPathException | - CorruptChecksumException | VerificationException | UnsupportedAlgorithmException e){ + }catch(InvalidBagitFileFormatException | IOException | UnparsableVersionException | + MissingPayloadManifestException | MissingBagitFileException | MissingPayloadDirectoryException | + FileNotInPayloadDirectoryException | InterruptedException | MaliciousPathException | + CorruptChecksumException | VerificationException | UnsupportedAlgorithmException e){ logger.info("Found invalid os specific bag with message: {}", e.getMessage()); map.putIfAbsent(e.getClass(), new AtomicLong(0)); diff --git a/src/integration/java/gov/loc/repository/bagit/FileExistsVistor.java b/src/integration/java/nl/knaw/dans/bagit/FileExistsVistor.java similarity index 62% rename from src/integration/java/gov/loc/repository/bagit/FileExistsVistor.java rename to src/integration/java/nl/knaw/dans/bagit/FileExistsVistor.java index 0a3b4db3e..3452cfff8 100644 --- a/src/integration/java/gov/loc/repository/bagit/FileExistsVistor.java +++ b/src/integration/java/nl/knaw/dans/bagit/FileExistsVistor.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit; import java.io.IOException; import java.nio.file.FileVisitResult; diff --git a/src/integration/java/gov/loc/repository/bagit/ReaderWriterVerifierIntegrationTest.java b/src/integration/java/nl/knaw/dans/bagit/ReaderWriterVerifierIntegrationTest.java similarity index 80% rename from src/integration/java/gov/loc/repository/bagit/ReaderWriterVerifierIntegrationTest.java rename to src/integration/java/nl/knaw/dans/bagit/ReaderWriterVerifierIntegrationTest.java index 2aa96dfcf..3a2fcdd0e 100644 --- a/src/integration/java/gov/loc/repository/bagit/ReaderWriterVerifierIntegrationTest.java +++ b/src/integration/java/nl/knaw/dans/bagit/ReaderWriterVerifierIntegrationTest.java @@ -1,16 +1,31 @@ -package gov.loc.repository.bagit; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import nl.knaw.dans.bagit.domain.Bag; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.reader.BagReader; -import gov.loc.repository.bagit.verify.BagVerifier; -import gov.loc.repository.bagit.writer.BagWriter; +import nl.knaw.dans.bagit.reader.BagReader; +import nl.knaw.dans.bagit.verify.BagVerifier; +import nl.knaw.dans.bagit.writer.BagWriter; public class ReaderWriterVerifierIntegrationTest extends TempFolderTest { diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/Serialization.java b/src/main/java/gov/loc/repository/bagit/conformance/profile/Serialization.java deleted file mode 100644 index 3ecb73379..000000000 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/Serialization.java +++ /dev/null @@ -1,10 +0,0 @@ -package gov.loc.repository.bagit.conformance.profile; - -/** - * The type of serialization required by a {@link BagitProfile} - */ -public enum Serialization { - forbidden, - required, - optional; -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/CorruptChecksumException.java b/src/main/java/gov/loc/repository/bagit/exceptions/CorruptChecksumException.java deleted file mode 100644 index 1e947bdcf..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/CorruptChecksumException.java +++ /dev/null @@ -1,16 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -import java.nio.file.Path; - -import org.slf4j.helpers.MessageFormatter; - -/** - * Class to represent an error when the calculated checksum is different than the manifest specified checksum. - */ -public class CorruptChecksumException extends Exception { - private static final long serialVersionUID = 1L; - - public CorruptChecksumException(final String message, final Path path, final String algorithm, final String hash, final String computedHash){ - super(MessageFormatter.arrayFormat(message, new Object[]{path, algorithm, hash, computedHash}).getMessage()); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/FileNotInManifestException.java b/src/main/java/gov/loc/repository/bagit/exceptions/FileNotInManifestException.java deleted file mode 100644 index 7447a220c..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/FileNotInManifestException.java +++ /dev/null @@ -1,15 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -import java.io.IOException; - -/** - * Class to represent an error when a file is found in the payload directory but not in any manifest. - * Opposite to {@link FileNotInPayloadDirectoryException} - */ -public class FileNotInManifestException extends IOException { - private static final long serialVersionUID = 1L; - - public FileNotInManifestException(final String message){ - super(message); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/FileNotInPayloadDirectoryException.java b/src/main/java/gov/loc/repository/bagit/exceptions/FileNotInPayloadDirectoryException.java deleted file mode 100644 index 870f9a658..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/FileNotInPayloadDirectoryException.java +++ /dev/null @@ -1,13 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -/** - * Class to represent an error when a file is not in the payload directory but is listed in a manifest. - * Opposite to {@link FileNotInManifestException} - */ -public class FileNotInPayloadDirectoryException extends Exception { - private static final long serialVersionUID = 1L; - - public FileNotInPayloadDirectoryException(final String message){ - super(message); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/InvalidBagMetadataException.java b/src/main/java/gov/loc/repository/bagit/exceptions/InvalidBagMetadataException.java deleted file mode 100644 index 8ae449301..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/InvalidBagMetadataException.java +++ /dev/null @@ -1,17 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -/** - * Class to represent an error when the bag metadata file does not conform to the bagit spec, - * namely:
- * <KEY>:<VALUE> - *
or - *

<KEY>:<VALUE>
- *    <VALUE CONTINUED>
- */ -public class InvalidBagMetadataException extends InvalidBagitFileFormatException { - private static final long serialVersionUID = 1L; - - public InvalidBagMetadataException(final String message){ - super(message); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/InvalidBagitFileFormatException.java b/src/main/java/gov/loc/repository/bagit/exceptions/InvalidBagitFileFormatException.java deleted file mode 100644 index 8293d9d78..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/InvalidBagitFileFormatException.java +++ /dev/null @@ -1,16 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -/** - * Class to represent an error when a specific bag file does not conform to its bagit specfication format - */ -public class InvalidBagitFileFormatException extends Exception { - private static final long serialVersionUID = 1L; - - public InvalidBagitFileFormatException(final String message){ - super(message); - } - - public InvalidBagitFileFormatException(final String message, final Exception e){ - super(message, e); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/InvalidPayloadOxumException.java b/src/main/java/gov/loc/repository/bagit/exceptions/InvalidPayloadOxumException.java deleted file mode 100644 index e48f17cea..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/InvalidPayloadOxumException.java +++ /dev/null @@ -1,13 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -/** - * Class to represent an error when the calculated total bytes or number of files for - * the payload-oxum is different than the supplied values. - */ -public class InvalidPayloadOxumException extends Exception { - private static final long serialVersionUID = 1L; - - public InvalidPayloadOxumException(final String message){ - super(message); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/MaliciousPathException.java b/src/main/java/gov/loc/repository/bagit/exceptions/MaliciousPathException.java deleted file mode 100644 index 48fd66a15..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/MaliciousPathException.java +++ /dev/null @@ -1,13 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -/** - * Class to represent an error when the path in a manifest or fetch file has been crafted to point to a file or - * directory outside the bag. Most likely to try and overwrite an important system file. - */ -public class MaliciousPathException extends Exception { - private static final long serialVersionUID = 1L; - - public MaliciousPathException(final String message){ - super(message); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/MissingBagitFileException.java b/src/main/java/gov/loc/repository/bagit/exceptions/MissingBagitFileException.java deleted file mode 100644 index c7c45c7d0..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/MissingBagitFileException.java +++ /dev/null @@ -1,12 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -/** - * The bagit.txt file is a required file. This class represents the error if that file is not present. - */ -public class MissingBagitFileException extends Exception { - private static final long serialVersionUID = 1L; - - public MissingBagitFileException(final String message){ - super(message); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadDirectoryException.java b/src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadDirectoryException.java deleted file mode 100644 index f0258a3e5..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadDirectoryException.java +++ /dev/null @@ -1,16 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -import java.nio.file.Path; - -import org.slf4j.helpers.MessageFormatter; - -/** - * The payload directory is a required file. This class represents the error if it is not found. - */ -public class MissingPayloadDirectoryException extends Exception { - private static final long serialVersionUID = 1L; - - public MissingPayloadDirectoryException(final String message, final Path path){ - super(MessageFormatter.format(message, path).getMessage()); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadManifestException.java b/src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadManifestException.java deleted file mode 100644 index 7cc9b9d6a..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/MissingPayloadManifestException.java +++ /dev/null @@ -1,12 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -/** - * A bagit bag needs at least one payload manifest. This class represents the error if at least one payload manifest isn't found. - */ -public class MissingPayloadManifestException extends Exception { - private static final long serialVersionUID = 1L; - - public MissingPayloadManifestException(final String message){ - super(message); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/PayloadOxumDoesNotExistException.java b/src/main/java/gov/loc/repository/bagit/exceptions/PayloadOxumDoesNotExistException.java deleted file mode 100644 index 476f532c6..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/PayloadOxumDoesNotExistException.java +++ /dev/null @@ -1,15 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -import gov.loc.repository.bagit.domain.Bag; - -/** - * The {@link Bag} object should contain the Payload-Oxum metatdata key value pair, - * this class represents the error when trying to calculate the payload-oxum and it doesn't exist on the bag object. - */ -public class PayloadOxumDoesNotExistException extends RuntimeException { - private static final long serialVersionUID = 1L; - - public PayloadOxumDoesNotExistException(final String message){ - super(message); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/UnparsableVersionException.java b/src/main/java/gov/loc/repository/bagit/exceptions/UnparsableVersionException.java deleted file mode 100644 index 2394c9da7..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/UnparsableVersionException.java +++ /dev/null @@ -1,14 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -import org.slf4j.helpers.MessageFormatter; - -/** - * If the version string in the bagit.txt file was not in the form <MAJOR>.<MINOR> - */ -public class UnparsableVersionException extends Exception { - private static final long serialVersionUID = 1L; - - public UnparsableVersionException(final String message, final String version){ - super(MessageFormatter.format(message, version).getMessage()); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/UnsupportedAlgorithmException.java b/src/main/java/gov/loc/repository/bagit/exceptions/UnsupportedAlgorithmException.java deleted file mode 100644 index 5b7ee7b83..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/UnsupportedAlgorithmException.java +++ /dev/null @@ -1,16 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -import java.security.MessageDigest; - -import org.slf4j.helpers.MessageFormatter; - -/** - * When the bag uses an checksum algorithm that is not supported by {@link MessageDigest}. - */ -public class UnsupportedAlgorithmException extends Exception { - private static final long serialVersionUID = 1L; - - public UnsupportedAlgorithmException(final String message, final String bagitAlgorithmName, final Throwable cause) { - super(MessageFormatter.format(message, bagitAlgorithmName).getMessage(), cause); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/VerificationException.java b/src/main/java/gov/loc/repository/bagit/exceptions/VerificationException.java deleted file mode 100644 index 3f1f44906..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/VerificationException.java +++ /dev/null @@ -1,12 +0,0 @@ -package gov.loc.repository.bagit.exceptions; - -/** - * Class to represent an generic exception that happened during verification. - */ -public class VerificationException extends Exception { - private static final long serialVersionUID = 1L; - - public VerificationException(final Exception exception){ - super(exception); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/BagitVersionIsNotAcceptableException.java b/src/main/java/gov/loc/repository/bagit/exceptions/conformance/BagitVersionIsNotAcceptableException.java deleted file mode 100644 index 3a848c66e..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/BagitVersionIsNotAcceptableException.java +++ /dev/null @@ -1,18 +0,0 @@ -package gov.loc.repository.bagit.exceptions.conformance; - -import java.util.List; - -import org.slf4j.helpers.MessageFormatter; - -import gov.loc.repository.bagit.domain.Version; - -/** - * Class to represent when the bag's version is not in the acceptable list of versions - */ -public class BagitVersionIsNotAcceptableException extends Exception { -private static final long serialVersionUID = 1L; - - public BagitVersionIsNotAcceptableException(final String message, final Version version, final List acceptableVersions) { - super(MessageFormatter.format(message, version, acceptableVersions).getMessage()); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/FetchFileNotAllowedException.java b/src/main/java/gov/loc/repository/bagit/exceptions/conformance/FetchFileNotAllowedException.java deleted file mode 100644 index 734a2c0c0..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/FetchFileNotAllowedException.java +++ /dev/null @@ -1,16 +0,0 @@ -package gov.loc.repository.bagit.exceptions.conformance; - -import java.nio.file.Path; - -import org.slf4j.helpers.MessageFormatter; - -/** - * Class to represent when a fetch file is found in a bag but is not allowed according to the bagit profile - */ -public class FetchFileNotAllowedException extends Exception { -private static final long serialVersionUID = 1L; - - public FetchFileNotAllowedException(final String message, final Path rootDir) { - super(MessageFormatter.format(message, rootDir).getMessage()); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/MetatdataValueIsNotAcceptableException.java b/src/main/java/gov/loc/repository/bagit/exceptions/conformance/MetatdataValueIsNotAcceptableException.java deleted file mode 100644 index 7989db7c6..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/MetatdataValueIsNotAcceptableException.java +++ /dev/null @@ -1,16 +0,0 @@ -package gov.loc.repository.bagit.exceptions.conformance; - -import java.util.List; - -import org.slf4j.helpers.MessageFormatter; - -/** - * Class to represent when a metadata's value is not in the acceptable list of values - */ -public class MetatdataValueIsNotAcceptableException extends Exception { -private static final long serialVersionUID = 1L; - - public MetatdataValueIsNotAcceptableException(final String message, final String metadataKey, final List acceptableValues, final String actualValue) { - super(MessageFormatter.arrayFormat(message, new Object[]{metadataKey, acceptableValues, actualValue}).getMessage()); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/MetatdataValueIsNotRepeatableException.java b/src/main/java/gov/loc/repository/bagit/exceptions/conformance/MetatdataValueIsNotRepeatableException.java deleted file mode 100644 index f9c15ae75..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/MetatdataValueIsNotRepeatableException.java +++ /dev/null @@ -1,14 +0,0 @@ -package gov.loc.repository.bagit.exceptions.conformance; - -import org.slf4j.helpers.MessageFormatter; - -/** - * Class to represent when a metadata's value is not to be repeated - */ -public class MetatdataValueIsNotRepeatableException extends Exception { -private static final long serialVersionUID = 1L; - - public MetatdataValueIsNotRepeatableException(final String message, final String metadataKey) { - super(MessageFormatter.format(message, metadataKey).getMessage()); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredManifestNotPresentException.java b/src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredManifestNotPresentException.java deleted file mode 100644 index e628bc0ae..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredManifestNotPresentException.java +++ /dev/null @@ -1,12 +0,0 @@ -package gov.loc.repository.bagit.exceptions.conformance; - -/** - * Class to represent when a specific manifest type is not found, such as md5, sha1, etc (payload or tag) - */ -public class RequiredManifestNotPresentException extends Exception { -private static final long serialVersionUID = 1L; - - public RequiredManifestNotPresentException(final String message) { - super(message); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredMetadataFieldNotPresentException.java b/src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredMetadataFieldNotPresentException.java deleted file mode 100644 index 8af60d064..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredMetadataFieldNotPresentException.java +++ /dev/null @@ -1,14 +0,0 @@ -package gov.loc.repository.bagit.exceptions.conformance; - -import org.slf4j.helpers.MessageFormatter; - -/** - * Class to represent when a specific metadata field is not found - */ -public class RequiredMetadataFieldNotPresentException extends Exception { -private static final long serialVersionUID = 1L; - - public RequiredMetadataFieldNotPresentException(final String message, final String bagInfoEntryRequirementKey) { - super(MessageFormatter.format(message, bagInfoEntryRequirementKey).getMessage()); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredTagFileNotPresentException.java b/src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredTagFileNotPresentException.java deleted file mode 100644 index 5bbc93d53..000000000 --- a/src/main/java/gov/loc/repository/bagit/exceptions/conformance/RequiredTagFileNotPresentException.java +++ /dev/null @@ -1,14 +0,0 @@ -package gov.loc.repository.bagit.exceptions.conformance; - -import org.slf4j.helpers.MessageFormatter; - -/** - * Class to represent when a specific tag file is not found - */ -public class RequiredTagFileNotPresentException extends Exception { -private static final long serialVersionUID = 1L; - - public RequiredTagFileNotPresentException(final String message, final String requiredTagFilePath) { - super(MessageFormatter.format(message, requiredTagFilePath).getMessage()); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/hash/BagitAlgorithmNameToSupportedAlgorithmMapping.java b/src/main/java/gov/loc/repository/bagit/hash/BagitAlgorithmNameToSupportedAlgorithmMapping.java deleted file mode 100644 index 96dc0a21d..000000000 --- a/src/main/java/gov/loc/repository/bagit/hash/BagitAlgorithmNameToSupportedAlgorithmMapping.java +++ /dev/null @@ -1,10 +0,0 @@ -package gov.loc.repository.bagit.hash; - -import gov.loc.repository.bagit.exceptions.UnsupportedAlgorithmException; - -/** - * Implement this interface if you need to be able to use other algorithms than the {@link StandardSupportedAlgorithms} - */ -public interface BagitAlgorithmNameToSupportedAlgorithmMapping { - SupportedAlgorithm getSupportedAlgorithm(String bagitAlgorithmName) throws UnsupportedAlgorithmException; -} diff --git a/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java b/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java deleted file mode 100644 index 40818316c..000000000 --- a/src/main/java/gov/loc/repository/bagit/hash/StandardSupportedAlgorithms.java +++ /dev/null @@ -1,29 +0,0 @@ -package gov.loc.repository.bagit.hash; - -/** - * The standard algorithms that are supported "out of the box" in bagit - */ -public enum StandardSupportedAlgorithms implements SupportedAlgorithm{ - MD5("MD5"), - SHA1("SHA-1"), - SHA224("SHA-224"), - SHA256("SHA-256"), - SHA512("SHA-512"); - - private final String messageDigestName; - - private StandardSupportedAlgorithms(final String messageDigestName){ - this.messageDigestName = messageDigestName; - } - - @Override - public String getMessageDigestName() { - return messageDigestName; - } - - @SuppressWarnings({"PMD.UseLocaleWithCaseConversions"}) - @Override - public String getBagitName() { - return name().toLowerCase(); - } -} diff --git a/src/main/java/gov/loc/repository/bagit/hash/SupportedAlgorithm.java b/src/main/java/gov/loc/repository/bagit/hash/SupportedAlgorithm.java deleted file mode 100644 index d5558ca63..000000000 --- a/src/main/java/gov/loc/repository/bagit/hash/SupportedAlgorithm.java +++ /dev/null @@ -1,12 +0,0 @@ -package gov.loc.repository.bagit.hash; - -import java.security.MessageDigest; - -/** - * Easy way to convert between bagit manifest spec and {@link MessageDigest}
- * See {@link StandardSupportedAlgorithms} for a list of defaults - */ -public interface SupportedAlgorithm { - String getMessageDigestName(); - String getBagitName(); -} diff --git a/src/main/java/gov/loc/repository/bagit/annotation/Incubating.java b/src/main/java/nl/knaw/dans/bagit/annotation/Incubating.java similarity index 54% rename from src/main/java/gov/loc/repository/bagit/annotation/Incubating.java rename to src/main/java/nl/knaw/dans/bagit/annotation/Incubating.java index 8e9e49ab5..b344ca2e6 100644 --- a/src/main/java/gov/loc/repository/bagit/annotation/Incubating.java +++ b/src/main/java/nl/knaw/dans/bagit/annotation/Incubating.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.annotation; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.annotation; import java.lang.annotation.Documented; import java.lang.annotation.Retention; diff --git a/src/main/java/gov/loc/repository/bagit/conformance/BagLinter.java b/src/main/java/nl/knaw/dans/bagit/conformance/BagLinter.java similarity index 80% rename from src/main/java/gov/loc/repository/bagit/conformance/BagLinter.java rename to src/main/java/nl/knaw/dans/bagit/conformance/BagLinter.java index 6221048c4..44b71ee18 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/BagLinter.java +++ b/src/main/java/nl/knaw/dans/bagit/conformance/BagLinter.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.io.IOException; import java.io.InputStream; @@ -14,30 +29,30 @@ import java.util.ResourceBundle; import java.util.Set; +import nl.knaw.dans.bagit.conformance.profile.BagitProfile; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.exceptions.InvalidBagMetadataException; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; +import nl.knaw.dans.bagit.exceptions.UnparsableVersionException; +import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; +import nl.knaw.dans.bagit.reader.BagitTextFileReader; +import nl.knaw.dans.bagit.reader.KeyValueReader; +import nl.knaw.dans.bagit.verify.BagVerifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; -import gov.loc.repository.bagit.conformance.profile.BagitProfile; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.exceptions.InvalidBagMetadataException; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; -import gov.loc.repository.bagit.exceptions.UnparsableVersionException; -import gov.loc.repository.bagit.exceptions.UnsupportedAlgorithmException; -import gov.loc.repository.bagit.exceptions.conformance.BagitVersionIsNotAcceptableException; -import gov.loc.repository.bagit.exceptions.conformance.FetchFileNotAllowedException; -import gov.loc.repository.bagit.exceptions.conformance.MetatdataValueIsNotAcceptableException; -import gov.loc.repository.bagit.exceptions.conformance.MetatdataValueIsNotRepeatableException; -import gov.loc.repository.bagit.exceptions.conformance.RequiredManifestNotPresentException; -import gov.loc.repository.bagit.exceptions.conformance.RequiredMetadataFieldNotPresentException; -import gov.loc.repository.bagit.exceptions.conformance.RequiredTagFileNotPresentException; -import gov.loc.repository.bagit.reader.BagitTextFileReader; -import gov.loc.repository.bagit.reader.KeyValueReader; -import gov.loc.repository.bagit.verify.BagVerifier; +import nl.knaw.dans.bagit.domain.Version; +import nl.knaw.dans.bagit.exceptions.conformance.BagitVersionIsNotAcceptableException; +import nl.knaw.dans.bagit.exceptions.conformance.FetchFileNotAllowedException; +import nl.knaw.dans.bagit.exceptions.conformance.MetatdataValueIsNotAcceptableException; +import nl.knaw.dans.bagit.exceptions.conformance.MetatdataValueIsNotRepeatableException; +import nl.knaw.dans.bagit.exceptions.conformance.RequiredManifestNotPresentException; +import nl.knaw.dans.bagit.exceptions.conformance.RequiredMetadataFieldNotPresentException; +import nl.knaw.dans.bagit.exceptions.conformance.RequiredTagFileNotPresentException; /** * Responsible for checking a bag and providing insight into how it cause problems. @@ -73,7 +88,7 @@ private BagLinter(){ * @throws BagitVersionIsNotAcceptableException if the version of the bag is not in the list of acceptable versions * @throws RequiredTagFileNotPresentException if a tag file is not present but should be */ - public static void checkAgainstProfile(final InputStream jsonProfile, final Bag bag) throws JsonParseException, JsonMappingException, + public static void checkAgainstProfile(final InputStream jsonProfile, final Bag bag) throws JsonParseException, JsonMappingException, IOException, FetchFileNotAllowedException, RequiredMetadataFieldNotPresentException, MetatdataValueIsNotAcceptableException, RequiredManifestNotPresentException, BagitVersionIsNotAcceptableException, RequiredTagFileNotPresentException, MetatdataValueIsNotRepeatableException{ BagProfileChecker.bagConformsToProfile(jsonProfile, bag); diff --git a/src/main/java/gov/loc/repository/bagit/conformance/BagProfileChecker.java b/src/main/java/nl/knaw/dans/bagit/conformance/BagProfileChecker.java similarity index 83% rename from src/main/java/gov/loc/repository/bagit/conformance/BagProfileChecker.java rename to src/main/java/nl/knaw/dans/bagit/conformance/BagProfileChecker.java index bec5709db..85f08885d 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/BagProfileChecker.java +++ b/src/main/java/nl/knaw/dans/bagit/conformance/BagProfileChecker.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.io.IOException; import java.io.InputStream; @@ -11,6 +26,12 @@ import java.util.ResourceBundle; import java.util.Set; +import nl.knaw.dans.bagit.conformance.profile.BagInfoRequirement; +import nl.knaw.dans.bagit.conformance.profile.BagitProfile; +import nl.knaw.dans.bagit.conformance.profile.BagitProfileDeserializer; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.FetchItem; +import nl.knaw.dans.bagit.domain.Manifest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; @@ -20,20 +41,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import gov.loc.repository.bagit.conformance.profile.BagInfoRequirement; -import gov.loc.repository.bagit.conformance.profile.BagitProfile; -import gov.loc.repository.bagit.conformance.profile.BagitProfileDeserializer; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.FetchItem; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.domain.Metadata; -import gov.loc.repository.bagit.exceptions.conformance.BagitVersionIsNotAcceptableException; -import gov.loc.repository.bagit.exceptions.conformance.FetchFileNotAllowedException; -import gov.loc.repository.bagit.exceptions.conformance.MetatdataValueIsNotAcceptableException; -import gov.loc.repository.bagit.exceptions.conformance.MetatdataValueIsNotRepeatableException; -import gov.loc.repository.bagit.exceptions.conformance.RequiredManifestNotPresentException; -import gov.loc.repository.bagit.exceptions.conformance.RequiredMetadataFieldNotPresentException; -import gov.loc.repository.bagit.exceptions.conformance.RequiredTagFileNotPresentException; +import nl.knaw.dans.bagit.domain.Metadata; +import nl.knaw.dans.bagit.exceptions.conformance.BagitVersionIsNotAcceptableException; +import nl.knaw.dans.bagit.exceptions.conformance.FetchFileNotAllowedException; +import nl.knaw.dans.bagit.exceptions.conformance.MetatdataValueIsNotAcceptableException; +import nl.knaw.dans.bagit.exceptions.conformance.MetatdataValueIsNotRepeatableException; +import nl.knaw.dans.bagit.exceptions.conformance.RequiredManifestNotPresentException; +import nl.knaw.dans.bagit.exceptions.conformance.RequiredMetadataFieldNotPresentException; +import nl.knaw.dans.bagit.exceptions.conformance.RequiredTagFileNotPresentException; /** * Responsible for checking a bag against a profile @@ -66,7 +81,7 @@ private BagProfileChecker(){ * @throws BagitVersionIsNotAcceptableException if the version of the bag is not in the list of acceptable versions * @throws RequiredTagFileNotPresentException if a tag file is not present but should be */ - public static void bagConformsToProfile(final InputStream jsonProfile, final Bag bag) throws JsonParseException, JsonMappingException, + public static void bagConformsToProfile(final InputStream jsonProfile, final Bag bag) throws JsonParseException, JsonMappingException, IOException, FetchFileNotAllowedException, RequiredMetadataFieldNotPresentException, MetatdataValueIsNotAcceptableException, RequiredManifestNotPresentException, BagitVersionIsNotAcceptableException, RequiredTagFileNotPresentException, MetatdataValueIsNotRepeatableException{ @@ -102,7 +117,7 @@ private static void checkFetch(final Path rootDir, final boolean allowFetchFile, } } - private static void checkMetadata(final Metadata bagMetadata, final Map bagInfoEntryRequirements) + private static void checkMetadata(final Metadata bagMetadata, final Map bagInfoEntryRequirements) throws RequiredMetadataFieldNotPresentException, MetatdataValueIsNotAcceptableException, MetatdataValueIsNotRepeatableException{ for(final Entry bagInfoEntryRequirement : bagInfoEntryRequirements.entrySet()){ diff --git a/src/main/java/gov/loc/repository/bagit/conformance/BagitWarning.java b/src/main/java/nl/knaw/dans/bagit/conformance/BagitWarning.java similarity index 66% rename from src/main/java/gov/loc/repository/bagit/conformance/BagitWarning.java rename to src/main/java/nl/knaw/dans/bagit/conformance/BagitWarning.java index f68332a1a..3fef9407a 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/BagitWarning.java +++ b/src/main/java/nl/knaw/dans/bagit/conformance/BagitWarning.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.util.ResourceBundle; diff --git a/src/main/java/gov/loc/repository/bagit/conformance/EncodingChecker.java b/src/main/java/nl/knaw/dans/bagit/conformance/EncodingChecker.java similarity index 59% rename from src/main/java/gov/loc/repository/bagit/conformance/EncodingChecker.java rename to src/main/java/nl/knaw/dans/bagit/conformance/EncodingChecker.java index b878ad6d0..7a81cd3be 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/EncodingChecker.java +++ b/src/main/java/nl/knaw/dans/bagit/conformance/EncodingChecker.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/gov/loc/repository/bagit/conformance/ManifestChecker.java b/src/main/java/nl/knaw/dans/bagit/conformance/ManifestChecker.java similarity index 92% rename from src/main/java/gov/loc/repository/bagit/conformance/ManifestChecker.java rename to src/main/java/nl/knaw/dans/bagit/conformance/ManifestChecker.java index b7247dc4e..a70e7294d 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/ManifestChecker.java +++ b/src/main/java/nl/knaw/dans/bagit/conformance/ManifestChecker.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.io.BufferedReader; import java.io.IOException; @@ -14,18 +29,18 @@ import java.util.ResourceBundle; import java.util.Set; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; +import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; +import nl.knaw.dans.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.reader.ManifestReader; +import nl.knaw.dans.bagit.util.PathUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; -import gov.loc.repository.bagit.exceptions.UnsupportedAlgorithmException; -import gov.loc.repository.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; -import gov.loc.repository.bagit.reader.ManifestReader; -import gov.loc.repository.bagit.util.PathUtils; +import nl.knaw.dans.bagit.domain.Version; /** * Part of the BagIt conformance suite. diff --git a/src/main/java/gov/loc/repository/bagit/conformance/MetadataChecker.java b/src/main/java/nl/knaw/dans/bagit/conformance/MetadataChecker.java similarity index 69% rename from src/main/java/gov/loc/repository/bagit/conformance/MetadataChecker.java rename to src/main/java/nl/knaw/dans/bagit/conformance/MetadataChecker.java index 912287fb1..0d02214e5 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/MetadataChecker.java +++ b/src/main/java/nl/knaw/dans/bagit/conformance/MetadataChecker.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.io.IOException; import java.nio.charset.Charset; @@ -9,12 +24,11 @@ import java.util.ResourceBundle; import java.util.Set; +import nl.knaw.dans.bagit.exceptions.InvalidBagMetadataException; +import nl.knaw.dans.bagit.reader.MetadataReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.exceptions.InvalidBagMetadataException; -import gov.loc.repository.bagit.reader.MetadataReader; - /** * Part of the BagIt conformance suite. * This checker checks the bag metadata (bag-info.txt) for various problems. @@ -28,7 +42,7 @@ private MetadataChecker(){ } public static void checkBagMetadata(final Path bagitDir, final Charset encoding, final Set warnings, - final Collection warningsToIgnore) throws IOException, InvalidBagMetadataException{ + final Collection warningsToIgnore) throws IOException, InvalidBagMetadataException { checkForPayloadOxumMetadata(bagitDir, encoding, warnings, warningsToIgnore); } diff --git a/src/main/java/gov/loc/repository/bagit/conformance/VersionChecker.java b/src/main/java/nl/knaw/dans/bagit/conformance/VersionChecker.java similarity index 57% rename from src/main/java/gov/loc/repository/bagit/conformance/VersionChecker.java rename to src/main/java/nl/knaw/dans/bagit/conformance/VersionChecker.java index df8d1ab29..a399d0408 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/VersionChecker.java +++ b/src/main/java/nl/knaw/dans/bagit/conformance/VersionChecker.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.util.Collection; import java.util.ResourceBundle; @@ -7,7 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.Version; +import nl.knaw.dans.bagit.domain.Version; /** * Part of the BagIt conformance suite. diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java b/src/main/java/nl/knaw/dans/bagit/conformance/profile/BagInfoRequirement.java similarity index 77% rename from src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java rename to src/main/java/nl/knaw/dans/bagit/conformance/profile/BagInfoRequirement.java index 25da68ca6..145b6f329 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirement.java +++ b/src/main/java/nl/knaw/dans/bagit/conformance/profile/BagInfoRequirement.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance.profile; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance.profile; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfile.java b/src/main/java/nl/knaw/dans/bagit/conformance/profile/BagitProfile.java similarity index 90% rename from src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfile.java rename to src/main/java/nl/knaw/dans/bagit/conformance/profile/BagitProfile.java index d7eb29933..2eef5ef08 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfile.java +++ b/src/main/java/nl/knaw/dans/bagit/conformance/profile/BagitProfile.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance.profile; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance.profile; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java b/src/main/java/nl/knaw/dans/bagit/conformance/profile/BagitProfileDeserializer.java similarity index 90% rename from src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java rename to src/main/java/nl/knaw/dans/bagit/conformance/profile/BagitProfileDeserializer.java index 3d48bec68..aced4aa15 100644 --- a/src/main/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializer.java +++ b/src/main/java/nl/knaw/dans/bagit/conformance/profile/BagitProfileDeserializer.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance.profile; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance.profile; import java.io.IOException; import java.util.ArrayList; @@ -73,8 +88,7 @@ private static void parseBagitProfileInfo(final JsonNode node, final BagitProfil } /** - * Parse required tags due to specification defined at - * {@link https://github.com/bagit-profiles/bagit-profiles} + * Parse required tags due to specification defined at bagit profiles * Note: If one of the tags is missing, a NullPointerException is thrown. * * @param bagitProfileInfoNode Root node of the bagit profile info section. @@ -101,8 +115,7 @@ private static void parseMandatoryTagsOfBagitProfileInfo(final JsonNode bagitPro } /** - * Parse optional tags due to specification defined at - * {@link https://github.com/bagit-profiles/bagit-profiles} + * Parse optional tags due to specification defined at bagit profiles * * @param bagitProfileInfoNode Root node of the bagit profile info section. * @param profile Representation of bagit profile . diff --git a/src/main/java/nl/knaw/dans/bagit/conformance/profile/Serialization.java b/src/main/java/nl/knaw/dans/bagit/conformance/profile/Serialization.java new file mode 100644 index 000000000..55be0a147 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/conformance/profile/Serialization.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance.profile; + +/** + * The type of serialization required by a {@link BagitProfile} + */ +public enum Serialization { + forbidden, + required, + optional; +} diff --git a/src/main/java/gov/loc/repository/bagit/creator/AbstractCreateManifestsVistor.java b/src/main/java/nl/knaw/dans/bagit/creator/AbstractCreateManifestsVistor.java similarity index 72% rename from src/main/java/gov/loc/repository/bagit/creator/AbstractCreateManifestsVistor.java rename to src/main/java/nl/knaw/dans/bagit/creator/AbstractCreateManifestsVistor.java index d37fd2c25..086c0914d 100644 --- a/src/main/java/gov/loc/repository/bagit/creator/AbstractCreateManifestsVistor.java +++ b/src/main/java/nl/knaw/dans/bagit/creator/AbstractCreateManifestsVistor.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.creator; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.creator; import java.io.IOException; import java.nio.file.FileVisitResult; @@ -10,13 +25,12 @@ import java.util.Map; import java.util.ResourceBundle; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.hash.Hasher; +import nl.knaw.dans.bagit.util.PathUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.hash.Hasher; -import gov.loc.repository.bagit.util.PathUtils; - /** * An implementation of the {@link SimpleFileVisitor} class that optionally avoids hidden files. * Mainly used in {@link BagCreator} diff --git a/src/main/java/gov/loc/repository/bagit/creator/BagCreator.java b/src/main/java/nl/knaw/dans/bagit/creator/BagCreator.java similarity index 89% rename from src/main/java/gov/loc/repository/bagit/creator/BagCreator.java rename to src/main/java/nl/knaw/dans/bagit/creator/BagCreator.java index e447fbbc2..2a421cbeb 100644 --- a/src/main/java/gov/loc/repository/bagit/creator/BagCreator.java +++ b/src/main/java/nl/knaw/dans/bagit/creator/BagCreator.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.creator; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.creator; import java.io.IOException; import java.nio.file.DirectoryStream; @@ -13,20 +28,20 @@ import java.util.Map; import java.util.ResourceBundle; +import nl.knaw.dans.bagit.annotation.Incubating; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.hash.Hasher; +import nl.knaw.dans.bagit.hash.SupportedAlgorithm; +import nl.knaw.dans.bagit.util.PathUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.annotation.Incubating; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.domain.Metadata; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.hash.Hasher; -import gov.loc.repository.bagit.hash.SupportedAlgorithm; -import gov.loc.repository.bagit.util.PathUtils; -import gov.loc.repository.bagit.writer.BagitFileWriter; -import gov.loc.repository.bagit.writer.ManifestWriter; -import gov.loc.repository.bagit.writer.MetadataWriter; +import nl.knaw.dans.bagit.domain.Metadata; +import nl.knaw.dans.bagit.domain.Version; +import nl.knaw.dans.bagit.writer.BagitFileWriter; +import nl.knaw.dans.bagit.writer.ManifestWriter; +import nl.knaw.dans.bagit.writer.MetadataWriter; /** * Responsible for creating a bag in place. diff --git a/src/main/java/gov/loc/repository/bagit/creator/CreatePayloadManifestsVistor.java b/src/main/java/nl/knaw/dans/bagit/creator/CreatePayloadManifestsVistor.java similarity index 52% rename from src/main/java/gov/loc/repository/bagit/creator/CreatePayloadManifestsVistor.java rename to src/main/java/nl/knaw/dans/bagit/creator/CreatePayloadManifestsVistor.java index 2780418a5..cecd44145 100644 --- a/src/main/java/gov/loc/repository/bagit/creator/CreatePayloadManifestsVistor.java +++ b/src/main/java/nl/knaw/dans/bagit/creator/CreatePayloadManifestsVistor.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.creator; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.creator; import java.io.IOException; import java.nio.file.FileVisitResult; @@ -7,7 +22,7 @@ import java.security.MessageDigest; import java.util.Map; -import gov.loc.repository.bagit.domain.Manifest; +import nl.knaw.dans.bagit.domain.Manifest; /** * Creates the payload manifests by walking the payload files and calculating their checksums diff --git a/src/main/java/gov/loc/repository/bagit/creator/CreateTagManifestsVistor.java b/src/main/java/nl/knaw/dans/bagit/creator/CreateTagManifestsVistor.java similarity index 52% rename from src/main/java/gov/loc/repository/bagit/creator/CreateTagManifestsVistor.java rename to src/main/java/nl/knaw/dans/bagit/creator/CreateTagManifestsVistor.java index 8d96aeb7b..f37a00769 100644 --- a/src/main/java/gov/loc/repository/bagit/creator/CreateTagManifestsVistor.java +++ b/src/main/java/nl/knaw/dans/bagit/creator/CreateTagManifestsVistor.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.creator; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.creator; import java.io.IOException; import java.nio.file.FileVisitResult; @@ -7,7 +22,7 @@ import java.security.MessageDigest; import java.util.Map; -import gov.loc.repository.bagit.domain.Manifest; +import nl.knaw.dans.bagit.domain.Manifest; /** * Creates the tag manifests by walking the tag files and calculating their checksums diff --git a/src/main/java/gov/loc/repository/bagit/domain/Bag.java b/src/main/java/nl/knaw/dans/bagit/domain/Bag.java similarity index 86% rename from src/main/java/gov/loc/repository/bagit/domain/Bag.java rename to src/main/java/nl/knaw/dans/bagit/domain/Bag.java index 9c7185cb3..b3bb2a105 100644 --- a/src/main/java/gov/loc/repository/bagit/domain/Bag.java +++ b/src/main/java/nl/knaw/dans/bagit/domain/Bag.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.domain; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.domain; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; diff --git a/src/main/java/gov/loc/repository/bagit/domain/FetchItem.java b/src/main/java/nl/knaw/dans/bagit/domain/FetchItem.java similarity index 75% rename from src/main/java/gov/loc/repository/bagit/domain/FetchItem.java rename to src/main/java/nl/knaw/dans/bagit/domain/FetchItem.java index 9eddb2acc..9b5cfab91 100644 --- a/src/main/java/gov/loc/repository/bagit/domain/FetchItem.java +++ b/src/main/java/nl/knaw/dans/bagit/domain/FetchItem.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.domain; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.domain; import java.net.URL; import java.nio.file.Path; diff --git a/src/main/java/gov/loc/repository/bagit/domain/Manifest.java b/src/main/java/nl/knaw/dans/bagit/domain/Manifest.java similarity index 65% rename from src/main/java/gov/loc/repository/bagit/domain/Manifest.java rename to src/main/java/nl/knaw/dans/bagit/domain/Manifest.java index 0eeb4e2a2..05e3bd956 100644 --- a/src/main/java/gov/loc/repository/bagit/domain/Manifest.java +++ b/src/main/java/nl/knaw/dans/bagit/domain/Manifest.java @@ -1,12 +1,26 @@ - -package gov.loc.repository.bagit.domain; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.domain; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import java.util.Objects; -import gov.loc.repository.bagit.hash.SupportedAlgorithm; +import nl.knaw.dans.bagit.hash.SupportedAlgorithm; /** * A manifest is a list of files and their corresponding checksum with the algorithm used to generate that checksum diff --git a/src/main/java/gov/loc/repository/bagit/domain/Metadata.java b/src/main/java/nl/knaw/dans/bagit/domain/Metadata.java similarity index 83% rename from src/main/java/gov/loc/repository/bagit/domain/Metadata.java rename to src/main/java/nl/knaw/dans/bagit/domain/Metadata.java index bcafa9c72..2950fbcec 100644 --- a/src/main/java/gov/loc/repository/bagit/domain/Metadata.java +++ b/src/main/java/nl/knaw/dans/bagit/domain/Metadata.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.domain; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.domain; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; @@ -63,7 +78,7 @@ public List get(final String key){ * @param key the label * @param value the value of the label * - * @return true (as specified by {@link Collection#add}) + * @return true (as specified by {@link Collection#add}) */ public boolean add(final String key, final String value){ if(PAYLOAD_OXUM.equalsIgnoreCase(key)){ @@ -121,7 +136,7 @@ public void addAll(final List> data){ * payload oxum is a special case where it makes no sense to have multiple values so instead of just appending we upsert (insert or update) * @param payloadOxumValue the value payload-oxum should be set to * - * @return true (as specified by {@link Collection#add}) + * @return true (as specified by {@link Collection#add}) */ public boolean upsertPayloadOxum(final String payloadOxumValue){ map.remove(PAYLOAD_OXUM.toUpperCase()); diff --git a/src/main/java/gov/loc/repository/bagit/domain/Version.java b/src/main/java/nl/knaw/dans/bagit/domain/Version.java similarity index 73% rename from src/main/java/gov/loc/repository/bagit/domain/Version.java rename to src/main/java/nl/knaw/dans/bagit/domain/Version.java index b332ddc25..f890cb9a6 100644 --- a/src/main/java/gov/loc/repository/bagit/domain/Version.java +++ b/src/main/java/nl/knaw/dans/bagit/domain/Version.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.domain; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.domain; import java.util.Objects; diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/CorruptChecksumException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/CorruptChecksumException.java new file mode 100644 index 000000000..e0c767254 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/CorruptChecksumException.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +import java.nio.file.Path; + +import org.slf4j.helpers.MessageFormatter; + +/** + * Class to represent an error when the calculated checksum is different than the manifest specified checksum. + */ +public class CorruptChecksumException extends Exception { + private static final long serialVersionUID = 1L; + + public CorruptChecksumException(final String message, final Path path, final String algorithm, final String hash, final String computedHash){ + super(MessageFormatter.arrayFormat(message, new Object[]{path, algorithm, hash, computedHash}).getMessage()); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/FileNotInManifestException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/FileNotInManifestException.java new file mode 100644 index 000000000..2825c0479 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/FileNotInManifestException.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +import java.io.IOException; + +/** + * Class to represent an error when a file is found in the payload directory but not in any manifest. + * Opposite to {@link FileNotInPayloadDirectoryException} + */ +public class FileNotInManifestException extends IOException { + private static final long serialVersionUID = 1L; + + public FileNotInManifestException(final String message){ + super(message); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/FileNotInPayloadDirectoryException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/FileNotInPayloadDirectoryException.java new file mode 100644 index 000000000..0e9a6a26e --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/FileNotInPayloadDirectoryException.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +/** + * Class to represent an error when a file is not in the payload directory but is listed in a manifest. + * Opposite to {@link FileNotInManifestException} + */ +public class FileNotInPayloadDirectoryException extends Exception { + private static final long serialVersionUID = 1L; + + public FileNotInPayloadDirectoryException(final String message){ + super(message); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/InvalidBagMetadataException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/InvalidBagMetadataException.java new file mode 100644 index 000000000..1ec436d1d --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/InvalidBagMetadataException.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +/** + * Class to represent an error when the bag metadata file does not conform to the bagit spec, + * namely:
+ * <KEY>:<VALUE> + *
or + *
<KEY>:<VALUE>
+ *    <VALUE CONTINUED>
+ */ +public class InvalidBagMetadataException extends InvalidBagitFileFormatException { + private static final long serialVersionUID = 1L; + + public InvalidBagMetadataException(final String message){ + super(message); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/InvalidBagitFileFormatException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/InvalidBagitFileFormatException.java new file mode 100644 index 000000000..c9df83dd8 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/InvalidBagitFileFormatException.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +/** + * Class to represent an error when a specific bag file does not conform to its bagit specfication format + */ +public class InvalidBagitFileFormatException extends Exception { + private static final long serialVersionUID = 1L; + + public InvalidBagitFileFormatException(final String message){ + super(message); + } + + public InvalidBagitFileFormatException(final String message, final Exception e){ + super(message, e); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/InvalidPayloadOxumException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/InvalidPayloadOxumException.java new file mode 100644 index 000000000..686843195 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/InvalidPayloadOxumException.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +/** + * Class to represent an error when the calculated total bytes or number of files for + * the payload-oxum is different than the supplied values. + */ +public class InvalidPayloadOxumException extends Exception { + private static final long serialVersionUID = 1L; + + public InvalidPayloadOxumException(final String message){ + super(message); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/MaliciousPathException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/MaliciousPathException.java new file mode 100644 index 000000000..720de9866 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/MaliciousPathException.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +/** + * Class to represent an error when the path in a manifest or fetch file has been crafted to point to a file or + * directory outside the bag. Most likely to try and overwrite an important system file. + */ +public class MaliciousPathException extends Exception { + private static final long serialVersionUID = 1L; + + public MaliciousPathException(final String message){ + super(message); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/MissingBagitFileException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/MissingBagitFileException.java new file mode 100644 index 000000000..d4808e24c --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/MissingBagitFileException.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +/** + * The bagit.txt file is a required file. This class represents the error if that file is not present. + */ +public class MissingBagitFileException extends Exception { + private static final long serialVersionUID = 1L; + + public MissingBagitFileException(final String message){ + super(message); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/MissingPayloadDirectoryException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/MissingPayloadDirectoryException.java new file mode 100644 index 000000000..4b4090e18 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/MissingPayloadDirectoryException.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +import java.nio.file.Path; + +import org.slf4j.helpers.MessageFormatter; + +/** + * The payload directory is a required file. This class represents the error if it is not found. + */ +public class MissingPayloadDirectoryException extends Exception { + private static final long serialVersionUID = 1L; + + public MissingPayloadDirectoryException(final String message, final Path path){ + super(MessageFormatter.format(message, path).getMessage()); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/MissingPayloadManifestException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/MissingPayloadManifestException.java new file mode 100644 index 000000000..03d04ef8e --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/MissingPayloadManifestException.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +/** + * A bagit bag needs at least one payload manifest. This class represents the error if at least one payload manifest isn't found. + */ +public class MissingPayloadManifestException extends Exception { + private static final long serialVersionUID = 1L; + + public MissingPayloadManifestException(final String message){ + super(message); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/PayloadOxumDoesNotExistException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/PayloadOxumDoesNotExistException.java new file mode 100644 index 000000000..e2b34be4e --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/PayloadOxumDoesNotExistException.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +import nl.knaw.dans.bagit.domain.Bag; + +/** + * The {@link Bag} object should contain the Payload-Oxum metatdata key value pair, + * this class represents the error when trying to calculate the payload-oxum and it doesn't exist on the bag object. + */ +public class PayloadOxumDoesNotExistException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public PayloadOxumDoesNotExistException(final String message){ + super(message); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/UnparsableVersionException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/UnparsableVersionException.java new file mode 100644 index 000000000..cc9f588ec --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/UnparsableVersionException.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +import org.slf4j.helpers.MessageFormatter; + +/** + * If the version string in the bagit.txt file was not in the form <MAJOR>.<MINOR> + */ +public class UnparsableVersionException extends Exception { + private static final long serialVersionUID = 1L; + + public UnparsableVersionException(final String message, final String version){ + super(MessageFormatter.format(message, version).getMessage()); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/UnsupportedAlgorithmException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/UnsupportedAlgorithmException.java new file mode 100644 index 000000000..ea3865723 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/UnsupportedAlgorithmException.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +import java.security.MessageDigest; + +import org.slf4j.helpers.MessageFormatter; + +/** + * When the bag uses an checksum algorithm that is not supported by {@link MessageDigest}. + */ +public class UnsupportedAlgorithmException extends Exception { + private static final long serialVersionUID = 1L; + + public UnsupportedAlgorithmException(final String message, final String bagitAlgorithmName, final Throwable cause) { + super(MessageFormatter.format(message, bagitAlgorithmName).getMessage(), cause); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/VerificationException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/VerificationException.java new file mode 100644 index 000000000..52df6e1ce --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/VerificationException.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions; + +/** + * Class to represent an generic exception that happened during verification. + */ +public class VerificationException extends Exception { + private static final long serialVersionUID = 1L; + + public VerificationException(final Exception exception){ + super(exception); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/BagitVersionIsNotAcceptableException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/BagitVersionIsNotAcceptableException.java new file mode 100644 index 000000000..7e6e2ea39 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/BagitVersionIsNotAcceptableException.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions.conformance; + +import java.util.List; + +import nl.knaw.dans.bagit.domain.Version; +import org.slf4j.helpers.MessageFormatter; + +/** + * Class to represent when the bag's version is not in the acceptable list of versions + */ +public class BagitVersionIsNotAcceptableException extends Exception { +private static final long serialVersionUID = 1L; + + public BagitVersionIsNotAcceptableException(final String message, final Version version, final List acceptableVersions) { + super(MessageFormatter.format(message, version, acceptableVersions).getMessage()); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/FetchFileNotAllowedException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/FetchFileNotAllowedException.java new file mode 100644 index 000000000..c73755afe --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/FetchFileNotAllowedException.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions.conformance; + +import java.nio.file.Path; + +import org.slf4j.helpers.MessageFormatter; + +/** + * Class to represent when a fetch file is found in a bag but is not allowed according to the bagit profile + */ +public class FetchFileNotAllowedException extends Exception { +private static final long serialVersionUID = 1L; + + public FetchFileNotAllowedException(final String message, final Path rootDir) { + super(MessageFormatter.format(message, rootDir).getMessage()); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/MetatdataValueIsNotAcceptableException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/MetatdataValueIsNotAcceptableException.java new file mode 100644 index 000000000..cdb2847c7 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/MetatdataValueIsNotAcceptableException.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions.conformance; + +import java.util.List; + +import org.slf4j.helpers.MessageFormatter; + +/** + * Class to represent when a metadata's value is not in the acceptable list of values + */ +public class MetatdataValueIsNotAcceptableException extends Exception { +private static final long serialVersionUID = 1L; + + public MetatdataValueIsNotAcceptableException(final String message, final String metadataKey, final List acceptableValues, final String actualValue) { + super(MessageFormatter.arrayFormat(message, new Object[]{metadataKey, acceptableValues, actualValue}).getMessage()); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/MetatdataValueIsNotRepeatableException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/MetatdataValueIsNotRepeatableException.java new file mode 100644 index 000000000..e68908850 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/MetatdataValueIsNotRepeatableException.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions.conformance; + +import org.slf4j.helpers.MessageFormatter; + +/** + * Class to represent when a metadata's value is not to be repeated + */ +public class MetatdataValueIsNotRepeatableException extends Exception { +private static final long serialVersionUID = 1L; + + public MetatdataValueIsNotRepeatableException(final String message, final String metadataKey) { + super(MessageFormatter.format(message, metadataKey).getMessage()); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredManifestNotPresentException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredManifestNotPresentException.java new file mode 100644 index 000000000..28fb7b7cf --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredManifestNotPresentException.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions.conformance; + +/** + * Class to represent when a specific manifest type is not found, such as md5, sha1, etc (payload or tag) + */ +public class RequiredManifestNotPresentException extends Exception { +private static final long serialVersionUID = 1L; + + public RequiredManifestNotPresentException(final String message) { + super(message); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredMetadataFieldNotPresentException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredMetadataFieldNotPresentException.java new file mode 100644 index 000000000..7b6f5aa51 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredMetadataFieldNotPresentException.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions.conformance; + +import org.slf4j.helpers.MessageFormatter; + +/** + * Class to represent when a specific metadata field is not found + */ +public class RequiredMetadataFieldNotPresentException extends Exception { +private static final long serialVersionUID = 1L; + + public RequiredMetadataFieldNotPresentException(final String message, final String bagInfoEntryRequirementKey) { + super(MessageFormatter.format(message, bagInfoEntryRequirementKey).getMessage()); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredTagFileNotPresentException.java b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredTagFileNotPresentException.java new file mode 100644 index 000000000..fd72310c3 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/exceptions/conformance/RequiredTagFileNotPresentException.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.exceptions.conformance; + +import org.slf4j.helpers.MessageFormatter; + +/** + * Class to represent when a specific tag file is not found + */ +public class RequiredTagFileNotPresentException extends Exception { +private static final long serialVersionUID = 1L; + + public RequiredTagFileNotPresentException(final String message, final String requiredTagFilePath) { + super(MessageFormatter.format(message, requiredTagFilePath).getMessage()); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/hash/BagitAlgorithmNameToSupportedAlgorithmMapping.java b/src/main/java/nl/knaw/dans/bagit/hash/BagitAlgorithmNameToSupportedAlgorithmMapping.java new file mode 100644 index 000000000..2772fc929 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/hash/BagitAlgorithmNameToSupportedAlgorithmMapping.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.hash; + +import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; + +/** + * Implement this interface if you need to be able to use other algorithms than the {@link StandardSupportedAlgorithms} + */ +public interface BagitAlgorithmNameToSupportedAlgorithmMapping { + SupportedAlgorithm getSupportedAlgorithm(String bagitAlgorithmName) throws UnsupportedAlgorithmException; +} diff --git a/src/main/java/gov/loc/repository/bagit/hash/Hasher.java b/src/main/java/nl/knaw/dans/bagit/hash/Hasher.java similarity index 85% rename from src/main/java/gov/loc/repository/bagit/hash/Hasher.java rename to src/main/java/nl/knaw/dans/bagit/hash/Hasher.java index 55816ac52..407008d21 100644 --- a/src/main/java/gov/loc/repository/bagit/hash/Hasher.java +++ b/src/main/java/nl/knaw/dans/bagit/hash/Hasher.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.hash; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.hash; import java.io.BufferedInputStream; import java.io.IOException; @@ -16,11 +31,10 @@ import java.util.ResourceBundle; import java.util.Map.Entry; +import nl.knaw.dans.bagit.domain.Manifest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.Manifest; - /** * Convenience class for generating a HEX formatted string of the checksum hash. */ diff --git a/src/main/java/gov/loc/repository/bagit/hash/StandardBagitAlgorithmNameToSupportedAlgorithmMapping.java b/src/main/java/nl/knaw/dans/bagit/hash/StandardBagitAlgorithmNameToSupportedAlgorithmMapping.java similarity index 51% rename from src/main/java/gov/loc/repository/bagit/hash/StandardBagitAlgorithmNameToSupportedAlgorithmMapping.java rename to src/main/java/nl/knaw/dans/bagit/hash/StandardBagitAlgorithmNameToSupportedAlgorithmMapping.java index d35e71cb2..c3e5aa63b 100644 --- a/src/main/java/gov/loc/repository/bagit/hash/StandardBagitAlgorithmNameToSupportedAlgorithmMapping.java +++ b/src/main/java/nl/knaw/dans/bagit/hash/StandardBagitAlgorithmNameToSupportedAlgorithmMapping.java @@ -1,9 +1,24 @@ -package gov.loc.repository.bagit.hash; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.hash; import java.util.Locale; import java.util.ResourceBundle; -import gov.loc.repository.bagit.exceptions.UnsupportedAlgorithmException; +import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; /** * Provides a mapping between bagit algorithm names and {@link SupportedAlgorithm} diff --git a/src/main/java/nl/knaw/dans/bagit/hash/StandardSupportedAlgorithms.java b/src/main/java/nl/knaw/dans/bagit/hash/StandardSupportedAlgorithms.java new file mode 100644 index 000000000..b7ee4647d --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/hash/StandardSupportedAlgorithms.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.hash; + +/** + * The standard algorithms that are supported "out of the box" in bagit + */ +public enum StandardSupportedAlgorithms implements SupportedAlgorithm{ + MD5("MD5"), + SHA1("SHA-1"), + SHA224("SHA-224"), + SHA256("SHA-256"), + SHA512("SHA-512"); + + private final String messageDigestName; + + private StandardSupportedAlgorithms(final String messageDigestName){ + this.messageDigestName = messageDigestName; + } + + @Override + public String getMessageDigestName() { + return messageDigestName; + } + + @SuppressWarnings({"PMD.UseLocaleWithCaseConversions"}) + @Override + public String getBagitName() { + return name().toLowerCase(); + } +} diff --git a/src/main/java/nl/knaw/dans/bagit/hash/SupportedAlgorithm.java b/src/main/java/nl/knaw/dans/bagit/hash/SupportedAlgorithm.java new file mode 100644 index 000000000..c6eb96e11 --- /dev/null +++ b/src/main/java/nl/knaw/dans/bagit/hash/SupportedAlgorithm.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.hash; + +import java.security.MessageDigest; + +/** + * Easy way to convert between bagit manifest spec and {@link MessageDigest}
+ * See {@link StandardSupportedAlgorithms} for a list of defaults + */ +public interface SupportedAlgorithm { + String getMessageDigestName(); + String getBagitName(); +} diff --git a/src/main/java/gov/loc/repository/bagit/reader/BagReader.java b/src/main/java/nl/knaw/dans/bagit/reader/BagReader.java similarity index 67% rename from src/main/java/gov/loc/repository/bagit/reader/BagReader.java rename to src/main/java/nl/knaw/dans/bagit/reader/BagReader.java index c92844d02..914906840 100644 --- a/src/main/java/gov/loc/repository/bagit/reader/BagReader.java +++ b/src/main/java/nl/knaw/dans/bagit/reader/BagReader.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.io.IOException; import java.nio.charset.Charset; @@ -6,15 +21,15 @@ import java.nio.file.Path; import java.util.AbstractMap.SimpleImmutableEntry; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.exceptions.InvalidBagMetadataException; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; -import gov.loc.repository.bagit.exceptions.UnparsableVersionException; -import gov.loc.repository.bagit.exceptions.UnsupportedAlgorithmException; -import gov.loc.repository.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; -import gov.loc.repository.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.Version; +import nl.knaw.dans.bagit.exceptions.InvalidBagMetadataException; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; +import nl.knaw.dans.bagit.exceptions.UnparsableVersionException; +import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; +import nl.knaw.dans.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; /** * Responsible for reading a bag from the filesystem. diff --git a/src/main/java/gov/loc/repository/bagit/reader/BagitTextFileReader.java b/src/main/java/nl/knaw/dans/bagit/reader/BagitTextFileReader.java similarity index 85% rename from src/main/java/gov/loc/repository/bagit/reader/BagitTextFileReader.java rename to src/main/java/nl/knaw/dans/bagit/reader/BagitTextFileReader.java index 8f8be00fa..b2fb7e882 100644 --- a/src/main/java/gov/loc/repository/bagit/reader/BagitTextFileReader.java +++ b/src/main/java/nl/knaw/dans/bagit/reader/BagitTextFileReader.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.io.IOException; import java.nio.charset.Charset; @@ -10,14 +25,14 @@ import java.util.AbstractMap.SimpleImmutableEntry; import java.util.Arrays; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.exceptions.InvalidBagMetadataException; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.UnparsableVersionException; +import nl.knaw.dans.bagit.domain.Version; +import nl.knaw.dans.bagit.exceptions.InvalidBagMetadataException; +import nl.knaw.dans.bagit.exceptions.UnparsableVersionException; /** * This class is responsible for reading and parsing bagit.txt files from the filesystem diff --git a/src/main/java/gov/loc/repository/bagit/reader/FetchReader.java b/src/main/java/nl/knaw/dans/bagit/reader/FetchReader.java similarity index 75% rename from src/main/java/gov/loc/repository/bagit/reader/FetchReader.java rename to src/main/java/nl/knaw/dans/bagit/reader/FetchReader.java index 3e0336dda..d19cb6961 100644 --- a/src/main/java/gov/loc/repository/bagit/reader/FetchReader.java +++ b/src/main/java/nl/knaw/dans/bagit/reader/FetchReader.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.io.BufferedReader; import java.io.IOException; @@ -10,12 +25,12 @@ import java.util.List; import java.util.ResourceBundle; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.FetchItem; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; +import nl.knaw.dans.bagit.domain.FetchItem; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; /** * This class is responsible for reading and parsing fetch.txt file from the filesystem diff --git a/src/main/java/gov/loc/repository/bagit/reader/KeyValueReader.java b/src/main/java/nl/knaw/dans/bagit/reader/KeyValueReader.java similarity index 81% rename from src/main/java/gov/loc/repository/bagit/reader/KeyValueReader.java rename to src/main/java/nl/knaw/dans/bagit/reader/KeyValueReader.java index c0413b2f8..1039c429e 100644 --- a/src/main/java/gov/loc/repository/bagit/reader/KeyValueReader.java +++ b/src/main/java/nl/knaw/dans/bagit/reader/KeyValueReader.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.io.BufferedReader; import java.io.IOException; @@ -14,7 +29,7 @@ import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; -import gov.loc.repository.bagit.exceptions.InvalidBagMetadataException; +import nl.knaw.dans.bagit.exceptions.InvalidBagMetadataException; /** * Convenience class for reading key value pairs from a file diff --git a/src/main/java/gov/loc/repository/bagit/reader/ManifestReader.java b/src/main/java/nl/knaw/dans/bagit/reader/ManifestReader.java similarity index 82% rename from src/main/java/gov/loc/repository/bagit/reader/ManifestReader.java rename to src/main/java/nl/knaw/dans/bagit/reader/ManifestReader.java index fc0b86117..35bc95ae4 100644 --- a/src/main/java/gov/loc/repository/bagit/reader/ManifestReader.java +++ b/src/main/java/nl/knaw/dans/bagit/reader/ManifestReader.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.io.BufferedReader; import java.io.IOException; @@ -11,17 +26,17 @@ import java.util.Map; import java.util.ResourceBundle; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; -import gov.loc.repository.bagit.exceptions.UnsupportedAlgorithmException; -import gov.loc.repository.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; -import gov.loc.repository.bagit.hash.SupportedAlgorithm; -import gov.loc.repository.bagit.util.PathUtils; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; +import nl.knaw.dans.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.hash.SupportedAlgorithm; +import nl.knaw.dans.bagit.util.PathUtils; /** * This class is responsible for reading and parsing manifest files on the filesystem diff --git a/src/main/java/gov/loc/repository/bagit/reader/MetadataReader.java b/src/main/java/nl/knaw/dans/bagit/reader/MetadataReader.java similarity index 73% rename from src/main/java/gov/loc/repository/bagit/reader/MetadataReader.java rename to src/main/java/nl/knaw/dans/bagit/reader/MetadataReader.java index adb9f9451..c62c01e4f 100644 --- a/src/main/java/gov/loc/repository/bagit/reader/MetadataReader.java +++ b/src/main/java/nl/knaw/dans/bagit/reader/MetadataReader.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.io.IOException; import java.nio.charset.Charset; @@ -12,7 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.exceptions.InvalidBagMetadataException; +import nl.knaw.dans.bagit.exceptions.InvalidBagMetadataException; /** * This class is responsible for reading and parsing bagit metadata files from the filesystem diff --git a/src/main/java/gov/loc/repository/bagit/reader/TagFileReader.java b/src/main/java/nl/knaw/dans/bagit/reader/TagFileReader.java similarity index 71% rename from src/main/java/gov/loc/repository/bagit/reader/TagFileReader.java rename to src/main/java/nl/knaw/dans/bagit/reader/TagFileReader.java index d2fceda39..be88e30ac 100644 --- a/src/main/java/gov/loc/repository/bagit/reader/TagFileReader.java +++ b/src/main/java/nl/knaw/dans/bagit/reader/TagFileReader.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.net.URI; import java.net.URISyntaxException; @@ -6,13 +21,13 @@ import java.nio.file.Paths; import java.util.ResourceBundle; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; -import gov.loc.repository.bagit.util.PathUtils; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; +import nl.knaw.dans.bagit.util.PathUtils; /** * Convenience class for reading tag files from the filesystem @@ -25,7 +40,7 @@ public interface TagFileReader { /* * Create the file and check it for various things, like starting with a *, or trying to access a file outside the bag */ - static Path createFileFromManifest(final Path bagRootDir, final String path) throws MaliciousPathException, InvalidBagitFileFormatException{ + static Path createFileFromManifest(final Path bagRootDir, final String path) throws MaliciousPathException, InvalidBagitFileFormatException { String fixedPath = path; if(path.charAt(0) == '*'){ logger.warn(messages.getString("removing_asterisk")); diff --git a/src/main/java/gov/loc/repository/bagit/util/PathUtils.java b/src/main/java/nl/knaw/dans/bagit/util/PathUtils.java similarity index 86% rename from src/main/java/gov/loc/repository/bagit/util/PathUtils.java rename to src/main/java/nl/knaw/dans/bagit/util/PathUtils.java index 744a695d6..38a53d611 100644 --- a/src/main/java/gov/loc/repository/bagit/util/PathUtils.java +++ b/src/main/java/nl/knaw/dans/bagit/util/PathUtils.java @@ -1,13 +1,28 @@ -package gov.loc.repository.bagit.util; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.util; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.DosFileAttributes; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.verify.FileCountAndTotalSizeVistor; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.Version; +import nl.knaw.dans.bagit.verify.FileCountAndTotalSizeVistor; /** * Convenience class for dealing with various path issues diff --git a/src/main/java/gov/loc/repository/bagit/verify/AbstractPayloadFileExistsInManifestsVistor.java b/src/main/java/nl/knaw/dans/bagit/verify/AbstractPayloadFileExistsInManifestsVistor.java similarity index 64% rename from src/main/java/gov/loc/repository/bagit/verify/AbstractPayloadFileExistsInManifestsVistor.java rename to src/main/java/nl/knaw/dans/bagit/verify/AbstractPayloadFileExistsInManifestsVistor.java index 2073786e2..39c0fa580 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/AbstractPayloadFileExistsInManifestsVistor.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/AbstractPayloadFileExistsInManifestsVistor.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.IOException; import java.nio.file.FileVisitResult; @@ -11,7 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.util.PathUtils; +import nl.knaw.dans.bagit.util.PathUtils; /** * Implements {@link SimpleFileVisitor} to ensure that the encountered file is in one of the manifests. diff --git a/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java b/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java similarity index 84% rename from src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java rename to src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java index 8e5e831bb..fdeb19ef1 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/BagVerifier.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.IOException; import java.nio.file.Path; @@ -11,26 +26,25 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.exceptions.CorruptChecksumException; +import nl.knaw.dans.bagit.exceptions.FileNotInManifestException; +import nl.knaw.dans.bagit.exceptions.FileNotInPayloadDirectoryException; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.exceptions.InvalidPayloadOxumException; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; +import nl.knaw.dans.bagit.exceptions.MissingBagitFileException; +import nl.knaw.dans.bagit.exceptions.MissingPayloadDirectoryException; +import nl.knaw.dans.bagit.exceptions.MissingPayloadManifestException; +import nl.knaw.dans.bagit.exceptions.PayloadOxumDoesNotExistException; +import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; +import nl.knaw.dans.bagit.exceptions.VerificationException; +import nl.knaw.dans.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.exceptions.CorruptChecksumException; -import gov.loc.repository.bagit.exceptions.FileNotInManifestException; -import gov.loc.repository.bagit.exceptions.FileNotInPayloadDirectoryException; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.InvalidPayloadOxumException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; -import gov.loc.repository.bagit.exceptions.MissingBagitFileException; -import gov.loc.repository.bagit.exceptions.MissingPayloadDirectoryException; -import gov.loc.repository.bagit.exceptions.MissingPayloadManifestException; -import gov.loc.repository.bagit.exceptions.PayloadOxumDoesNotExistException; -import gov.loc.repository.bagit.exceptions.UnsupportedAlgorithmException; -import gov.loc.repository.bagit.exceptions.VerificationException; -import gov.loc.repository.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; -import gov.loc.repository.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; - /** * Responsible for verifying if a bag is valid, complete */ @@ -102,7 +116,7 @@ public static boolean canQuickVerify(final Bag bag){ * @param bag the bag to verify by payload-oxum * * @throws IOException if there is an error reading a file - * @throws InvalidPayloadOxumException if either the total bytes or the number of files + * @throws InvalidPayloadOxumException if either the total bytes or the number of files * calculated for the payload directory of the bag is different than the supplied values * @throws PayloadOxumDoesNotExistException if the bag does not contain a payload-oxum. * To check, run {@link BagVerifier#canQuickVerify} diff --git a/src/main/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTask.java b/src/main/java/nl/knaw/dans/bagit/verify/CheckIfFileExistsTask.java similarity index 78% rename from src/main/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTask.java rename to src/main/java/nl/knaw/dans/bagit/verify/CheckIfFileExistsTask.java index 9007c0d80..c4d32e4e2 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTask.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/CheckIfFileExistsTask.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.IOException; import java.nio.file.DirectoryStream; diff --git a/src/main/java/gov/loc/repository/bagit/verify/CheckManifestHashesTask.java b/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java similarity index 75% rename from src/main/java/gov/loc/repository/bagit/verify/CheckManifestHashesTask.java rename to src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java index b7153f0a2..79c847d2b 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/CheckManifestHashesTask.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.IOException; import java.nio.file.Files; @@ -10,11 +25,11 @@ import java.util.ResourceBundle; import java.util.concurrent.CountDownLatch; +import nl.knaw.dans.bagit.exceptions.CorruptChecksumException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.exceptions.CorruptChecksumException; -import gov.loc.repository.bagit.hash.Hasher; +import nl.knaw.dans.bagit.hash.Hasher; /** * Checks a give file to make sure the given checksum hash matches the computed checksum hash. diff --git a/src/main/java/gov/loc/repository/bagit/verify/FileCountAndTotalSizeVistor.java b/src/main/java/nl/knaw/dans/bagit/verify/FileCountAndTotalSizeVistor.java similarity index 69% rename from src/main/java/gov/loc/repository/bagit/verify/FileCountAndTotalSizeVistor.java rename to src/main/java/nl/knaw/dans/bagit/verify/FileCountAndTotalSizeVistor.java index 25babc547..ca6d3b212 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/FileCountAndTotalSizeVistor.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/FileCountAndTotalSizeVistor.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.IOException; import java.nio.file.FileVisitResult; diff --git a/src/main/java/gov/loc/repository/bagit/verify/MandatoryVerifier.java b/src/main/java/nl/knaw/dans/bagit/verify/MandatoryVerifier.java similarity index 79% rename from src/main/java/gov/loc/repository/bagit/verify/MandatoryVerifier.java rename to src/main/java/nl/knaw/dans/bagit/verify/MandatoryVerifier.java index 7d14d8357..e7ece22f5 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/MandatoryVerifier.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/MandatoryVerifier.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.File; import java.io.IOException; @@ -8,18 +23,18 @@ import java.util.List; import java.util.ResourceBundle; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.FetchItem; +import nl.knaw.dans.bagit.exceptions.FileNotInPayloadDirectoryException; +import nl.knaw.dans.bagit.exceptions.MissingBagitFileException; +import nl.knaw.dans.bagit.exceptions.MissingPayloadDirectoryException; +import nl.knaw.dans.bagit.exceptions.MissingPayloadManifestException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.FetchItem; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.exceptions.FileNotInPayloadDirectoryException; -import gov.loc.repository.bagit.exceptions.MissingBagitFileException; -import gov.loc.repository.bagit.exceptions.MissingPayloadDirectoryException; -import gov.loc.repository.bagit.exceptions.MissingPayloadManifestException; -import gov.loc.repository.bagit.util.PathUtils; +import nl.knaw.dans.bagit.domain.Version; +import nl.knaw.dans.bagit.util.PathUtils; /** * Responsible for checking all things related to mandatory files for the bagit specification diff --git a/src/main/java/gov/loc/repository/bagit/verify/ManifestVerifier.java b/src/main/java/nl/knaw/dans/bagit/verify/ManifestVerifier.java similarity index 84% rename from src/main/java/gov/loc/repository/bagit/verify/ManifestVerifier.java rename to src/main/java/nl/knaw/dans/bagit/verify/ManifestVerifier.java index 645953f15..453e24cd2 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/ManifestVerifier.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/ManifestVerifier.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.IOException; import java.nio.file.DirectoryStream; @@ -12,21 +27,21 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.exceptions.FileNotInPayloadDirectoryException; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; +import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; +import nl.knaw.dans.bagit.reader.ManifestReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.exceptions.FileNotInPayloadDirectoryException; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; -import gov.loc.repository.bagit.exceptions.UnsupportedAlgorithmException; -import gov.loc.repository.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; -import gov.loc.repository.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; -import gov.loc.repository.bagit.reader.ManifestReader; -import gov.loc.repository.bagit.util.PathUtils; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.domain.Version; +import nl.knaw.dans.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.util.PathUtils; /** * Responsible for all things related to the manifest during verification. diff --git a/src/main/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAllManifestsVistor.java b/src/main/java/nl/knaw/dans/bagit/verify/PayloadFileExistsInAllManifestsVistor.java similarity index 60% rename from src/main/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAllManifestsVistor.java rename to src/main/java/nl/knaw/dans/bagit/verify/PayloadFileExistsInAllManifestsVistor.java index 3763f913c..82d5cbb66 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAllManifestsVistor.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/PayloadFileExistsInAllManifestsVistor.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -7,11 +22,10 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.Set; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.exceptions.FileNotInManifestException; import org.slf4j.helpers.MessageFormatter; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.exceptions.FileNotInManifestException; - /** * Implements {@link SimpleFileVisitor} to ensure that the encountered file is in one of the manifests. */ @@ -24,7 +38,7 @@ public PayloadFileExistsInAllManifestsVistor(final Set manifests, fina } @Override - public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs)throws FileNotInManifestException{ + public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs)throws FileNotInManifestException { if(Files.isRegularFile(path)){ for(final Manifest manifest : manifests){ if(!manifest.getFileToChecksumMap().keySet().contains(path.normalize())){ diff --git a/src/main/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistor.java b/src/main/java/nl/knaw/dans/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistor.java similarity index 67% rename from src/main/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistor.java rename to src/main/java/nl/knaw/dans/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistor.java index ea6077f6c..f3835ea0c 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistor.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistor.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.IOException; import java.nio.file.FileVisitResult; @@ -9,10 +24,9 @@ import java.util.ResourceBundle; import java.util.Set; +import nl.knaw.dans.bagit.exceptions.FileNotInManifestException; import org.slf4j.helpers.MessageFormatter; -import gov.loc.repository.bagit.exceptions.FileNotInManifestException; - /** * Implements {@link SimpleFileVisitor} to ensure that the encountered file is in one of the manifests. */ @@ -26,7 +40,7 @@ public PayloadFileExistsInAtLeastOneManifestVistor(final Set filesListedIn } @Override - public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs)throws IOException, FileNotInManifestException{ + public FileVisitResult visitFile(final Path path, final BasicFileAttributes attrs)throws IOException, FileNotInManifestException { if(Files.isHidden(path) && ignoreHiddenFiles){ logger.debug(messages.getString("skipping_hidden_file"), path); } diff --git a/src/main/java/gov/loc/repository/bagit/verify/QuickVerifier.java b/src/main/java/nl/knaw/dans/bagit/verify/QuickVerifier.java similarity index 80% rename from src/main/java/gov/loc/repository/bagit/verify/QuickVerifier.java rename to src/main/java/nl/knaw/dans/bagit/verify/QuickVerifier.java index 2b6689eb1..993521089 100644 --- a/src/main/java/gov/loc/repository/bagit/verify/QuickVerifier.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/QuickVerifier.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.IOException; import java.nio.file.Files; @@ -6,14 +21,14 @@ import java.util.ResourceBundle; import java.util.AbstractMap.SimpleImmutableEntry; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.exceptions.InvalidPayloadOxumException; +import nl.knaw.dans.bagit.exceptions.PayloadOxumDoesNotExistException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.exceptions.InvalidPayloadOxumException; -import gov.loc.repository.bagit.exceptions.PayloadOxumDoesNotExistException; -import gov.loc.repository.bagit.util.PathUtils; +import nl.knaw.dans.bagit.util.PathUtils; /** * responsible for all things related to quick verification. Quick verification does not @@ -59,7 +74,7 @@ private static String getPayloadOxum(final Bag bag){ * @param bag the bag to verify by payload-oxum * * @throws IOException if there is an error reading a file - * @throws InvalidPayloadOxumException if either the total bytes or the number of files + * @throws InvalidPayloadOxumException if either the total bytes or the number of files * calculated for the payload directory of the bag is different than the supplied values * @throws PayloadOxumDoesNotExistException if the bag does not contain a payload-oxum. * To check, run {@link BagVerifier#canQuickVerify} diff --git a/src/main/java/gov/loc/repository/bagit/writer/BagWriter.java b/src/main/java/nl/knaw/dans/bagit/writer/BagWriter.java similarity index 85% rename from src/main/java/gov/loc/repository/bagit/writer/BagWriter.java rename to src/main/java/nl/knaw/dans/bagit/writer/BagWriter.java index 8c87a4ff4..7f1ba2e4a 100644 --- a/src/main/java/gov/loc/repository/bagit/writer/BagWriter.java +++ b/src/main/java/nl/knaw/dans/bagit/writer/BagWriter.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.io.IOException; import java.nio.file.Files; @@ -10,14 +25,13 @@ import java.util.ResourceBundle; import java.util.Set; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.hash.Hasher; +import nl.knaw.dans.bagit.util.PathUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.hash.Hasher; -import gov.loc.repository.bagit.util.PathUtils; - /** * responsible for writing out a {@link Bag} */ diff --git a/src/main/java/gov/loc/repository/bagit/writer/BagitFileWriter.java b/src/main/java/nl/knaw/dans/bagit/writer/BagitFileWriter.java similarity index 71% rename from src/main/java/gov/loc/repository/bagit/writer/BagitFileWriter.java rename to src/main/java/nl/knaw/dans/bagit/writer/BagitFileWriter.java index 7faef55e2..5920bd2c6 100644 --- a/src/main/java/gov/loc/repository/bagit/writer/BagitFileWriter.java +++ b/src/main/java/nl/knaw/dans/bagit/writer/BagitFileWriter.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.io.IOException; import java.nio.charset.Charset; @@ -10,7 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.Version; +import nl.knaw.dans.bagit.domain.Version; import java.io.BufferedWriter; /** diff --git a/src/main/java/gov/loc/repository/bagit/writer/FetchWriter.java b/src/main/java/nl/knaw/dans/bagit/writer/FetchWriter.java similarity index 76% rename from src/main/java/gov/loc/repository/bagit/writer/FetchWriter.java rename to src/main/java/nl/knaw/dans/bagit/writer/FetchWriter.java index ab121d1af..3a1018b68 100644 --- a/src/main/java/gov/loc/repository/bagit/writer/FetchWriter.java +++ b/src/main/java/nl/knaw/dans/bagit/writer/FetchWriter.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.io.IOException; import java.nio.charset.Charset; @@ -8,10 +23,10 @@ import java.util.List; import java.util.ResourceBundle; +import nl.knaw.dans.bagit.domain.FetchItem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.FetchItem; import java.io.BufferedWriter; /** diff --git a/src/main/java/gov/loc/repository/bagit/writer/ManifestWriter.java b/src/main/java/nl/knaw/dans/bagit/writer/ManifestWriter.java similarity index 83% rename from src/main/java/gov/loc/repository/bagit/writer/ManifestWriter.java rename to src/main/java/nl/knaw/dans/bagit/writer/ManifestWriter.java index 445e7515a..7b0188d42 100644 --- a/src/main/java/gov/loc/repository/bagit/writer/ManifestWriter.java +++ b/src/main/java/nl/knaw/dans/bagit/writer/ManifestWriter.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.io.IOException; import java.nio.charset.Charset; @@ -9,10 +24,10 @@ import java.util.ResourceBundle; import java.util.Set; +import nl.knaw.dans.bagit.domain.Manifest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.Manifest; import java.io.BufferedWriter; /** diff --git a/src/main/java/gov/loc/repository/bagit/writer/MetadataWriter.java b/src/main/java/nl/knaw/dans/bagit/writer/MetadataWriter.java similarity index 73% rename from src/main/java/gov/loc/repository/bagit/writer/MetadataWriter.java rename to src/main/java/nl/knaw/dans/bagit/writer/MetadataWriter.java index 5f971d41f..1a887ceec 100644 --- a/src/main/java/gov/loc/repository/bagit/writer/MetadataWriter.java +++ b/src/main/java/nl/knaw/dans/bagit/writer/MetadataWriter.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.io.IOException; import java.nio.charset.Charset; @@ -8,12 +23,11 @@ import java.util.ResourceBundle; import java.util.AbstractMap.SimpleImmutableEntry; +import nl.knaw.dans.bagit.domain.Metadata; +import nl.knaw.dans.bagit.domain.Version; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.Metadata; -import gov.loc.repository.bagit.domain.Version; - /** * Responsible for writing out the bag {@link Metadata} to the filesystem */ diff --git a/src/main/java/gov/loc/repository/bagit/writer/PayloadWriter.java b/src/main/java/nl/knaw/dans/bagit/writer/PayloadWriter.java similarity index 80% rename from src/main/java/gov/loc/repository/bagit/writer/PayloadWriter.java rename to src/main/java/nl/knaw/dans/bagit/writer/PayloadWriter.java index 1dee4a4ac..6471a57cc 100644 --- a/src/main/java/gov/loc/repository/bagit/writer/PayloadWriter.java +++ b/src/main/java/nl/knaw/dans/bagit/writer/PayloadWriter.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.io.IOException; import java.nio.file.Files; @@ -9,14 +24,13 @@ import java.util.ResourceBundle; import java.util.Set; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.FetchItem; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.domain.Version; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.FetchItem; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.domain.Version; - /** * Responsible for writing out the bag payload to the filesystem */ diff --git a/src/main/java/gov/loc/repository/bagit/writer/RelativePathWriter.java b/src/main/java/nl/knaw/dans/bagit/writer/RelativePathWriter.java similarity index 51% rename from src/main/java/gov/loc/repository/bagit/writer/RelativePathWriter.java rename to src/main/java/nl/knaw/dans/bagit/writer/RelativePathWriter.java index 437c29303..ffe1150c9 100644 --- a/src/main/java/gov/loc/repository/bagit/writer/RelativePathWriter.java +++ b/src/main/java/nl/knaw/dans/bagit/writer/RelativePathWriter.java @@ -1,8 +1,23 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.nio.file.Path; -import gov.loc.repository.bagit.util.PathUtils; +import nl.knaw.dans.bagit.util.PathUtils; /** * Convenience class for writing a relative path diff --git a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileTest.java b/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileTest.java deleted file mode 100644 index 958da1fe3..000000000 --- a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package gov.loc.repository.bagit.conformance.profile; - -import java.io.File; -import java.util.Arrays; -import java.util.HashMap; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class BagitProfileTest extends AbstractBagitProfileTest{ - - @Test - public void testToString() throws Exception{ - String expectedOutput = "BagitProfile [bagitProfileIdentifier=http://canadiana.org/standards/bagit/tdr_ingest.json, " - + "sourceOrganization=Candiana.org, " - + "externalDescription=BagIt profile for ingesting content into the C.O. TDR loading dock., " - + "contactName=William Wueppelmann, " - + "contactEmail=tdr@canadiana.com, " - + "contactPhone=+1 613 907 7040, " - + "version=1.2, " - + "bagInfoRequirements={" - + "Payload-Oxum=[required=true, acceptableValues=[], repeatable=false], " - + "Bag-Size=[required=true, acceptableValues=[], repeatable=false], " - + "Bagging-Date=[required=true, acceptableValues=[], repeatable=false], " - + "Source-Organization=[required=true, acceptableValues=[Simon Fraser University, York University], repeatable=false], " - + "Bag-Count=[required=true, acceptableValues=[], repeatable=false], " - + "Organization-Address=[required=true, acceptableValues=[8888 University Drive Burnaby, B.C. V5A 1S6 Canada, 4700 Keele Street Toronto, Ontario M3J 1P3 Canada], repeatable=false], " - + "Bag-Group-Identifier=[required=false, acceptableValues=[], repeatable=false], " - + "External-Identifier=[required=false, acceptableValues=[], repeatable=false], " - + "Internal-Sender-Identifier=[required=false, acceptableValues=[], repeatable=false], " - + "Contact-Email=[required=true, acceptableValues=[], repeatable=false], " - + "Contact-Phone=[required=false, acceptableValues=[], repeatable=false], " - + "Internal-Sender-Description=[required=false, acceptableValues=[], repeatable=false], " - + "External-Description=[required=true, acceptableValues=[], repeatable=false], " - + "Contact-Name=[required=true, acceptableValues=[Mark Jordan, Nick Ruest], repeatable=false]}, " - + "manifestTypesRequired=[md5], " - + "fetchFileAllowed=false, " - + "serialization=forbidden, " - + "acceptableMIMESerializationTypes=[application/zip], " - + "acceptableBagitVersions=[0.96], " - + "tagManifestTypesRequired=[md5], " - + "tagFilesRequired=[DPN/dpnFirstNode.txt, DPN/dpnRegistry]]"; - - BagitProfile profile = mapper.readValue(new File("src/test/resources/bagitProfiles/exampleProfile.json"), BagitProfile.class); - System.err.println(profile.toString()); - Assertions.assertEquals(expectedOutput, profile.toString()); - } - - @Test - public void testEquals(){ - BagitProfile profile = createExpectedProfile(); - - Assertions.assertFalse(profile.equals(null)); - - BagitProfile differentBagitProfileIdentifier = createExpectedProfile(); - differentBagitProfileIdentifier.setBagitProfileIdentifier("foo"); - Assertions.assertFalse(profile.equals(differentBagitProfileIdentifier)); - - BagitProfile differentSourceOrganization = createExpectedProfile(); - differentSourceOrganization.setSourceOrganization("foo"); - Assertions.assertFalse(profile.equals(differentSourceOrganization)); - - BagitProfile differentExternalDescription = createExpectedProfile(); - differentExternalDescription.setExternalDescription("foo"); - Assertions.assertFalse(profile.equals(differentExternalDescription)); - - BagitProfile differentContactName = createExpectedProfile(); - differentContactName.setContactName("foo"); - Assertions.assertFalse(profile.equals(differentContactName)); - - BagitProfile differentContactEmail = createExpectedProfile(); - differentContactEmail.setContactEmail("foo"); - Assertions.assertFalse(profile.equals(differentContactEmail)); - - BagitProfile differentContactPhone = createExpectedProfile(); - differentContactPhone.setContactPhone("foo"); - Assertions.assertFalse(profile.equals(differentContactPhone)); - - BagitProfile differentVersion = createExpectedProfile(); - differentVersion.setVersion("foo"); - Assertions.assertFalse(profile.equals(differentVersion)); - - BagitProfile differentBagInfoRequirements = createExpectedProfile(); - differentBagInfoRequirements.setBagInfoRequirements(new HashMap<>()); - Assertions.assertFalse(profile.equals(differentBagInfoRequirements)); - - BagitProfile differentManifestTypesRequired = createExpectedProfile(); - differentManifestTypesRequired.setManifestTypesRequired(Arrays.asList("foo")); - Assertions.assertFalse(profile.equals(differentManifestTypesRequired)); - - BagitProfile differentFetchFileAllowed = createExpectedProfile(); - differentFetchFileAllowed.setFetchFileAllowed(true); - Assertions.assertFalse(profile.equals(differentFetchFileAllowed)); - - BagitProfile differentSerialization = createExpectedProfile(); - differentSerialization.setSerialization(Serialization.required); - Assertions.assertFalse(profile.equals(differentSerialization)); - - BagitProfile differentAcceptableMIMESerializationTypes = createExpectedProfile(); - differentAcceptableMIMESerializationTypes.setAcceptableMIMESerializationTypes(Arrays.asList("foo")); - Assertions.assertFalse(profile.equals(differentAcceptableMIMESerializationTypes)); - - BagitProfile differentAcceptableBagitVersions = createExpectedProfile(); - differentAcceptableBagitVersions.setAcceptableBagitVersions(Arrays.asList("foo")); - Assertions.assertFalse(profile.equals(differentAcceptableBagitVersions)); - - BagitProfile differentTagManifestTypesRequired = createExpectedProfile(); - differentTagManifestTypesRequired.setTagManifestTypesRequired(Arrays.asList("foo")); - Assertions.assertFalse(profile.equals(differentTagManifestTypesRequired)); - - BagitProfile differentTagFilesRequired = createExpectedProfile(); - differentTagFilesRequired.setTagFilesRequired(Arrays.asList("foo")); - Assertions.assertFalse(profile.equals(differentTagFilesRequired)); - } -} diff --git a/src/test/java/gov/loc/repository/bagit/verify/MySupportedNameToAlgorithmMapping.java b/src/test/java/gov/loc/repository/bagit/verify/MySupportedNameToAlgorithmMapping.java deleted file mode 100644 index 55e72898d..000000000 --- a/src/test/java/gov/loc/repository/bagit/verify/MySupportedNameToAlgorithmMapping.java +++ /dev/null @@ -1,18 +0,0 @@ -package gov.loc.repository.bagit.verify; - -import gov.loc.repository.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; -import gov.loc.repository.bagit.hash.StandardSupportedAlgorithms; -import gov.loc.repository.bagit.hash.SupportedAlgorithm; - -public class MySupportedNameToAlgorithmMapping implements BagitAlgorithmNameToSupportedAlgorithmMapping { - - @Override - public SupportedAlgorithm getSupportedAlgorithm(String bagitAlgorithmName) { - if("sha3256".equals(bagitAlgorithmName)){ - return new SHA3256Algorithm(); - } - - return StandardSupportedAlgorithms.valueOf(bagitAlgorithmName.toUpperCase()); - } - -} diff --git a/src/test/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistorTest.java b/src/test/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistorTest.java deleted file mode 100644 index 41b2a74a1..000000000 --- a/src/test/java/gov/loc/repository/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistorTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package gov.loc.repository.bagit.verify; - -import java.util.HashSet; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import gov.loc.repository.bagit.TempFolderTest; -import gov.loc.repository.bagit.exceptions.FileNotInManifestException; - -public class PayloadFileExistsInAtLeastOneManifestVistorTest extends TempFolderTest { - - @Test - public void testFileNotInManifestException() throws Exception{ - - PayloadFileExistsInAtLeastOneManifestVistor sut = new PayloadFileExistsInAtLeastOneManifestVistor(new HashSet<>(), true); - Assertions.assertThrows(FileNotInManifestException.class, - () -> { sut.visitFile(createFile("aNewFile"), null); }); - } -} diff --git a/src/test/java/gov/loc/repository/bagit/verify/SHA3256Algorithm.java b/src/test/java/gov/loc/repository/bagit/verify/SHA3256Algorithm.java deleted file mode 100644 index 6f18bdb65..000000000 --- a/src/test/java/gov/loc/repository/bagit/verify/SHA3256Algorithm.java +++ /dev/null @@ -1,17 +0,0 @@ -package gov.loc.repository.bagit.verify; - -import gov.loc.repository.bagit.hash.SupportedAlgorithm; - -public class SHA3256Algorithm implements SupportedAlgorithm { - - @Override - public String getMessageDigestName() { - return "SHA3-256"; - } - - @Override - public String getBagitName() { - return "sha3256"; - } - -} diff --git a/src/test/java/gov/loc/repository/bagit/PrivateConstructorTest.java b/src/test/java/nl/knaw/dans/bagit/PrivateConstructorTest.java similarity index 68% rename from src/test/java/gov/loc/repository/bagit/PrivateConstructorTest.java rename to src/test/java/nl/knaw/dans/bagit/PrivateConstructorTest.java index e78907d48..c63a09ba8 100644 --- a/src/test/java/gov/loc/repository/bagit/PrivateConstructorTest.java +++ b/src/test/java/nl/knaw/dans/bagit/PrivateConstructorTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; diff --git a/src/test/java/gov/loc/repository/bagit/TempFolderTest.java b/src/test/java/nl/knaw/dans/bagit/TempFolderTest.java similarity index 77% rename from src/test/java/gov/loc/repository/bagit/TempFolderTest.java rename to src/test/java/nl/knaw/dans/bagit/TempFolderTest.java index 471c21039..dea3fe640 100644 --- a/src/test/java/gov/loc/repository/bagit/TempFolderTest.java +++ b/src/test/java/nl/knaw/dans/bagit/TempFolderTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit; import java.io.IOException; import java.nio.file.FileVisitResult; diff --git a/src/test/java/gov/loc/repository/bagit/TestUtils.java b/src/test/java/nl/knaw/dans/bagit/TestUtils.java similarity index 70% rename from src/test/java/gov/loc/repository/bagit/TestUtils.java rename to src/test/java/nl/knaw/dans/bagit/TestUtils.java index 3c29083ad..22a153d85 100644 --- a/src/test/java/gov/loc/repository/bagit/TestUtils.java +++ b/src/test/java/nl/knaw/dans/bagit/TestUtils.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit; import java.io.IOException; import java.nio.file.FileVisitResult; diff --git a/src/test/java/gov/loc/repository/bagit/conformance/BagLinterTest.java b/src/test/java/nl/knaw/dans/bagit/conformance/BagLinterTest.java similarity index 75% rename from src/test/java/gov/loc/repository/bagit/conformance/BagLinterTest.java rename to src/test/java/nl/knaw/dans/bagit/conformance/BagLinterTest.java index 5af360e26..f04d8c2fa 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/BagLinterTest.java +++ b/src/test/java/nl/knaw/dans/bagit/conformance/BagLinterTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.io.File; import java.io.InputStream; @@ -12,14 +27,14 @@ import java.util.HashSet; import java.util.Set; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.domain.Bag; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.reader.BagReader; +import nl.knaw.dans.bagit.reader.BagReader; -public class BagLinterTest extends PrivateConstructorTest{ +public class BagLinterTest extends PrivateConstructorTest { private final Path rootDir = Paths.get("src","test","resources","linterTestBag"); diff --git a/src/test/java/gov/loc/repository/bagit/conformance/BagProfileCheckerTest.java b/src/test/java/nl/knaw/dans/bagit/conformance/BagProfileCheckerTest.java similarity index 78% rename from src/test/java/gov/loc/repository/bagit/conformance/BagProfileCheckerTest.java rename to src/test/java/nl/knaw/dans/bagit/conformance/BagProfileCheckerTest.java index 1074d2da4..c17d8ef2f 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/BagProfileCheckerTest.java +++ b/src/test/java/nl/knaw/dans/bagit/conformance/BagProfileCheckerTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.io.File; import java.io.InputStream; @@ -7,19 +22,19 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.domain.Bag; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.exceptions.conformance.BagitVersionIsNotAcceptableException; -import gov.loc.repository.bagit.exceptions.conformance.FetchFileNotAllowedException; -import gov.loc.repository.bagit.exceptions.conformance.MetatdataValueIsNotAcceptableException; -import gov.loc.repository.bagit.exceptions.conformance.MetatdataValueIsNotRepeatableException; -import gov.loc.repository.bagit.exceptions.conformance.RequiredManifestNotPresentException; -import gov.loc.repository.bagit.exceptions.conformance.RequiredMetadataFieldNotPresentException; -import gov.loc.repository.bagit.exceptions.conformance.RequiredTagFileNotPresentException; -import gov.loc.repository.bagit.reader.BagReader; +import nl.knaw.dans.bagit.exceptions.conformance.BagitVersionIsNotAcceptableException; +import nl.knaw.dans.bagit.exceptions.conformance.FetchFileNotAllowedException; +import nl.knaw.dans.bagit.exceptions.conformance.MetatdataValueIsNotAcceptableException; +import nl.knaw.dans.bagit.exceptions.conformance.MetatdataValueIsNotRepeatableException; +import nl.knaw.dans.bagit.exceptions.conformance.RequiredManifestNotPresentException; +import nl.knaw.dans.bagit.exceptions.conformance.RequiredMetadataFieldNotPresentException; +import nl.knaw.dans.bagit.exceptions.conformance.RequiredTagFileNotPresentException; +import nl.knaw.dans.bagit.reader.BagReader; public class BagProfileCheckerTest extends PrivateConstructorTest { private static final Path profileJson = new File("src/test/resources/bagitProfiles/exampleProfile.json").toPath(); diff --git a/src/test/java/gov/loc/repository/bagit/conformance/EncodingCheckerTest.java b/src/test/java/nl/knaw/dans/bagit/conformance/EncodingCheckerTest.java similarity index 55% rename from src/test/java/gov/loc/repository/bagit/conformance/EncodingCheckerTest.java rename to src/test/java/nl/knaw/dans/bagit/conformance/EncodingCheckerTest.java index 1f21a1f53..2ae22006a 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/EncodingCheckerTest.java +++ b/src/test/java/nl/knaw/dans/bagit/conformance/EncodingCheckerTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.nio.charset.StandardCharsets; import java.util.Arrays; diff --git a/src/test/java/gov/loc/repository/bagit/conformance/ManifestCheckerTest.java b/src/test/java/nl/knaw/dans/bagit/conformance/ManifestCheckerTest.java similarity index 87% rename from src/test/java/gov/loc/repository/bagit/conformance/ManifestCheckerTest.java rename to src/test/java/nl/knaw/dans/bagit/conformance/ManifestCheckerTest.java index 537fe812a..2fe809be1 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/ManifestCheckerTest.java +++ b/src/test/java/nl/knaw/dans/bagit/conformance/ManifestCheckerTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; @@ -10,14 +25,14 @@ import java.util.HashSet; import java.util.Set; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.domain.Version; -public class ManifestCheckerTest extends PrivateConstructorTest{ +public class ManifestCheckerTest extends PrivateConstructorTest { private final Path rootDir = Paths.get("src","test","resources","linterTestBag"); @@ -146,7 +161,7 @@ public void testOSSpecificFilesRegex(){ } @Test - public void testParsePath() throws InvalidBagitFileFormatException{ + public void testParsePath() throws InvalidBagitFileFormatException { Assertions.assertThrows(InvalidBagitFileFormatException.class, () -> { ManifestChecker.parsePath("foobarham"); }); } diff --git a/src/test/java/gov/loc/repository/bagit/conformance/MetadataCheckerTest.java b/src/test/java/nl/knaw/dans/bagit/conformance/MetadataCheckerTest.java similarity index 64% rename from src/test/java/gov/loc/repository/bagit/conformance/MetadataCheckerTest.java rename to src/test/java/nl/knaw/dans/bagit/conformance/MetadataCheckerTest.java index 822e660a0..7bb28e03e 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/MetadataCheckerTest.java +++ b/src/test/java/nl/knaw/dans/bagit/conformance/MetadataCheckerTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; @@ -9,11 +24,10 @@ import java.util.HashSet; import java.util.Set; +import nl.knaw.dans.bagit.PrivateConstructorTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; - public class MetadataCheckerTest extends PrivateConstructorTest { private final Path rootDir = Paths.get("src","test","resources","linterTestBag"); diff --git a/src/test/java/gov/loc/repository/bagit/conformance/VersionCheckerTest.java b/src/test/java/nl/knaw/dans/bagit/conformance/VersionCheckerTest.java similarity index 52% rename from src/test/java/gov/loc/repository/bagit/conformance/VersionCheckerTest.java rename to src/test/java/nl/knaw/dans/bagit/conformance/VersionCheckerTest.java index b5450cde6..e0bcd9c6c 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/VersionCheckerTest.java +++ b/src/test/java/nl/knaw/dans/bagit/conformance/VersionCheckerTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance; import java.util.Arrays; import java.util.Collections; @@ -8,7 +23,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.domain.Version; +import nl.knaw.dans.bagit.domain.Version; public class VersionCheckerTest { diff --git a/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java b/src/test/java/nl/knaw/dans/bagit/conformance/profile/AbstractBagitProfileTest.java similarity index 85% rename from src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java rename to src/test/java/nl/knaw/dans/bagit/conformance/profile/AbstractBagitProfileTest.java index 59d91c460..eb98c1760 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/profile/AbstractBagitProfileTest.java +++ b/src/test/java/nl/knaw/dans/bagit/conformance/profile/AbstractBagitProfileTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance.profile; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance.profile; import java.util.Arrays; import java.util.HashMap; diff --git a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirementTest.java b/src/test/java/nl/knaw/dans/bagit/conformance/profile/BagInfoRequirementTest.java similarity index 59% rename from src/test/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirementTest.java rename to src/test/java/nl/knaw/dans/bagit/conformance/profile/BagInfoRequirementTest.java index 14bfb9f63..35c3bd87c 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagInfoRequirementTest.java +++ b/src/test/java/nl/knaw/dans/bagit/conformance/profile/BagInfoRequirementTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance.profile; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance.profile; import java.util.Arrays; diff --git a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializerTest.java b/src/test/java/nl/knaw/dans/bagit/conformance/profile/BagitProfileDeserializerTest.java similarity index 84% rename from src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializerTest.java rename to src/test/java/nl/knaw/dans/bagit/conformance/profile/BagitProfileDeserializerTest.java index 30a6b3031..a1fa94fd5 100644 --- a/src/test/java/gov/loc/repository/bagit/conformance/profile/BagitProfileDeserializerTest.java +++ b/src/test/java/nl/knaw/dans/bagit/conformance/profile/BagitProfileDeserializerTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.conformance.profile; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance.profile; import java.io.File; diff --git a/src/test/java/nl/knaw/dans/bagit/conformance/profile/BagitProfileTest.java b/src/test/java/nl/knaw/dans/bagit/conformance/profile/BagitProfileTest.java new file mode 100644 index 000000000..0eab58d05 --- /dev/null +++ b/src/test/java/nl/knaw/dans/bagit/conformance/profile/BagitProfileTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.conformance.profile; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.HashMap; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +public class BagitProfileTest extends AbstractBagitProfileTest { + + @Test + public void testToString() throws Exception { + String expectedOutput = "BagitProfile [bagitProfileIdentifier=http://canadiana.org/standards/bagit/tdr_ingest.json, " + + "sourceOrganization=Candiana.org, " + + "externalDescription=BagIt profile for ingesting content into the C.O. TDR loading dock., " + + "contactName=William Wueppelmann, " + + "contactEmail=tdr@canadiana.com, " + + "contactPhone=+1 613 907 7040, " + + "version=1.2, " + + "bagInfoRequirements={" + + "Payload-Oxum=[required=true, acceptableValues=[], repeatable=false], " + + "Bag-Size=[required=true, acceptableValues=[], repeatable=false], " + + "Bagging-Date=[required=true, acceptableValues=[], repeatable=false], " + + "Source-Organization=[required=true, acceptableValues=[Simon Fraser University, York University], repeatable=false], " + + "Bag-Count=[required=true, acceptableValues=[], repeatable=false], " + + "Organization-Address=[required=true, acceptableValues=[8888 University Drive Burnaby, B.C. V5A 1S6 Canada, 4700 Keele Street Toronto, Ontario M3J 1P3 Canada], repeatable=false], " + + "Bag-Group-Identifier=[required=false, acceptableValues=[], repeatable=false], " + + "External-Identifier=[required=false, acceptableValues=[], repeatable=false], " + + "Internal-Sender-Identifier=[required=false, acceptableValues=[], repeatable=false], " + + "Contact-Email=[required=true, acceptableValues=[], repeatable=false], " + + "Contact-Phone=[required=false, acceptableValues=[], repeatable=false], " + + "Internal-Sender-Description=[required=false, acceptableValues=[], repeatable=false], " + + "External-Description=[required=true, acceptableValues=[], repeatable=false], " + + "Contact-Name=[required=true, acceptableValues=[Mark Jordan, Nick Ruest], repeatable=false]}, " + + "manifestTypesRequired=[md5], " + + "fetchFileAllowed=false, " + + "serialization=forbidden, " + + "acceptableMIMESerializationTypes=[application/zip], " + + "acceptableBagitVersions=[0.96], " + + "tagManifestTypesRequired=[md5], " + + "tagFilesRequired=[DPN/dpnFirstNode.txt, DPN/dpnRegistry]]"; + + BagitProfile profile = mapper.readValue(new File("src/test/resources/bagitProfiles/exampleProfile.json"), BagitProfile.class); + Assertions.assertEquals(expectedOutput, profile.toString()); + } + + @Test + public void testEquals() { + BagitProfile profile = createExpectedProfile(); + + assertNotEquals(null, profile); + + BagitProfile differentBagitProfileIdentifier = createExpectedProfile(); + differentBagitProfileIdentifier.setBagitProfileIdentifier("foo"); + assertNotEquals(profile, differentBagitProfileIdentifier); + + BagitProfile differentSourceOrganization = createExpectedProfile(); + differentSourceOrganization.setSourceOrganization("foo"); + assertNotEquals(profile, differentSourceOrganization); + + BagitProfile differentExternalDescription = createExpectedProfile(); + differentExternalDescription.setExternalDescription("foo"); + assertNotEquals(profile, differentExternalDescription); + + BagitProfile differentContactName = createExpectedProfile(); + differentContactName.setContactName("foo"); + assertNotEquals(profile, differentContactName); + + BagitProfile differentContactEmail = createExpectedProfile(); + differentContactEmail.setContactEmail("foo"); + assertNotEquals(profile, differentContactEmail); + + BagitProfile differentContactPhone = createExpectedProfile(); + differentContactPhone.setContactPhone("foo"); + assertNotEquals(profile, differentContactPhone); + + BagitProfile differentVersion = createExpectedProfile(); + differentVersion.setVersion("foo"); + assertNotEquals(profile, differentVersion); + + BagitProfile differentBagInfoRequirements = createExpectedProfile(); + differentBagInfoRequirements.setBagInfoRequirements(new HashMap<>()); + assertNotEquals(profile, differentBagInfoRequirements); + + BagitProfile differentManifestTypesRequired = createExpectedProfile(); + differentManifestTypesRequired.setManifestTypesRequired(List.of("foo")); + assertNotEquals(profile, differentManifestTypesRequired); + + BagitProfile differentFetchFileAllowed = createExpectedProfile(); + differentFetchFileAllowed.setFetchFileAllowed(true); + assertNotEquals(profile, differentFetchFileAllowed); + + BagitProfile differentSerialization = createExpectedProfile(); + differentSerialization.setSerialization(Serialization.required); + assertNotEquals(profile, differentSerialization); + + BagitProfile differentAcceptableMIMESerializationTypes = createExpectedProfile(); + differentAcceptableMIMESerializationTypes.setAcceptableMIMESerializationTypes(List.of("foo")); + assertNotEquals(profile, differentAcceptableMIMESerializationTypes); + + BagitProfile differentAcceptableBagitVersions = createExpectedProfile(); + differentAcceptableBagitVersions.setAcceptableBagitVersions(List.of("foo")); + assertNotEquals(profile, differentAcceptableBagitVersions); + + BagitProfile differentTagManifestTypesRequired = createExpectedProfile(); + differentTagManifestTypesRequired.setTagManifestTypesRequired(List.of("foo")); + assertNotEquals(profile, differentTagManifestTypesRequired); + + BagitProfile differentTagFilesRequired = createExpectedProfile(); + differentTagFilesRequired.setTagFilesRequired(List.of("foo")); + assertNotEquals(profile, differentTagFilesRequired); + } +} diff --git a/src/test/java/gov/loc/repository/bagit/creator/AddPayloadToBagManifestVistorTest.java b/src/test/java/nl/knaw/dans/bagit/creator/AddPayloadToBagManifestVistorTest.java similarity index 78% rename from src/test/java/gov/loc/repository/bagit/creator/AddPayloadToBagManifestVistorTest.java rename to src/test/java/nl/knaw/dans/bagit/creator/AddPayloadToBagManifestVistorTest.java index 09eb29bc1..3bdd2db1f 100644 --- a/src/test/java/gov/loc/repository/bagit/creator/AddPayloadToBagManifestVistorTest.java +++ b/src/test/java/nl/knaw/dans/bagit/creator/AddPayloadToBagManifestVistorTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.creator; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.creator; import java.io.File; import java.io.IOException; @@ -10,14 +25,13 @@ import java.util.HashMap; import java.util.Map; +import nl.knaw.dans.bagit.TempFolderTest; +import nl.knaw.dans.bagit.TestUtils; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.hash.StandardSupportedAlgorithms; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.TempFolderTest; -import gov.loc.repository.bagit.TestUtils; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.hash.StandardSupportedAlgorithms; - public class AddPayloadToBagManifestVistorTest extends TempFolderTest { @Test diff --git a/src/test/java/gov/loc/repository/bagit/creator/BagCreatorTest.java b/src/test/java/nl/knaw/dans/bagit/creator/BagCreatorTest.java similarity index 84% rename from src/test/java/gov/loc/repository/bagit/creator/BagCreatorTest.java rename to src/test/java/nl/knaw/dans/bagit/creator/BagCreatorTest.java index b8432f9a0..20e71d4fc 100644 --- a/src/test/java/gov/loc/repository/bagit/creator/BagCreatorTest.java +++ b/src/test/java/nl/knaw/dans/bagit/creator/BagCreatorTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.creator; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.creator; import java.io.File; import java.io.IOException; @@ -9,16 +24,16 @@ import java.util.Arrays; import java.util.List; +import nl.knaw.dans.bagit.TempFolderTest; +import nl.knaw.dans.bagit.TestUtils; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.hash.StandardSupportedAlgorithms; +import nl.knaw.dans.bagit.util.PathUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.TempFolderTest; -import gov.loc.repository.bagit.TestUtils; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.hash.StandardSupportedAlgorithms; -import gov.loc.repository.bagit.util.PathUtils; +import nl.knaw.dans.bagit.domain.Version; public class BagCreatorTest extends TempFolderTest { @@ -102,7 +117,7 @@ private TestStructure createTestStructure() throws IOException{ Assertions.assertTrue(Files.isHidden(hiddenFile)); //because the Files.isHidden() always returns false for windows if it is a directory - Assertions.assertTrue(PathUtils.isHidden(hiddenDirectory)); + Assertions.assertTrue(PathUtils.isHidden(hiddenDirectory)); Path hiddenFile2 = hiddenDirectory.resolve(".hiddenFile2.txt"); Files.createFile(hiddenFile2); diff --git a/src/test/java/gov/loc/repository/bagit/domain/BagTest.java b/src/test/java/nl/knaw/dans/bagit/domain/BagTest.java similarity index 72% rename from src/test/java/gov/loc/repository/bagit/domain/BagTest.java rename to src/test/java/nl/knaw/dans/bagit/domain/BagTest.java index a86841bbc..6a87a00e8 100644 --- a/src/test/java/gov/loc/repository/bagit/domain/BagTest.java +++ b/src/test/java/nl/knaw/dans/bagit/domain/BagTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.domain; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.domain; import java.net.MalformedURLException; import java.net.URL; @@ -7,7 +22,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.hash.StandardSupportedAlgorithms; +import nl.knaw.dans.bagit.hash.StandardSupportedAlgorithms; public class BagTest { diff --git a/src/test/java/gov/loc/repository/bagit/domain/FetchItemTest.java b/src/test/java/nl/knaw/dans/bagit/domain/FetchItemTest.java similarity index 77% rename from src/test/java/gov/loc/repository/bagit/domain/FetchItemTest.java rename to src/test/java/nl/knaw/dans/bagit/domain/FetchItemTest.java index 2101a0d55..54fa4a7b9 100644 --- a/src/test/java/gov/loc/repository/bagit/domain/FetchItemTest.java +++ b/src/test/java/nl/knaw/dans/bagit/domain/FetchItemTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.domain; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.domain; import java.io.File; import java.net.MalformedURLException; diff --git a/src/test/java/gov/loc/repository/bagit/domain/ManifestTest.java b/src/test/java/nl/knaw/dans/bagit/domain/ManifestTest.java similarity index 72% rename from src/test/java/gov/loc/repository/bagit/domain/ManifestTest.java rename to src/test/java/nl/knaw/dans/bagit/domain/ManifestTest.java index 69f1edcf6..ae5a1a563 100644 --- a/src/test/java/gov/loc/repository/bagit/domain/ManifestTest.java +++ b/src/test/java/nl/knaw/dans/bagit/domain/ManifestTest.java @@ -1,9 +1,24 @@ -package gov.loc.repository.bagit.domain; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.domain; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.hash.StandardSupportedAlgorithms; +import nl.knaw.dans.bagit.hash.StandardSupportedAlgorithms; public class ManifestTest { diff --git a/src/test/java/gov/loc/repository/bagit/domain/MetadataTest.java b/src/test/java/nl/knaw/dans/bagit/domain/MetadataTest.java similarity index 81% rename from src/test/java/gov/loc/repository/bagit/domain/MetadataTest.java rename to src/test/java/nl/knaw/dans/bagit/domain/MetadataTest.java index e1ed19ac7..ea7c1fce4 100644 --- a/src/test/java/gov/loc/repository/bagit/domain/MetadataTest.java +++ b/src/test/java/nl/knaw/dans/bagit/domain/MetadataTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.domain; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.domain; import java.util.Arrays; diff --git a/src/test/java/gov/loc/repository/bagit/domain/VersionTest.java b/src/test/java/nl/knaw/dans/bagit/domain/VersionTest.java similarity index 84% rename from src/test/java/gov/loc/repository/bagit/domain/VersionTest.java rename to src/test/java/nl/knaw/dans/bagit/domain/VersionTest.java index c97ea48db..ac41ab7f1 100644 --- a/src/test/java/gov/loc/repository/bagit/domain/VersionTest.java +++ b/src/test/java/nl/knaw/dans/bagit/domain/VersionTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.domain; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.domain; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/src/test/java/gov/loc/repository/bagit/examples/fetching/FetchHttpFileExample.java b/src/test/java/nl/knaw/dans/bagit/examples/fetching/FetchHttpFileExample.java similarity index 50% rename from src/test/java/gov/loc/repository/bagit/examples/fetching/FetchHttpFileExample.java rename to src/test/java/nl/knaw/dans/bagit/examples/fetching/FetchHttpFileExample.java index cf4935851..2b38c6895 100644 --- a/src/test/java/gov/loc/repository/bagit/examples/fetching/FetchHttpFileExample.java +++ b/src/test/java/nl/knaw/dans/bagit/examples/fetching/FetchHttpFileExample.java @@ -1,17 +1,31 @@ -package gov.loc.repository.bagit.examples.fetching; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.examples.fetching; import java.io.IOException; import java.net.URL; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import nl.knaw.dans.bagit.TempFolderTest; +import nl.knaw.dans.bagit.domain.FetchItem; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.TempFolderTest; -import gov.loc.repository.bagit.domain.FetchItem; - -public class FetchHttpFileExample extends TempFolderTest{ +public class FetchHttpFileExample extends TempFolderTest { /** * THIS IS JUST AN EXAMPLE. DO NOT USE IN PRODUCTION! diff --git a/src/test/java/gov/loc/repository/bagit/examples/serialization/CreateTarBagExample.java b/src/test/java/nl/knaw/dans/bagit/examples/serialization/CreateTarBagExample.java similarity index 73% rename from src/test/java/gov/loc/repository/bagit/examples/serialization/CreateTarBagExample.java rename to src/test/java/nl/knaw/dans/bagit/examples/serialization/CreateTarBagExample.java index 31d49d436..2626fe3f6 100644 --- a/src/test/java/gov/loc/repository/bagit/examples/serialization/CreateTarBagExample.java +++ b/src/test/java/nl/knaw/dans/bagit/examples/serialization/CreateTarBagExample.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.examples.serialization; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.examples.serialization; import java.io.File; import java.io.IOException; @@ -11,14 +26,13 @@ import java.nio.file.StandardOpenOption; import java.nio.file.attribute.BasicFileAttributes; +import nl.knaw.dans.bagit.TempFolderTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.kamranzafar.jtar.TarEntry; import org.kamranzafar.jtar.TarOutputStream; -import gov.loc.repository.bagit.TempFolderTest; - public class CreateTarBagExample extends TempFolderTest { private Path bagRoot; diff --git a/src/test/java/gov/loc/repository/bagit/examples/serialization/CreateZipBagExample.java b/src/test/java/nl/knaw/dans/bagit/examples/serialization/CreateZipBagExample.java similarity index 72% rename from src/test/java/gov/loc/repository/bagit/examples/serialization/CreateZipBagExample.java rename to src/test/java/nl/knaw/dans/bagit/examples/serialization/CreateZipBagExample.java index 3cccdbbff..0980859ba 100644 --- a/src/test/java/gov/loc/repository/bagit/examples/serialization/CreateZipBagExample.java +++ b/src/test/java/nl/knaw/dans/bagit/examples/serialization/CreateZipBagExample.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.examples.serialization; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.examples.serialization; import java.io.File; import java.io.IOException; @@ -13,16 +28,15 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; +import nl.knaw.dans.bagit.TempFolderTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.TempFolderTest; - /** * Example(s) for creating a zipped bag. */ -public class CreateZipBagExample extends TempFolderTest{ +public class CreateZipBagExample extends TempFolderTest { private Path bagRoot; private Path zippedBagPath; diff --git a/src/test/java/gov/loc/repository/bagit/hash/HasherTest.java b/src/test/java/nl/knaw/dans/bagit/hash/HasherTest.java similarity index 65% rename from src/test/java/gov/loc/repository/bagit/hash/HasherTest.java rename to src/test/java/nl/knaw/dans/bagit/hash/HasherTest.java index 96f03b1ab..c74720a28 100644 --- a/src/test/java/gov/loc/repository/bagit/hash/HasherTest.java +++ b/src/test/java/nl/knaw/dans/bagit/hash/HasherTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.hash; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.hash; import java.io.File; import java.io.IOException; @@ -12,7 +27,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.PrivateConstructorTest; public class HasherTest extends PrivateConstructorTest { diff --git a/src/test/java/gov/loc/repository/bagit/reader/BagReaderTest.java b/src/test/java/nl/knaw/dans/bagit/reader/BagReaderTest.java similarity index 89% rename from src/test/java/gov/loc/repository/bagit/reader/BagReaderTest.java rename to src/test/java/nl/knaw/dans/bagit/reader/BagReaderTest.java index b742a57e1..1e40e078e 100644 --- a/src/test/java/gov/loc/repository/bagit/reader/BagReaderTest.java +++ b/src/test/java/nl/knaw/dans/bagit/reader/BagReaderTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.io.File; import java.net.URL; @@ -10,15 +25,15 @@ import java.util.ArrayList; import java.util.List; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.FetchItem; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.FetchItem; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.domain.Metadata; -import gov.loc.repository.bagit.domain.Version; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.domain.Metadata; +import nl.knaw.dans.bagit.domain.Version; public class BagReaderTest { private BagReader sut; diff --git a/src/test/java/gov/loc/repository/bagit/reader/BagitTextFileReaderTest.java b/src/test/java/nl/knaw/dans/bagit/reader/BagitTextFileReaderTest.java similarity index 79% rename from src/test/java/gov/loc/repository/bagit/reader/BagitTextFileReaderTest.java rename to src/test/java/nl/knaw/dans/bagit/reader/BagitTextFileReaderTest.java index 5353f667e..5d576ee23 100644 --- a/src/test/java/gov/loc/repository/bagit/reader/BagitTextFileReaderTest.java +++ b/src/test/java/nl/knaw/dans/bagit/reader/BagitTextFileReaderTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.io.File; import java.lang.reflect.InvocationTargetException; @@ -10,13 +25,13 @@ import java.util.Arrays; import java.util.List; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.exceptions.UnparsableVersionException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.Version; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.UnparsableVersionException; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.domain.Version; public class BagitTextFileReaderTest extends PrivateConstructorTest { @@ -35,7 +50,7 @@ public void testLinesMatchesStrict() throws Exception{ public void testFirstLineMatchesStrict() throws Exception{ //should fail because it has spaces before the colon List lines = Arrays.asList("BagIt-Version : 1.0", "Tag-File-Character-Encoding: UTF-8"); - Assertions.assertThrows(InvalidBagitFileFormatException.class, + Assertions.assertThrows(InvalidBagitFileFormatException.class, () -> { BagitTextFileReader.throwErrorIfLinesDoNotMatchStrict(lines); }); } @@ -56,7 +71,7 @@ public void testMatchesStrictWithTooManyLines() throws Exception{ } @Test - public void testParseVersionWithBadVersion() throws UnparsableVersionException{ + public void testParseVersionWithBadVersion() throws UnparsableVersionException { Assertions.assertThrows(UnparsableVersionException.class, () -> { BagitTextFileReader.parseVersion("someVersionThatIsUnparsable"); }); } diff --git a/src/test/java/gov/loc/repository/bagit/reader/FetchReaderTest.java b/src/test/java/nl/knaw/dans/bagit/reader/FetchReaderTest.java similarity index 81% rename from src/test/java/gov/loc/repository/bagit/reader/FetchReaderTest.java rename to src/test/java/nl/knaw/dans/bagit/reader/FetchReaderTest.java index 0bef9a31f..fbff0bba6 100644 --- a/src/test/java/gov/loc/repository/bagit/reader/FetchReaderTest.java +++ b/src/test/java/nl/knaw/dans/bagit/reader/FetchReaderTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; @@ -9,16 +24,16 @@ import java.util.Arrays; import java.util.List; +import nl.knaw.dans.bagit.domain.FetchItem; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.condition.OS; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.FetchItem; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; +import nl.knaw.dans.bagit.PrivateConstructorTest; public class FetchReaderTest extends PrivateConstructorTest { @@ -71,7 +86,7 @@ public void testReadFetchWithSizeSpecified() throws Exception{ @Test public void testReadBlankLinesThrowsException() throws Exception{ Path fetchFile = Paths.get(getClass().getClassLoader().getResource("fetchFiles/fetchWithBlankLines.txt").toURI()); - Assertions.assertThrows(InvalidBagitFileFormatException.class, + Assertions.assertThrows(InvalidBagitFileFormatException.class, () -> { FetchReader.readFetch(fetchFile, StandardCharsets.UTF_8, Paths.get("/foo")); }); } @@ -85,7 +100,7 @@ public void testReadWindowsSpecialDirMaliciousFetchThrowsException() throws Exce @Test public void testReadUpADirMaliciousFetchThrowsException() throws Exception{ Path fetchFile = Paths.get(getClass().getClassLoader().getResource("maliciousFetchFile/upAdirectoryReference.txt").toURI()); - Assertions.assertThrows(MaliciousPathException.class, + Assertions.assertThrows(MaliciousPathException.class, () -> { FetchReader.readFetch(fetchFile, StandardCharsets.UTF_8, Paths.get("/bar")); }); } @@ -103,16 +118,4 @@ public void testReadFileUrlMaliciousFetchThrowsException() throws Exception{ Assertions.assertThrows(MaliciousPathException.class, () -> { FetchReader.readFetch(fetchFile, StandardCharsets.UTF_8, Paths.get("/bar")); }); } - - @Test - public void foo(){ - String regex = ".*[ \t]*(\\d*|-)[ \t]*.*"; - String test1 = "http://localhost/foo/data/test2.txt - ~/foo/bar/ham.txt"; - String test2 = "http://localhost/foo/data/dir1/test3.txt 100057 data/dir1/test3.txt"; - String test3 = "http://localhost/foo/data/dir1/test3.txt \t 100057 \t data/dir1/test3.txt"; - - System.err.println(test1.matches(regex)); - System.err.println(test2.matches(regex)); - System.err.println(test3.matches(regex)); - } } diff --git a/src/test/java/gov/loc/repository/bagit/reader/KeyValueReaderTest.java b/src/test/java/nl/knaw/dans/bagit/reader/KeyValueReaderTest.java similarity index 61% rename from src/test/java/gov/loc/repository/bagit/reader/KeyValueReaderTest.java rename to src/test/java/nl/knaw/dans/bagit/reader/KeyValueReaderTest.java index db34aee33..a82e0dc65 100644 --- a/src/test/java/gov/loc/repository/bagit/reader/KeyValueReaderTest.java +++ b/src/test/java/nl/knaw/dans/bagit/reader/KeyValueReaderTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; @@ -8,8 +23,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.exceptions.InvalidBagMetadataException; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.exceptions.InvalidBagMetadataException; public class KeyValueReaderTest extends PrivateConstructorTest { diff --git a/src/test/java/gov/loc/repository/bagit/reader/ManifestReaderTest.java b/src/test/java/nl/knaw/dans/bagit/reader/ManifestReaderTest.java similarity index 71% rename from src/test/java/gov/loc/repository/bagit/reader/ManifestReaderTest.java rename to src/test/java/nl/knaw/dans/bagit/reader/ManifestReaderTest.java index 7e32cba03..222bba0c7 100644 --- a/src/test/java/gov/loc/repository/bagit/reader/ManifestReaderTest.java +++ b/src/test/java/nl/knaw/dans/bagit/reader/ManifestReaderTest.java @@ -1,20 +1,35 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.condition.OS; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; -import gov.loc.repository.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; public class ManifestReaderTest extends PrivateConstructorTest { @@ -36,7 +51,7 @@ public void testReadAllManifests() throws Exception{ @Test public void testReadUpDirectoryMaliciousManifestThrowsException() throws Exception{ Path manifestFile = Paths.get(getClass().getClassLoader().getResource("maliciousManifestFile/upAdirectoryReference.txt").toURI()); - Assertions.assertThrows(MaliciousPathException.class, + Assertions.assertThrows(MaliciousPathException.class, () -> { ManifestReader.readChecksumFileMap(manifestFile, Paths.get("/foo"), StandardCharsets.UTF_8); }); } @@ -58,7 +73,7 @@ public void testReadFileUrlMaliciousManifestThrowsException() throws Exception{ @Test public void testReadWindowsSpecialDirMaliciousManifestThrowsException() throws Exception{ Path manifestFile = Paths.get(getClass().getClassLoader().getResource("maliciousManifestFile/windowsSpecialDirectoryName.txt").toURI()); - Assertions.assertThrows(InvalidBagitFileFormatException.class, + Assertions.assertThrows(InvalidBagitFileFormatException.class, () -> { ManifestReader.readChecksumFileMap(manifestFile, Paths.get("/foo"), StandardCharsets.UTF_8); }); } } diff --git a/src/test/java/gov/loc/repository/bagit/reader/MetadataReaderTest.java b/src/test/java/nl/knaw/dans/bagit/reader/MetadataReaderTest.java similarity index 77% rename from src/test/java/gov/loc/repository/bagit/reader/MetadataReaderTest.java rename to src/test/java/nl/knaw/dans/bagit/reader/MetadataReaderTest.java index 6f95f7921..9bf2027e9 100644 --- a/src/test/java/gov/loc/repository/bagit/reader/MetadataReaderTest.java +++ b/src/test/java/nl/knaw/dans/bagit/reader/MetadataReaderTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; @@ -11,7 +26,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.PrivateConstructorTest; public class MetadataReaderTest extends PrivateConstructorTest { diff --git a/src/test/java/gov/loc/repository/bagit/reader/TagFileReaderTest.java b/src/test/java/nl/knaw/dans/bagit/reader/TagFileReaderTest.java similarity index 73% rename from src/test/java/gov/loc/repository/bagit/reader/TagFileReaderTest.java rename to src/test/java/nl/knaw/dans/bagit/reader/TagFileReaderTest.java index a2d643a28..90e065d0d 100644 --- a/src/test/java/gov/loc/repository/bagit/reader/TagFileReaderTest.java +++ b/src/test/java/nl/knaw/dans/bagit/reader/TagFileReaderTest.java @@ -1,14 +1,28 @@ -package gov.loc.repository.bagit.reader; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.reader; import java.nio.file.Path; import java.nio.file.Paths; +import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; +import nl.knaw.dans.bagit.exceptions.MaliciousPathException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.exceptions.InvalidBagitFileFormatException; -import gov.loc.repository.bagit.exceptions.MaliciousPathException; - public class TagFileReaderTest { @Test @@ -36,14 +50,14 @@ public void testCreateFileFromManifestWithURISyntax() throws Exception{ @Test public void testBackslashThrowsException() throws Exception{ Path bagRootDir = Paths.get("foo"); - Assertions.assertThrows(InvalidBagitFileFormatException.class, + Assertions.assertThrows(InvalidBagitFileFormatException.class, () -> { TagFileReader.createFileFromManifest(bagRootDir, "data\\bar\\ham.txt"); }); } @Test public void testOutsideDataDirReferenceThrowsException() throws Exception{ Path bagRootDir = Paths.get("foo"); - Assertions.assertThrows(MaliciousPathException.class, + Assertions.assertThrows(MaliciousPathException.class, () -> { TagFileReader.createFileFromManifest(bagRootDir, "/bar/ham.txt"); }); } diff --git a/src/test/java/gov/loc/repository/bagit/util/PathUtilsTest.java b/src/test/java/nl/knaw/dans/bagit/util/PathUtilsTest.java similarity index 83% rename from src/test/java/gov/loc/repository/bagit/util/PathUtilsTest.java rename to src/test/java/nl/knaw/dans/bagit/util/PathUtilsTest.java index 243536f36..571cf9489 100644 --- a/src/test/java/gov/loc/repository/bagit/util/PathUtilsTest.java +++ b/src/test/java/nl/knaw/dans/bagit/util/PathUtilsTest.java @@ -1,17 +1,32 @@ -package gov.loc.repository.bagit.util; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.util; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.nio.file.Path; import java.nio.file.Paths; +import nl.knaw.dans.bagit.domain.Bag; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.TestUtils; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Version; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.TestUtils; +import nl.knaw.dans.bagit.domain.Version; public class PathUtilsTest extends PrivateConstructorTest { diff --git a/src/test/java/gov/loc/repository/bagit/verify/BagVerifierTest.java b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java similarity index 84% rename from src/test/java/gov/loc/repository/bagit/verify/BagVerifierTest.java rename to src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java index 8099091b4..e3b57e35e 100644 --- a/src/test/java/gov/loc/repository/bagit/verify/BagVerifierTest.java +++ b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.File; import java.nio.file.Files; @@ -12,17 +27,17 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.TempFolderTest; -import gov.loc.repository.bagit.TestUtils; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.exceptions.CorruptChecksumException; -import gov.loc.repository.bagit.exceptions.FileNotInManifestException; -import gov.loc.repository.bagit.exceptions.UnsupportedAlgorithmException; -import gov.loc.repository.bagit.exceptions.VerificationException; -import gov.loc.repository.bagit.hash.StandardSupportedAlgorithms; -import gov.loc.repository.bagit.hash.SupportedAlgorithm; -import gov.loc.repository.bagit.reader.BagReader; +import nl.knaw.dans.bagit.TempFolderTest; +import nl.knaw.dans.bagit.TestUtils; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.exceptions.CorruptChecksumException; +import nl.knaw.dans.bagit.exceptions.FileNotInManifestException; +import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; +import nl.knaw.dans.bagit.exceptions.VerificationException; +import nl.knaw.dans.bagit.hash.StandardSupportedAlgorithms; +import nl.knaw.dans.bagit.hash.SupportedAlgorithm; +import nl.knaw.dans.bagit.reader.BagReader; public class BagVerifierTest extends TempFolderTest{ static { diff --git a/src/test/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTaskTest.java b/src/test/java/nl/knaw/dans/bagit/verify/CheckIfFileExistsTaskTest.java similarity index 62% rename from src/test/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTaskTest.java rename to src/test/java/nl/knaw/dans/bagit/verify/CheckIfFileExistsTaskTest.java index 6ea65d417..c929c38e1 100644 --- a/src/test/java/gov/loc/repository/bagit/verify/CheckIfFileExistsTaskTest.java +++ b/src/test/java/nl/knaw/dans/bagit/verify/CheckIfFileExistsTaskTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.nio.file.Path; import java.text.Normalizer; @@ -11,7 +26,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.TempFolderTest; +import nl.knaw.dans.bagit.TempFolderTest; public class CheckIfFileExistsTaskTest extends TempFolderTest { diff --git a/src/test/java/gov/loc/repository/bagit/verify/FileCountAndTotalSizeVistorTest.java b/src/test/java/nl/knaw/dans/bagit/verify/FileCountAndTotalSizeVistorTest.java similarity index 55% rename from src/test/java/gov/loc/repository/bagit/verify/FileCountAndTotalSizeVistorTest.java rename to src/test/java/nl/knaw/dans/bagit/verify/FileCountAndTotalSizeVistorTest.java index 32ba5a729..1283be4e0 100644 --- a/src/test/java/gov/loc/repository/bagit/verify/FileCountAndTotalSizeVistorTest.java +++ b/src/test/java/nl/knaw/dans/bagit/verify/FileCountAndTotalSizeVistorTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.File; import java.io.IOException; @@ -10,7 +25,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.TestUtils; +import nl.knaw.dans.bagit.TestUtils; /** * Tests the ignore of hidden files while walking the file tree. diff --git a/src/test/java/gov/loc/repository/bagit/verify/MandatoryVerifierTest.java b/src/test/java/nl/knaw/dans/bagit/verify/MandatoryVerifierTest.java similarity index 74% rename from src/test/java/gov/loc/repository/bagit/verify/MandatoryVerifierTest.java rename to src/test/java/nl/knaw/dans/bagit/verify/MandatoryVerifierTest.java index 005a5f414..14b9d3a1c 100644 --- a/src/test/java/gov/loc/repository/bagit/verify/MandatoryVerifierTest.java +++ b/src/test/java/nl/knaw/dans/bagit/verify/MandatoryVerifierTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.File; import java.io.IOException; @@ -10,16 +25,16 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.exceptions.FileNotInPayloadDirectoryException; +import nl.knaw.dans.bagit.exceptions.MissingPayloadDirectoryException; +import nl.knaw.dans.bagit.exceptions.MissingPayloadManifestException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.exceptions.FileNotInPayloadDirectoryException; -import gov.loc.repository.bagit.exceptions.MissingBagitFileException; -import gov.loc.repository.bagit.exceptions.MissingPayloadDirectoryException; -import gov.loc.repository.bagit.exceptions.MissingPayloadManifestException; -import gov.loc.repository.bagit.reader.BagReader; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.exceptions.MissingBagitFileException; +import nl.knaw.dans.bagit.reader.BagReader; public class MandatoryVerifierTest extends PrivateConstructorTest { @@ -36,7 +51,7 @@ public void testErrorWhenFetchItemsDontExist() throws Exception{ rootDir = Paths.get(new File("src/test/resources/bad-fetch-bag").toURI()); Bag bag = reader.read(rootDir); - Assertions.assertThrows(FileNotInPayloadDirectoryException.class, + Assertions.assertThrows(FileNotInPayloadDirectoryException.class, () -> { MandatoryVerifier.checkFetchItemsExist(bag.getItemsToFetch(), bag.getRootDir()); }); } @@ -47,7 +62,7 @@ public void testErrorWhenMissingPayloadDirectory() throws Exception{ Path dataDir = createDirectory("data"); deleteDirectory(dataDir); - Assertions.assertThrows(MissingPayloadDirectoryException.class, + Assertions.assertThrows(MissingPayloadDirectoryException.class, () -> { MandatoryVerifier.checkPayloadDirectoryExists(bag); }); } @@ -58,7 +73,7 @@ public void testErrorWhenMissingPayloadManifest() throws Exception{ Path manifestFile = folder.resolve("manifest-md5.txt"); Files.delete(manifestFile); - Assertions.assertThrows(MissingPayloadManifestException.class, + Assertions.assertThrows(MissingPayloadManifestException.class, () -> { MandatoryVerifier.checkIfAtLeastOnePayloadManifestsExist(bag.getRootDir(), bag.getVersion()); }); } diff --git a/src/test/java/gov/loc/repository/bagit/verify/ManifestVerifierTest.java b/src/test/java/nl/knaw/dans/bagit/verify/ManifestVerifierTest.java similarity index 69% rename from src/test/java/gov/loc/repository/bagit/verify/ManifestVerifierTest.java rename to src/test/java/nl/knaw/dans/bagit/verify/ManifestVerifierTest.java index 5d10736bf..736ccdd49 100644 --- a/src/test/java/gov/loc/repository/bagit/verify/ManifestVerifierTest.java +++ b/src/test/java/nl/knaw/dans/bagit/verify/ManifestVerifierTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.File; import java.nio.file.Path; @@ -9,11 +24,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.exceptions.FileNotInManifestException; -import gov.loc.repository.bagit.exceptions.FileNotInPayloadDirectoryException; -import gov.loc.repository.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; -import gov.loc.repository.bagit.reader.BagReader; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.exceptions.FileNotInManifestException; +import nl.knaw.dans.bagit.exceptions.FileNotInPayloadDirectoryException; +import nl.knaw.dans.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.reader.BagReader; public class ManifestVerifierTest { diff --git a/src/test/java/nl/knaw/dans/bagit/verify/MySupportedNameToAlgorithmMapping.java b/src/test/java/nl/knaw/dans/bagit/verify/MySupportedNameToAlgorithmMapping.java new file mode 100644 index 000000000..5b33fdc20 --- /dev/null +++ b/src/test/java/nl/knaw/dans/bagit/verify/MySupportedNameToAlgorithmMapping.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; + +import nl.knaw.dans.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.hash.StandardSupportedAlgorithms; +import nl.knaw.dans.bagit.hash.SupportedAlgorithm; + +public class MySupportedNameToAlgorithmMapping implements BagitAlgorithmNameToSupportedAlgorithmMapping { + + @Override + public SupportedAlgorithm getSupportedAlgorithm(String bagitAlgorithmName) { + if("sha3256".equals(bagitAlgorithmName)){ + return new SHA3256Algorithm(); + } + + return StandardSupportedAlgorithms.valueOf(bagitAlgorithmName.toUpperCase()); + } + +} diff --git a/src/test/java/nl/knaw/dans/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistorTest.java b/src/test/java/nl/knaw/dans/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistorTest.java new file mode 100644 index 000000000..2ff74d6f5 --- /dev/null +++ b/src/test/java/nl/knaw/dans/bagit/verify/PayloadFileExistsInAtLeastOneManifestVistorTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; + +import java.util.HashSet; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import nl.knaw.dans.bagit.TempFolderTest; +import nl.knaw.dans.bagit.exceptions.FileNotInManifestException; + +public class PayloadFileExistsInAtLeastOneManifestVistorTest extends TempFolderTest { + + @Test + public void testFileNotInManifestException() throws Exception{ + + PayloadFileExistsInAtLeastOneManifestVistor sut = new PayloadFileExistsInAtLeastOneManifestVistor(new HashSet<>(), true); + Assertions.assertThrows(FileNotInManifestException.class, + () -> { sut.visitFile(createFile("aNewFile"), null); }); + } +} diff --git a/src/test/java/gov/loc/repository/bagit/verify/QuickVerifierTest.java b/src/test/java/nl/knaw/dans/bagit/verify/QuickVerifierTest.java similarity index 72% rename from src/test/java/gov/loc/repository/bagit/verify/QuickVerifierTest.java rename to src/test/java/nl/knaw/dans/bagit/verify/QuickVerifierTest.java index 90b71897c..de8b170fb 100644 --- a/src/test/java/gov/loc/repository/bagit/verify/QuickVerifierTest.java +++ b/src/test/java/nl/knaw/dans/bagit/verify/QuickVerifierTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.verify; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; import java.io.File; import java.lang.reflect.InvocationTargetException; @@ -8,11 +23,11 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.exceptions.InvalidPayloadOxumException; -import gov.loc.repository.bagit.exceptions.PayloadOxumDoesNotExistException; -import gov.loc.repository.bagit.reader.BagReader; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.exceptions.InvalidPayloadOxumException; +import nl.knaw.dans.bagit.exceptions.PayloadOxumDoesNotExistException; +import nl.knaw.dans.bagit.reader.BagReader; public class QuickVerifierTest extends PrivateConstructorTest { diff --git a/src/test/java/nl/knaw/dans/bagit/verify/SHA3256Algorithm.java b/src/test/java/nl/knaw/dans/bagit/verify/SHA3256Algorithm.java new file mode 100644 index 000000000..51ba3f5ba --- /dev/null +++ b/src/test/java/nl/knaw/dans/bagit/verify/SHA3256Algorithm.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; + +import nl.knaw.dans.bagit.hash.SupportedAlgorithm; + +public class SHA3256Algorithm implements SupportedAlgorithm { + + @Override + public String getMessageDigestName() { + return "SHA3-256"; + } + + @Override + public String getBagitName() { + return "sha3256"; + } + +} diff --git a/src/test/java/gov/loc/repository/bagit/writer/BagWriterTest.java b/src/test/java/nl/knaw/dans/bagit/writer/BagWriterTest.java similarity index 85% rename from src/test/java/gov/loc/repository/bagit/writer/BagWriterTest.java rename to src/test/java/nl/knaw/dans/bagit/writer/BagWriterTest.java index 2b27dcf2e..5ced4be4a 100644 --- a/src/test/java/gov/loc/repository/bagit/writer/BagWriterTest.java +++ b/src/test/java/nl/knaw/dans/bagit/writer/BagWriterTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; @@ -7,15 +22,15 @@ import java.util.Arrays; import java.util.List; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.hash.StandardSupportedAlgorithms; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.creator.BagCreator; -import gov.loc.repository.bagit.domain.Bag; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.hash.StandardSupportedAlgorithms; -import gov.loc.repository.bagit.reader.BagReader; +import nl.knaw.dans.bagit.creator.BagCreator; +import nl.knaw.dans.bagit.reader.BagReader; public class BagWriterTest extends PrivateConstructorTest { diff --git a/src/test/java/gov/loc/repository/bagit/writer/BagitFileWriterTest.java b/src/test/java/nl/knaw/dans/bagit/writer/BagitFileWriterTest.java similarity index 63% rename from src/test/java/gov/loc/repository/bagit/writer/BagitFileWriterTest.java rename to src/test/java/nl/knaw/dans/bagit/writer/BagitFileWriterTest.java index 9a2c9ba71..a29530e40 100644 --- a/src/test/java/gov/loc/repository/bagit/writer/BagitFileWriterTest.java +++ b/src/test/java/nl/knaw/dans/bagit/writer/BagitFileWriterTest.java @@ -1,15 +1,30 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import nl.knaw.dans.bagit.PrivateConstructorTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.Version; +import nl.knaw.dans.bagit.domain.Version; public class BagitFileWriterTest extends PrivateConstructorTest { diff --git a/src/test/java/gov/loc/repository/bagit/writer/FetchWriterTest.java b/src/test/java/nl/knaw/dans/bagit/writer/FetchWriterTest.java similarity index 79% rename from src/test/java/gov/loc/repository/bagit/writer/FetchWriterTest.java rename to src/test/java/nl/knaw/dans/bagit/writer/FetchWriterTest.java index 144bdf479..000eabc2c 100644 --- a/src/test/java/gov/loc/repository/bagit/writer/FetchWriterTest.java +++ b/src/test/java/nl/knaw/dans/bagit/writer/FetchWriterTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.lang.reflect.InvocationTargetException; import java.net.URL; @@ -9,12 +24,11 @@ import java.util.Arrays; import java.util.List; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.domain.FetchItem; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.FetchItem; - public class FetchWriterTest extends PrivateConstructorTest { @Test diff --git a/src/test/java/gov/loc/repository/bagit/writer/ManifestWriterTest.java b/src/test/java/nl/knaw/dans/bagit/writer/ManifestWriterTest.java similarity index 72% rename from src/test/java/gov/loc/repository/bagit/writer/ManifestWriterTest.java rename to src/test/java/nl/knaw/dans/bagit/writer/ManifestWriterTest.java index 2717d94fb..fea405917 100644 --- a/src/test/java/gov/loc/repository/bagit/writer/ManifestWriterTest.java +++ b/src/test/java/nl/knaw/dans/bagit/writer/ManifestWriterTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -10,13 +25,12 @@ import java.util.List; import java.util.Set; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.hash.StandardSupportedAlgorithms; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.hash.StandardSupportedAlgorithms; - public class ManifestWriterTest extends PrivateConstructorTest { @Test diff --git a/src/test/java/gov/loc/repository/bagit/writer/MetadataWriterTest.java b/src/test/java/nl/knaw/dans/bagit/writer/MetadataWriterTest.java similarity index 62% rename from src/test/java/gov/loc/repository/bagit/writer/MetadataWriterTest.java rename to src/test/java/nl/knaw/dans/bagit/writer/MetadataWriterTest.java index f6ee0368f..21a5e080a 100644 --- a/src/test/java/gov/loc/repository/bagit/writer/MetadataWriterTest.java +++ b/src/test/java/nl/knaw/dans/bagit/writer/MetadataWriterTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -6,12 +21,12 @@ import java.nio.file.Files; import java.nio.file.Path; +import nl.knaw.dans.bagit.PrivateConstructorTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.Metadata; -import gov.loc.repository.bagit.domain.Version; +import nl.knaw.dans.bagit.domain.Metadata; +import nl.knaw.dans.bagit.domain.Version; public class MetadataWriterTest extends PrivateConstructorTest { diff --git a/src/test/java/gov/loc/repository/bagit/writer/PayloadWriterTest.java b/src/test/java/nl/knaw/dans/bagit/writer/PayloadWriterTest.java similarity index 74% rename from src/test/java/gov/loc/repository/bagit/writer/PayloadWriterTest.java rename to src/test/java/nl/knaw/dans/bagit/writer/PayloadWriterTest.java index fd23fb768..87f25e6cb 100644 --- a/src/test/java/gov/loc/repository/bagit/writer/PayloadWriterTest.java +++ b/src/test/java/nl/knaw/dans/bagit/writer/PayloadWriterTest.java @@ -1,4 +1,19 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -11,14 +26,13 @@ import java.util.HashSet; import java.util.Set; +import nl.knaw.dans.bagit.PrivateConstructorTest; +import nl.knaw.dans.bagit.domain.FetchItem; +import nl.knaw.dans.bagit.domain.Manifest; +import nl.knaw.dans.bagit.hash.StandardSupportedAlgorithms; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; -import gov.loc.repository.bagit.domain.FetchItem; -import gov.loc.repository.bagit.domain.Manifest; -import gov.loc.repository.bagit.hash.StandardSupportedAlgorithms; - public class PayloadWriterTest extends PrivateConstructorTest { @Test @@ -55,7 +69,7 @@ public void testWritePayloadFilesMinusFetchFiles() throws IOException, URISyntax Assertions.assertFalse(Files.exists(copiedFile) || Files.exists(copiedFile.getParent())); PayloadWriter.writePayloadFiles(payloadManifests, - Arrays.asList(new FetchItem(null, null, Paths.get("data/dir1/test3.txt"))), + Arrays.asList(new FetchItem(null, null, Paths.get("data/dir1/test3.txt"))), outputDir, rootDir.resolve("data")); Assertions.assertFalse(Files.exists(copiedFile) || Files.exists(copiedFile.getParent())); diff --git a/src/test/java/gov/loc/repository/bagit/writer/RelativePathWriterTest.java b/src/test/java/nl/knaw/dans/bagit/writer/RelativePathWriterTest.java similarity index 60% rename from src/test/java/gov/loc/repository/bagit/writer/RelativePathWriterTest.java rename to src/test/java/nl/knaw/dans/bagit/writer/RelativePathWriterTest.java index d9c83dccb..798bc9fcd 100644 --- a/src/test/java/gov/loc/repository/bagit/writer/RelativePathWriterTest.java +++ b/src/test/java/nl/knaw/dans/bagit/writer/RelativePathWriterTest.java @@ -1,14 +1,28 @@ -package gov.loc.repository.bagit.writer; +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.writer; import java.lang.reflect.InvocationTargetException; import java.nio.file.Path; import java.nio.file.Paths; +import nl.knaw.dans.bagit.PrivateConstructorTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import gov.loc.repository.bagit.PrivateConstructorTest; - public class RelativePathWriterTest extends PrivateConstructorTest { @Test public void testClassIsWellDefined() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException{ diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml new file mode 100644 index 000000000..2fa3d828d --- /dev/null +++ b/src/test/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + %-5level %msg%n + + + + + + + + + + + From cadc8114948e3859834478b06553dddccae817ce Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 24 Mar 2023 15:29:09 +0100 Subject: [PATCH 027/104] trigger --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 053f2d867..a8bdc4b0d 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,7 @@ 1.0.0-SNAPSHOT + com.fasterxml.jackson.core From a1e3a482275e860480d444abc5474511208f7c76 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 24 Mar 2023 15:41:44 +0100 Subject: [PATCH 028/104] Updated test coverage badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 75644e5f1..ba4db5c84 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ | | | |---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Build Status | ![Build Status](https://github.com/ericdevries/bagit-java/actions/workflows/build.yml/badge.svg) | -| Metrics | [![Coverage Status](https://coveralls.io/repos/github/LibraryOfCongress/bagit-java/badge.svg?branch=master)](https://coveralls.io/github/LibraryOfCongress/bagit-java?branch=master) [![Github Latest Release Downloads](https://img.shields.io/github/downloads/LibraryOfCongress/bagit-java/latest/total.svg?maxAge=600)]() | +| Test Coverage | ![Coverage Status](https://coveralls.io/repos/github/DANS-KNAW/dans-bagit-lib/badge.svg?branch=master) | | Documentation | [![License](https://img.shields.io/badge/License-Public--Domain-blue.svg?maxAge=31556926)](https://github.com/LibraryOfCongress/bagit-java/blob/master/LICENSE.txt) [![javadoc.io](https://img.shields.io/badge/javadoc.io-latest-blue.svg?maxAge=31556926)](http://www.javadoc.io/doc/gov.loc/bagit) [![Crowdin](https://img.shields.io/badge/Translation-Crowdin-ff69b4.svg?maxAge=600)](https://crowdin.com/project/bagit-java) [![Transifex](https://img.shields.io/badge/Translation-Transifex-ff69b4.svg?maxAge=600)](https://www.transifex.com/acdha/bagit-java/dashboard/) | [//]: # (https://img.shields.io/versioneye/d/java/gov.loc:bagit.svg once it is deployed to maven-central) From 00cc003acef27f8323f461cf9caa1920a69230c6 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 24 Mar 2023 15:51:55 +0100 Subject: [PATCH 029/104] Test caching of Maven cache --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index a8bdc4b0d..053f2d867 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,6 @@ 1.0.0-SNAPSHOT - com.fasterxml.jackson.core From 802a808693c151dc5ecee259574c94b390873b3d Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 24 Mar 2023 16:24:59 +0100 Subject: [PATCH 030/104] Modified license and notice --- LICENSE | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE.txt | 83 ---------------- NOTICE | 9 ++ NOTICE.txt | 22 ----- 4 files changed, 280 insertions(+), 105 deletions(-) create mode 100644 LICENSE delete mode 100644 LICENSE.txt create mode 100644 NOTICE delete mode 100644 NOTICE.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..1d4b6a044 --- /dev/null +++ b/LICENSE @@ -0,0 +1,271 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +-------------------------------------------------------------------- +The license below applies to the https://github.com/LibraryOfCongress/bagit-java project +that the current project was based on: + +License for BAGIT Library (BIL) +------------------------------- +This software is a work of the United States Government and is not subject +to copyright protection in the United States. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR THE UNITED STATES BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +Foreign copyrights may apply. To the extent that foreign copyrights in the +software exist outside the United States, the following terms apply: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +-------------------------------------------------------------------- + +The license below applies to the following bundled dependencies: + * slf4j + + MIT license +------------------------------ + + Copyright (c) 2004-2013 QOS.ch + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index b3953522b..000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,83 +0,0 @@ -==== - Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -==== - -This project is based on https://github.com/LibraryOfCongress/bagit-java, which has the following license conditions: - -License for BAGIT Library (BIL) -------------------------------- -This software is a work of the United States Government and is not subject -to copyright protection in the United States. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR THE UNITED STATES BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - -Foreign copyrights may apply. To the extent that foreign copyrights in the -software exist outside the United States, the following terms apply: - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - --------------------------------------------------------------------- - -The license below applies to the following bundled dependencies: - * slf4j - - MIT license ------------------------------- - - Copyright (c) 2004-2013 QOS.ch - All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..1cb888a97 --- /dev/null +++ b/NOTICE @@ -0,0 +1,9 @@ +Acknowledgements +---------------- +This software was based on https://github.com/LibraryOfCongress/bagit-java. +Please see LICENSE for the licenses that apply to this project. + +This software uses code from the following projects: + * slf4j (http://www.slf4j.org/) + +Please see LICENSE for the licenses that apply to these dependencies. \ No newline at end of file diff --git a/NOTICE.txt b/NOTICE.txt deleted file mode 100644 index 51366d8ae..000000000 --- a/NOTICE.txt +++ /dev/null @@ -1,22 +0,0 @@ -==== - Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -==== - -Acknowledgements ----------------- -This software uses code from the following projects: - * slf4j (http://www.slf4j.org/) - -Please see LICENSE.txt for the licenses that apply to these dependencies. \ No newline at end of file From e90f81ddf77b90d0d0befca78fbda7793d455051 Mon Sep 17 00:00:00 2001 From: Eric de Vries Date: Wed, 29 Mar 2023 16:26:19 +0200 Subject: [PATCH 031/104] Dd 1328 (#5) * Add new cache for site running * Add branch for test purposes * Update workflow to trigger action * Remove branch --- .github/workflows/build.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index adfd54964..031aa60d9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,6 @@ on: push: branches: - master - - DD-1316 pull_request: branches: - master @@ -63,13 +62,11 @@ jobs: uses: actions/cache@v3 with: path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-site-report restore-keys: | ${{ runner.os }}-maven- - - name: Generate reports and publish reports run: mvn -B clean test site jacoco:report coveralls:report --file pom.xml -DrepoToken=${{ secrets.COVERALLS_REPO_TOKEN }} -Djarsigner.skip=true - - name: Generate pages uses: JamesIves/github-pages-deploy-action@v4 with: From c400063b54312d8916561c954bc0ae2698589c21 Mon Sep 17 00:00:00 2001 From: Eric de Vries Date: Mon, 3 Apr 2023 14:21:09 +0200 Subject: [PATCH 032/104] Dd 1327 (#6) * Docs * Add mkdocs script * Set up separate pages, add separate docs build * Updated names * Remove report step from build, add it to docs * Remove branch * Updated links --- .github/workflows/build.yml | 25 --- .github/workflows/docs.yml | 52 ++++++ .github/workflows/mkdocs/mkdocs.sh | 27 +++ .github/workflows/mkdocs/requirements.txt | 3 + .gitignore | 1 + README.md | 202 +--------------------- docs/api.md | 6 + docs/dev.md | 37 ++++ docs/getting-started.md | 126 ++++++++++++++ docs/index.md | 49 ++++++ docs/javadoc.md | 6 + mkdocs.yml | 49 ++++++ pom.xml | 59 ++++--- 13 files changed, 398 insertions(+), 244 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100755 .github/workflows/mkdocs/mkdocs.sh create mode 100644 .github/workflows/mkdocs/requirements.txt create mode 100644 docs/api.md create mode 100644 docs/dev.md create mode 100644 docs/getting-started.md create mode 100755 docs/index.md create mode 100644 docs/javadoc.md create mode 100644 mkdocs.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 031aa60d9..d314f3b08 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,28 +47,3 @@ jobs: ${{ runner.os }}-maven- - name: Build with Maven run: mvn -B clean package --file pom.xml -Djarsigner.skip=true - - report: - runs-on: ubuntu-latest - needs: build - steps: - - uses: actions/checkout@v3 - - name: Set up JDK 11 - uses: actions/setup-java@v3 - with: - distribution: adopt-openj9 - java-version: 11 - - name: Cache local Maven repository - uses: actions/cache@v3 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-site-report - restore-keys: | - ${{ runner.os }}-maven- - - name: Generate reports and publish reports - run: mvn -B clean test site jacoco:report coveralls:report --file pom.xml -DrepoToken=${{ secrets.COVERALLS_REPO_TOKEN }} -Djarsigner.skip=true - - name: Generate pages - uses: JamesIves/github-pages-deploy-action@v4 - with: - folder: target/site - clean: true diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..d62e5c8df --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,52 @@ +name: Build documentation site + +on: + push: + branches: + - master + - DD-1327 + +jobs: + mkdocs: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: 3.9 + + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + distribution: adopt-openj9 + java-version: 11 + + - name: Cache local Maven repository + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-docs-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven-docs- + + - name: Generate maven site + run: mvn -B clean test site jacoco:report coveralls:report --file pom.xml -DrepoToken=${{ secrets.COVERALLS_REPO_TOKEN }} -Djarsigner.skip=true + + - name: Copy javadocs to site folder + run: cp -r target/site/ docs/mvnsite/ + + - name: Run script + run: bash .github/workflows/mkdocs/mkdocs.sh + shell: bash diff --git a/.github/workflows/mkdocs/mkdocs.sh b/.github/workflows/mkdocs/mkdocs.sh new file mode 100755 index 000000000..a0617caa9 --- /dev/null +++ b/.github/workflows/mkdocs/mkdocs.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# +# Copyright (C) 2016 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +REMOTE="https://@github.com/${GITHUB_REPOSITORY}" +git remote set-url origin ${REMOTE} + +pip install -r .github/workflows/mkdocs/requirements.txt + +echo "START deploying docs to GitHub pages..." +mkdocs gh-deploy --force +echo "DONE deploying docs to GitHub pages." diff --git a/.github/workflows/mkdocs/requirements.txt b/.github/workflows/mkdocs/requirements.txt new file mode 100644 index 000000000..557ea036d --- /dev/null +++ b/.github/workflows/mkdocs/requirements.txt @@ -0,0 +1,3 @@ +mkdocs==1.3.0 +pyyaml==6.0 +mkdocs-markdownextradata-plugin==0.2.5 diff --git a/.gitignore b/.gitignore index d25af074d..df7549894 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ release.properties site target/ venv/ +docs/mvnsite/ diff --git a/README.md b/README.md index ba4db5c84..f705ab45c 100644 --- a/README.md +++ b/README.md @@ -1,198 +1,10 @@ -# BagIt Library (BIL) +dans-bagit-lib +============== +![Build Status](https://github.com/DANS-KNAW/dans-bagit-lib/actions/workflows/build.yml/badge.svg) +![Site Status](https://github.com/DANS-KNAW/dans-bagit-lib/actions/workflows/docs.yml/badge.svg) +![Coverage Status](https://coveralls.io/repos/github/DANS-KNAW/dans-bagit-lib/badge.svg?branch=master) | -| | | -|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Build Status | ![Build Status](https://github.com/ericdevries/bagit-java/actions/workflows/build.yml/badge.svg) | -| Test Coverage | ![Coverage Status](https://coveralls.io/repos/github/DANS-KNAW/dans-bagit-lib/badge.svg?branch=master) | -| Documentation | [![License](https://img.shields.io/badge/License-Public--Domain-blue.svg?maxAge=31556926)](https://github.com/LibraryOfCongress/bagit-java/blob/master/LICENSE.txt) [![javadoc.io](https://img.shields.io/badge/javadoc.io-latest-blue.svg?maxAge=31556926)](http://www.javadoc.io/doc/gov.loc/bagit) [![Crowdin](https://img.shields.io/badge/Translation-Crowdin-ff69b4.svg?maxAge=600)](https://crowdin.com/project/bagit-java) [![Transifex](https://img.shields.io/badge/Translation-Transifex-ff69b4.svg?maxAge=600)](https://www.transifex.com/acdha/bagit-java/dashboard/) | +Java library for the [BagIt format](https://datatracker.ietf.org/doc/html/rfc8493) -[//]: # (https://img.shields.io/versioneye/d/java/gov.loc:bagit.svg once it is deployed to maven-central) - -[//]: # (see https://github.com/jirutka/maven-badges once you have deployed past 5.0-BETA on maven central so that it will automatically update) - -[//]: # (see https://github.com/moznion/javadocio-badges for automatic javadoc) - -## Description - -The BAGIT LIBRARY is a software library intended to support the creation, -manipulation, and validation of bags. Its current version is 0.97. It is version aware with the earliest -supported version being 0.93. - -## Requirements - -* Java 11 -* Maven (for development only) - -## Support - -1. The Digital Curation Google Group (https://groups.google.com/d/forum/digital-curation) is an open discussion list that reaches many of the contributors to - and users of this open-source project -2. If you have found a bug please create a new issue on [the issues page](https://github.com/LibraryOfCongress/bagit-java/issues/new) -3. If you would like to contribute, please submit a [pull request](https://help.github.com/articles/creating-a-pull-request/) - -## Major differences between version 5 and 4.* - -##### Command Line Interface - -The 5.x versions do not include a command-line interface. -Users who need a command-line utility can continue to use the latest 4.x release -([download 4.12.3](https://github.com/LibraryOfCongress/bagit-java/releases/download/v4.12.3/bagit-v4.12.3.zip) -or switch to an alternative implementation such as -[bagit-python](https://github.com/LibraryOfCongress/bagit-python) or -[BagIt for Ruby](https://github.com/tipr/bagit). - -##### Serialization - -Starting with the 5.x versions bagit-java no longer supports directly -serializing a bag to an archive file. The examples show how to implement a -custom serializer for the -[zip](https://github.com/LibraryOfCongress/bagit-java/blob/master/src/test/java/gov/loc/repository/bagit/examples/serialization/CreateZipBagExample.java) -and -[tar](https://github.com/LibraryOfCongress/bagit-java/blob/master/src/test/java/gov/loc/repository/bagit/examples/serialization/CreateTarBagExample.java) -formats. - -##### Fetching - -The 5.x versions do not include a core `fetch.txt` implementation. If you need -this functionality, the -[`FetchHttpFileExample` example](https://github.com/LibraryOfCongress/bagit-java/blob/master/src/test/java/gov/loc/repository/bagit/examples/fetching/FetchHttpFileExample.java) -demonstrates how you can implement this feature with your additional application -and workflow requirements. - -##### Internationalization - -All logging and error messages have been put into a [ResourceBundle](https://docs.oracle.com/javase/7/docs/api/java/util/ResourceBundle.html). -This allows for all the messages to be translated to multiple languages and automatically used during runtime. -If you would like to contribute to translations please visit https://www.transifex.com/acdha/bagit-java/dashboard/ or https://crowdin.com/project/bagit-java. - -##### New Interfaces - -The 5.x version is a complete rewrite of the bagit-java library which attempts -to follow modern Java practices and will require some changes to existing code: - -### Examples of using the new bagit-java library - -##### Create a bag from a folder using version 0.97 - -```java -Path folder=Paths.get("FolderYouWantToBag"); - StandardSupportedAlgorithms algorithm=StandardSupportedAlgorithms.MD5; - boolean includeHiddenFiles=false; - Bag bag=BagCreator.bagInPlace(folder,Arrays.asList(algorithm),includeHiddenFiles); -``` - -##### Read an existing bag (version 0.93 and higher) - -```java -Path rootDir=Paths.get("RootDirectoryOfExistingBag"); - BagReader reader=new BagReader(); - Bag bag=reader.read(rootDir); -``` - -##### Write a Bag object to disk - -```java -Path outputDir=Paths.get("WhereYouWantToWriteTheBagTo"); - BagWriter.write(bag,outputDir); //where bag is a Bag object -``` - -##### Verify Complete - -```java -boolean ignoreHiddenFiles=true; - BagVerifier verifier=new BagVerifier(); - verifier.isComplete(bag,ignoreHiddenFiles); -``` - -##### Verify Valid - -```java -boolean ignoreHiddenFiles=true; - BagVerifier verifier=new BagVerifier(); - verifier.isValid(bag,ignoreHiddenFiles); -``` - -##### Quickly verify by payload-oxum - -```java -boolean ignoreHiddenFiles=true; - - if(BagVerifier.canQuickVerify(bag)){ - BagVerifier.quicklyVerify(bag,ignoreHiddenFiles); - } -``` - -##### Add other checksum algorithms - -You only need to implement 2 interfaces: - -```java -public class MyNewSupportedAlgorithm implements SupportedAlgorithm { - @Override - public String getMessageDigestName() { - return "SHA3-256"; - } - - @Override - public String getBagitName() { - return "sha3256"; - } -} - -public class MyNewNameMapping implements BagitAlgorithmNameToSupportedAlgorithmMapping { - @Override - public SupportedAlgorithm getMessageDigestName(String bagitAlgorithmName) { - if ("sha3256".equals(bagitAlgorithmName)) { - return new MyNewSupportedAlgorithm(); - } - - return StandardSupportedAlgorithms.valueOf(bagitAlgorithmName.toUpperCase()); - } -} -``` - -and then add the implemented `BagitAlgorithmNameToSupportedAlgorithmMapping` -class to your `BagReader` or `bagVerifier` object before using their methods. - -#### Check for potential problems - -The BagIt format is extremely flexible and allows for some conditions which are -technically allowed but should be avoided to minimize confusion and maximize -portability. The `BagLinter` class allows you to easily check a bag for -warnings: - -```java -Path rootDir=Paths.get("RootDirectoryOfExistingBag"); - BagLinter linter=new BagLinter(); - List warnings=linter.lintBag(rootDir,Collections.emptyList()); -``` - -You can provide a list of specific warnings to ignore: - -```java -dependencycheckth rootDir=Paths.get("RootDirectoryOfExistingBag"); - BagLinter linter=new BagLinter(); - List warnings=linter.lintBag(rootDir,Arrays.asList(BagitWarning.OLD_BAGIT_VERSION); -``` - -## Developing Bagit-Java - -Bagit-Java uses Maven for its build system. - -##### Running tests and code quality checks - -Inside the bagit-java root directory, run `mvn verify`. - -##### Uploading to maven central - -1. Follow their guides -1. http://central.sonatype.org/pages/releasing-the-deployment.html -2. https://issues.sonatype.org/secure/Dashboard.jspa -2. Once you have access, to create an official release and upload it you should specify the version by running `./gradlew -Pversion= uploadArchives` -1. *Don't forget to tag the repository!* - -### Roadmap for this library - -* Fix bugs/issues reported with new library (on going) -* Translate to various languages (on going) +For more documentation, see [the project website](https://dans-knaw.github.io/dans-bagit-lib/) \ No newline at end of file diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 000000000..0e56014f8 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,6 @@ +Maven project info +======== + +Open the [Maven project info]{:target=_blank:} in a new tab. + +[Maven project info]: mvnsite/index.html diff --git a/docs/dev.md b/docs/dev.md new file mode 100644 index 000000000..396f7b526 --- /dev/null +++ b/docs/dev.md @@ -0,0 +1,37 @@ +Development +=========== +This page contains information for developers about how to contribute to this project. + + +Requirements +------------ + +* Java 11 +* Maven + +Running tests and code quality checks +------------------------------------- + +Inside the bagit-java root directory, run `mvn verify`. + +General +------- + +* When extending the library follow the established patterns, to keep it easy to understand for any + new user. + +JavaDoc +------- +Since this is a library, the JavaDocs should be relatively extensive, although there is no need to go +overboard with this. At a minimum: + +* The JavaDocs must be generated successfully. As of today this is a standard part of the build; the build + will fail if doc generation fails. +* Every API endpoint method needs JavaDocs that documents the parameters and exceptions and has + a deep link to the Dataverse docs for the end-point that is called. This must be a link to target + "_blank". See existing code for examples. +* If an example program for the end-point method is available (which _should_ be the case) also add + a deep link to (the latest commit of) the example code. +* [Run the documentation site locally](https://dans-knaw.github.io/dans-datastation-architecture/dev/#documentation-with-mkdocs){:target=_blank} + to check how it renders. + diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 000000000..09c24f77c --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,126 @@ +Getting started +=============== + +### Create a bag from a folder using version 0.97 + +```java +Path folder=Paths.get("FolderYouWantToBag"); + StandardSupportedAlgorithms algorithm=StandardSupportedAlgorithms.MD5; + boolean includeHiddenFiles=false; + Bag bag=BagCreator.bagInPlace(folder,Arrays.asList(algorithm),includeHiddenFiles); +``` + +### Read an existing bag (version 0.93 and higher) + +```java +Path rootDir=Paths.get("RootDirectoryOfExistingBag"); + BagReader reader=new BagReader(); + Bag bag=reader.read(rootDir); +``` + +### Write a Bag object to disk + +```java +Path outputDir=Paths.get("WhereYouWantToWriteTheBagTo"); + BagWriter.write(bag,outputDir); //where bag is a Bag object +``` + +### Verify Complete + +```java +boolean ignoreHiddenFiles=true; + BagVerifier verifier=new BagVerifier(); + verifier.isComplete(bag,ignoreHiddenFiles); +``` + +### Verify Valid + +```java +boolean ignoreHiddenFiles=true; + BagVerifier verifier=new BagVerifier(); + verifier.isValid(bag,ignoreHiddenFiles); +``` + +### Quickly verify by payload-oxum + +```java +boolean ignoreHiddenFiles=true; + if(BagVerifier.canQuickVerify(bag)){ + BagVerifier.quicklyVerify(bag,ignoreHiddenFiles); + } +``` + +### Add other checksum algorithms + +You only need to implement 2 interfaces: + +```java +public class MyNewSupportedAlgorithm implements SupportedAlgorithm { + @Override + public String getMessageDigestName() { + return "SHA3-256"; + } + + @Override + public String getBagitName() { + return "sha3256"; + } +} + +public class MyNewNameMapping implements BagitAlgorithmNameToSupportedAlgorithmMapping { + @Override + public SupportedAlgorithm getMessageDigestName(String bagitAlgorithmName) { + if ("sha3256".equals(bagitAlgorithmName)) { + return new MyNewSupportedAlgorithm(); + } + + return StandardSupportedAlgorithms.valueOf(bagitAlgorithmName.toUpperCase()); + } +} +``` + +and then add the implemented `BagitAlgorithmNameToSupportedAlgorithmMapping` +class to your `BagReader` or `bagVerifier` object before using their methods. + +### Check for potential problems + +The BagIt format is extremely flexible and allows for some conditions which are +technically allowed but should be avoided to minimize confusion and maximize +portability. The `BagLinter` class allows you to easily check a bag for +warnings: + +```java +Path rootDir=Paths.get("RootDirectoryOfExistingBag"); + BagLinter linter=new BagLinter(); + List warnings=linter.lintBag(rootDir,Collections.emptyList()); +``` + +You can provide a list of specific warnings to ignore: + +```java +Path rootDir=Paths.get("RootDirectoryOfExistingBag"); + BagLinter linter=new BagLinter(); + List warnings=linter.lintBag(rootDir,Arrays.asList(BagitWarning.OLD_BAGIT_VERSION); +``` + +### Serialization + +The dans-bagit-lib does not support directly +serializing a bag to an archive file. The examples show how to implement a +custom serializer for the +[zip](https://github.com/DANS-KNAW/dans-bagit-lib/blob/master/src/test/java/nl/knaw/dans/bagit/examples/serialization/CreateZipBagExample.java){:target=_blank:} +and +[tar](https://github.com/DANS-KNAW/dans-bagit-lib/blob/master/src/test/java/nl/knaw/dans/bagit/examples/serialization/CreateTarBagExample.java){:target=_blank:} +formats. + +### Fetching + +If you need `fetch.txt` functionality, the +[`FetchHttpFileExample` example](https://github.com/DANS-KNAW/dans-bagit-lib/blob/master/src/test/java/nl/knaw/dans/bagit/examples/fetching/FetchHttpFileExample.java){:target=_blank:} +demonstrates how you can implement this feature with your additional application +and workflow requirements. + +### Internationalization + +All logging and error messages have been put into a [ResourceBundle](https://docs.oracle.com/javase/7/docs/api/java/util/ResourceBundle.html){:target=_blank:}. +This allows for all the messages to be translated to multiple languages and automatically used during runtime. diff --git a/docs/index.md b/docs/index.md new file mode 100755 index 000000000..4ea194e31 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,49 @@ +MANUAL +====== +Library with classes and functions for working with the BagIt format + +DESCRIPTION +----------- +BagIt is a set of hierarchical file layout conventions designed to +support storage and transfer of arbitrary digital content. A "bag" +consists of a directory containing the payload files and other +accompanying metadata files known as "tag" files. The "tags" are +metadata files intended to facilitate and document the storage and +transfer of the bag. Processing a bag does not require any +understanding of the payload file contents, and the payload files can +be accessed without processing the BagIt metadata. + +This BagIt library is a software library intended to support the creation, +manipulation, and validation of bags. Its current version is 0.97. It is version aware with the earliest +supported version being 0.93. + +See: {:target=_blank}. + +This library was first developed by the [LibraryOfCongress](https://github.com/LibraryOfCongress/bagit-java/){:target=_blank:} and +forked by DANS-KNAW. + +INSTALLATION +------------ + +To use this library in a Maven-based project: + +1. Include in your `pom.xml` a declaration for the DANS maven repository: + + + + + DANS + + true + + https://maven.dans.knaw.nl/releases/ + + + +2. Include a dependency on this library. + + + nl.knaw.dans + dans-bagit-lib + {version} + diff --git a/docs/javadoc.md b/docs/javadoc.md new file mode 100644 index 000000000..398ca46f2 --- /dev/null +++ b/docs/javadoc.md @@ -0,0 +1,6 @@ +JavaDocs +======== + +Open the [JavaDoc]{:target=_blank:} in a new tab. + +[JavaDoc]: mvnsite/apidocs/index.html diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..daa3a11e6 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,49 @@ +# +# Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +site_name: dans-bagit-lib +theme: + name: readthedocs + +repo_name: DANS-KNAW/dans-bagit-lib +repo_url: https://github.com/DANS-KNAW/dans-bagit-lib + +nav: + - Manual: index.md + - Getting started: getting-started.md + - JavaDoc: javadoc.md + - Maven project info: api.md + - Development: dev.md + +extra: + deposit_directory: https://dans-knaw.github.io/dans-datastation-architecture/deposit-directory/ + dans_bagit_profile: https://dans-knaw.github.io/dans-bagit-profile/versions/1.0.0/ + +plugins: + - markdownextradata + - search + +markdown_extensions: + - attr_list + - admonition + - codehilite: + guess_lang: False + use_pygments: False + - def_list + - footnotes + - meta + - toc: + permalink: true diff --git a/pom.xml b/pom.xml index 053f2d867..547d74127 100644 --- a/pom.xml +++ b/pom.xml @@ -92,6 +92,7 @@ bagit-conformance-suite/** + site/** @@ -202,30 +203,30 @@ maven-release-plugin - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -275,6 +276,16 @@ spotbugs-maven-plugin 4.7.2.1 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.5.0 + + private + true + + From d667e3e0830996f74bc453e58792814557fe8408 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Sun, 23 Apr 2023 11:08:19 +0200 Subject: [PATCH 033/104] Added codecov --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 053f2d867..d96f4e4e8 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ nl.knaw.dans dd-parent - 0.16.0 + 0.19.0 dans-bagit-lib From 976401052c286dad2c2fd75a32a93ff2204d6012 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Sun, 23 Apr 2023 11:09:50 +0200 Subject: [PATCH 034/104] Added codecov --- .github/workflows/coverage.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 000000000..2b98c4108 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,18 @@ +name: Codecov +on: [push, pull_request] +jobs: + run: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Install dependencies + run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V + - name: Run tests and collect coverage + run: mvn -B test + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 From e42d4efffd906a23ece5b7fc2a46fbb75b574fd8 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 24 Apr 2023 13:35:47 +0200 Subject: [PATCH 035/104] Switched to Codecov --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f705ab45c..fda172ca1 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ dans-bagit-lib ============== ![Build Status](https://github.com/DANS-KNAW/dans-bagit-lib/actions/workflows/build.yml/badge.svg) +[![codecov](https://codecov.io/gh/DANS-KNAW/dans-bagit-lib/branch/master/graph/badge.svg)](https://codecov.io/gh/DANS-KNAW/dans-bagit-lib) ![Site Status](https://github.com/DANS-KNAW/dans-bagit-lib/actions/workflows/docs.yml/badge.svg) -![Coverage Status](https://coveralls.io/repos/github/DANS-KNAW/dans-bagit-lib/badge.svg?branch=master) | Java library for the [BagIt format](https://datatracker.ietf.org/doc/html/rfc8493) -For more documentation, see [the project website](https://dans-knaw.github.io/dans-bagit-lib/) \ No newline at end of file +For more documentation, see [the project website](https://dans-knaw.github.io/dans-bagit-lib/) From cec18f3e5511ac84e27cab8088710ad7432e70e8 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Tue, 25 Apr 2023 11:22:26 +0200 Subject: [PATCH 036/104] Rename run -> codecov in coverage.yml GitHub action --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 2b98c4108..29a0162a4 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,7 +1,7 @@ name: Codecov on: [push, pull_request] jobs: - run: + codecov: runs-on: ubuntu-latest steps: - name: Checkout From 343a795bb44d1e9007f051a6ee0b5e90d5dee37e Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Tue, 25 Apr 2023 11:34:43 +0200 Subject: [PATCH 037/104] Create codeql.yml --- .github/workflows/codeql.yml | 76 ++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..2dacf54d5 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,76 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '20 13 * * 3' + +jobs: + analyze: + name: Analyze + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" From fbc0d120277166221a977fa9c261caa255c82d3a Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 14 Aug 2023 13:16:52 +0200 Subject: [PATCH 038/104] Upgraded parent pom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c5b52a9f3..6d035ebb3 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ nl.knaw.dans dd-parent - 0.19.0 + 0.21.0 dans-bagit-lib From bed261b047c513316813b011473421c285163782 Mon Sep 17 00:00:00 2001 From: Ali Sheikhi Date: Thu, 19 Oct 2023 11:55:23 +0200 Subject: [PATCH 039/104] DD-1381 Upgrade microservices to DropWizard 3 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6d035ebb3..03aee22e1 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ nl.knaw.dans dd-parent - 0.21.0 + 0.23.0 dans-bagit-lib @@ -59,7 +59,7 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on org.kamranzafar From d629efbfb894ffc6e8a30cca81c1efb4b10969c0 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 23 Oct 2023 15:30:30 +0200 Subject: [PATCH 040/104] [maven-release-plugin] prepare release v1.0.0 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 03aee22e1..1ca1d4b89 100644 --- a/pom.xml +++ b/pom.xml @@ -16,8 +16,7 @@ limitations under the License. --> - + 4.0.0 @@ -27,7 +26,7 @@ dans-bagit-lib - 1.0.0-SNAPSHOT + 1.0.0 bagit https://github.com/DANS-KNAW/dans-bagit-lib @@ -36,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - 1.0.0-SNAPSHOT + v1.0.0 From 03a65499960b93c4f9d3d64808fb87401ef3a7f7 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 23 Oct 2023 15:30:34 +0200 Subject: [PATCH 041/104] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1ca1d4b89..6edb77a8b 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.0.0 + 1.0.1-SNAPSHOT bagit https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - v1.0.0 + 1.0.0-SNAPSHOT From 3786b1017e6cf1da4f53e33324fc1ae3b59085e2 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Thu, 21 Dec 2023 11:44:53 +0100 Subject: [PATCH 042/104] Upgrade to parent 0.25.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6edb77a8b..72b2ca302 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ nl.knaw.dans dd-parent - 0.23.0 + 0.25.0 dans-bagit-lib From 3975528d06801b098b7e18bac9ad3993c2eafc41 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Wed, 10 Jan 2024 10:10:38 +0100 Subject: [PATCH 043/104] Upgrade parent pom to 0.26.0 and switching to Java 17 for compilation. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 72b2ca302..d7e9b9da1 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ nl.knaw.dans dd-parent - 0.25.0 + 0.26.0 dans-bagit-lib From b233a1a23273b5cf253108f54bdf729e1a5df148 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 15 Jan 2024 17:48:15 +0100 Subject: [PATCH 044/104] Updated PR template to refer to core-systems instead of dataversedans --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a978bcc90..255c3ac45 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -12,4 +12,4 @@ Fixes DD- # Notify -@DANS-KNAW/dataversedans +@DANS-KNAW/core-systems From 09fe96b90fd811674e68de06409e7ea27092fcc6 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Tue, 30 Jan 2024 14:33:02 +0100 Subject: [PATCH 045/104] Upgrade GitHub actions --- .github/workflows/build.yml | 12 ++++++------ .github/workflows/codeql.yml | 10 ++++++++-- .github/workflows/coverage.yml | 5 +++-- .github/workflows/docs.yml | 11 +++++------ 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d314f3b08..e90a0e197 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,14 +12,14 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: adopt-openj9 java-version: 11 - name: Cache local Maven repository - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} @@ -32,14 +32,14 @@ jobs: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: adopt-openj9 java-version: 11 - name: Cache local Maven repository - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2dacf54d5..2bcbb0f20 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -40,9 +40,15 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - # Initializes the CodeQL tools for scanning. + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'adopt' + + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 29a0162a4..0e321402c 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -5,11 +5,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: java-version: 11 + distribution: 'adopt' - name: Install dependencies run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V - name: Run tests and collect coverage diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d62e5c8df..d80b38f6b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,22 +4,21 @@ on: push: branches: - master - - DD-1327 jobs: mkdocs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: Cache pip - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} @@ -28,13 +27,13 @@ jobs: ${{ runner.os }}- - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: adopt-openj9 java-version: 11 - name: Cache local Maven repository - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-docs-${{ hashFiles('**/pom.xml') }} From 1d3997b1710ff64a37fbef3ce46579d17f0ccabc Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Tue, 30 Jan 2024 14:56:20 +0100 Subject: [PATCH 046/104] Upgrade Github actions to Java 17 --- .github/workflows/build.yml | 8 ++++---- .github/workflows/codeql.yml | 4 ++-- .github/workflows/coverage.yml | 4 ++-- .github/workflows/docs.yml | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e90a0e197..fec5b265d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,11 +13,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: distribution: adopt-openj9 - java-version: 11 + java-version: 17 - name: Cache local Maven repository uses: actions/cache@v4 with: @@ -33,11 +33,11 @@ jobs: needs: test steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: distribution: adopt-openj9 - java-version: 11 + java-version: 17 - name: Cache local Maven repository uses: actions/cache@v4 with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2bcbb0f20..ca01bab95 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -42,10 +42,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: '11' + java-version: '17' distribution: 'adopt' # Initializes the CodeQL tools for scanning. diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 0e321402c..df2e30c34 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -6,10 +6,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 17 distribution: 'adopt' - name: Install dependencies run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d80b38f6b..b1b88ca92 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -26,11 +26,11 @@ jobs: ${{ runner.os }}-pip- ${{ runner.os }}- - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: distribution: adopt-openj9 - java-version: 11 + java-version: 17 - name: Cache local Maven repository uses: actions/cache@v4 From 31a8158550bda97214fcfc42d1cedfb90cafbb57 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Thu, 29 Feb 2024 17:47:06 +0100 Subject: [PATCH 047/104] upgrade parent --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d7e9b9da1..61e985e0c 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ nl.knaw.dans dd-parent - 0.26.0 + 1.1.0-SNAPSHOT dans-bagit-lib From 5f60be2ab79d33ab568e17e56a0aa2b145512da2 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 1 Mar 2024 12:51:08 +0100 Subject: [PATCH 048/104] upgraded parent --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 61e985e0c..948974787 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ nl.knaw.dans dd-parent - 1.1.0-SNAPSHOT + 1.2.0 dans-bagit-lib From 1cc90534d866e888d36a33fda7c52b5766190e94 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 8 Mar 2024 13:44:32 +0100 Subject: [PATCH 049/104] Corrected name element in POM. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 948974787..2006c3ef2 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ dans-bagit-lib 1.0.1-SNAPSHOT - bagit + DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib The BAGIT LIBRARY is a software library intended to support the creation, manipulation, and validation of bags. Its current version is 0.97. It is version aware with the earliest supported version being 0.93. 2023 From e913e32218e49fb135d52ea7d17278a50e604da8 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Thu, 21 Mar 2024 12:02:01 +0100 Subject: [PATCH 050/104] Update GitHub Actions. --- .github/workflows/build.yml | 49 +++++++++---------------------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fec5b265d..06a827b80 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,41 +9,16 @@ on: - master jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: adopt-openj9 - java-version: 17 - - name: Cache local Maven repository - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - - name: Run tests - run: mvn -B clean test --file pom.xml + build: + runs-on: ubuntu-latest - build: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: adopt-openj9 - java-version: 17 - - name: Cache local Maven repository - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven- - - name: Build with Maven - run: mvn -B clean package --file pom.xml -Djarsigner.skip=true + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: 17 + distribution: 'adopt' + cache: 'maven' + - name: Build with Maven + run: mvn -B clean package --file pom.xml From d2ebebcf5f0efffc6b38291407ee1f917cc33ffb Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Thu, 21 Mar 2024 13:25:06 +0100 Subject: [PATCH 051/104] Update github actions --- .github/workflows/codeql.yml | 9 +++++---- .github/workflows/coverage.yml | 1 + .github/workflows/docs.yml | 19 ++----------------- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ca01bab95..c5b7a575b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,7 +23,7 @@ on: jobs: analyze: name: Analyze - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + runs-on: ubuntu-latest permissions: actions: read contents: read @@ -47,10 +47,11 @@ jobs: with: java-version: '17' distribution: 'adopt' + cache: 'maven' # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -64,7 +65,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -77,6 +78,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index df2e30c34..c2dd0520d 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -11,6 +11,7 @@ jobs: with: java-version: 17 distribution: 'adopt' + cache: 'maven' - name: Install dependencies run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V - name: Run tests and collect coverage diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b1b88ca92..ada8f535f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,29 +16,14 @@ jobs: uses: actions/setup-python@v5 with: python-version: 3.9 - - - name: Cache pip - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - ${{ runner.os }}- + cache: 'pip' - name: Set up JDK 17 uses: actions/setup-java@v4 with: distribution: adopt-openj9 java-version: 17 - - - name: Cache local Maven repository - uses: actions/cache@v4 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-docs-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven-docs- + cache: 'maven' - name: Generate maven site run: mvn -B clean test site jacoco:report coveralls:report --file pom.xml -DrepoToken=${{ secrets.COVERALLS_REPO_TOKEN }} -Djarsigner.skip=true From a8de48528f8929f317124c21373b6e644a598b11 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Thu, 21 Mar 2024 14:25:36 +0100 Subject: [PATCH 052/104] Update github actions --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ada8f535f..d7eae048b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -21,7 +21,7 @@ jobs: - name: Set up JDK 17 uses: actions/setup-java@v4 with: - distribution: adopt-openj9 + distribution: adopt java-version: 17 cache: 'maven' From 302a13c5958cdd3e5982c4b34d0a9ef1f5ecc087 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 22 Mar 2024 11:16:06 +0100 Subject: [PATCH 053/104] upgraded parent --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2006c3ef2..138cd93ed 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ nl.knaw.dans dd-parent - 1.2.0 + 1.3.1 dans-bagit-lib From 6ff02606979e8044bbf3af7013ece3d7b440ec92 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Tue, 18 Jun 2024 12:56:38 +0200 Subject: [PATCH 054/104] Update parent pom. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 138cd93ed..0ec46bbf9 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ nl.knaw.dans dd-parent - 1.3.1 + 1.5.1 dans-bagit-lib From c50758f13820b322ee61f21f68e169c5db720b46 Mon Sep 17 00:00:00 2001 From: jo-pol Date: Thu, 27 Jun 2024 14:40:28 +0200 Subject: [PATCH 055/104] stick to java8 --- pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pom.xml b/pom.xml index 138cd93ed..d0cc7b748 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,10 @@ 1.0.0-SNAPSHOT + + 1.8 + + com.fasterxml.jackson.core From c8e65d2f3d3a036c4342a4312bde54bc6ca373a3 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Thu, 27 Jun 2024 15:27:09 +0200 Subject: [PATCH 056/104] [maven-release-plugin] prepare release v1.2.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 62a815c27..6e12a97e3 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.0.1-SNAPSHOT + 1.2.0 DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - 1.0.0-SNAPSHOT + v1.2.0 From 4fb97dc135f8e6a7ba3a47604ff01a6bf95baad0 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Thu, 27 Jun 2024 15:27:12 +0200 Subject: [PATCH 057/104] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6e12a97e3..f16125bd6 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.2.0 + 1.2.1-SNAPSHOT DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - v1.2.0 + 1.0.0-SNAPSHOT From 05c983368ec77524c64c7a39bea0015913e9f196 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 23 Aug 2024 16:05:43 +0200 Subject: [PATCH 058/104] Updated parent pom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f16125bd6..fe834b531 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ nl.knaw.dans dd-parent - 1.5.1 + 1.9.0 dans-bagit-lib From ba88b5dec91e390605c0cf9169015c7970b311ce Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 21 Jul 2025 11:38:47 +0200 Subject: [PATCH 059/104] Upgrade pyyaml to 6.0.1 for compatibility with later Python versions. --- .github/workflows/mkdocs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mkdocs/requirements.txt b/.github/workflows/mkdocs/requirements.txt index 557ea036d..68afb21a1 100644 --- a/.github/workflows/mkdocs/requirements.txt +++ b/.github/workflows/mkdocs/requirements.txt @@ -1,3 +1,3 @@ mkdocs==1.3.0 -pyyaml==6.0 +pyyaml==6.0.1 mkdocs-markdownextradata-plugin==0.2.5 From 34fd16753aff0539a66aac727305eef8537a4d2d Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 3 Nov 2025 15:49:16 +0100 Subject: [PATCH 060/104] Revert to codecov --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index c2dd0520d..d77ab23b3 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -17,4 +17,4 @@ jobs: - name: Run tests and collect coverage run: mvn -B test - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 From 811f5344c955c57221dedc60bd0f62aa8e361c94 Mon Sep 17 00:00:00 2001 From: jo-pol Date: Tue, 23 Sep 2025 15:35:22 +0200 Subject: [PATCH 061/104] sync with new parent pom --- pom.xml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index fe834b531..5e02e48aa 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ nl.knaw.dans dd-parent - 1.9.0 + 1.9.1-SNAPSHOT dans-bagit-lib @@ -58,7 +58,6 @@ org.apiguardian apiguardian-api - 1.1.2 org.bouncycastle @@ -67,7 +66,6 @@ org.kamranzafar jtar - 2.3 @@ -148,7 +146,7 @@ javax.xml.bind jaxb-api - 2.3.1 + ${jaxb2-maven-plugin.version} From 336cd0f6f9f20bdbfdc80177d51b0710d5e2fa0d Mon Sep 17 00:00:00 2001 From: jo-pol Date: Tue, 14 Oct 2025 09:27:46 +0200 Subject: [PATCH 062/104] no more java 1.8 --- pom.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pom.xml b/pom.xml index 5e02e48aa..0b2b82de6 100644 --- a/pom.xml +++ b/pom.xml @@ -38,10 +38,6 @@ 1.0.0-SNAPSHOT - - 1.8 - - com.fasterxml.jackson.core From 29a907075a826ba6eb1c0b99642b34c06afb39bf Mon Sep 17 00:00:00 2001 From: jo-pol Date: Tue, 25 Nov 2025 15:51:55 +0100 Subject: [PATCH 063/104] upgrade parent --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0b2b82de6..cfbfa2bad 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ nl.knaw.dans dd-parent - 1.9.1-SNAPSHOT + 1.10.0 dans-bagit-lib From 53ef4214bb663ee9d4112d40d091d407654dee27 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Sun, 14 Dec 2025 17:39:34 +0100 Subject: [PATCH 064/104] Fixed error message --- .../nl/knaw/dans/bagit/conformance/BagProfileChecker.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/nl/knaw/dans/bagit/conformance/BagProfileChecker.java b/src/main/java/nl/knaw/dans/bagit/conformance/BagProfileChecker.java index 85f08885d..e2d9b2471 100644 --- a/src/main/java/nl/knaw/dans/bagit/conformance/BagProfileChecker.java +++ b/src/main/java/nl/knaw/dans/bagit/conformance/BagProfileChecker.java @@ -172,11 +172,11 @@ private static void requiredManifestsExist(final Set manifests, final for(final String requiredManifestType : requiredManifestTypes){ if(!manifestTypesPresent.contains(requiredManifestType)){ final StringBuilder sb = new StringBuilder(); - if(isPayloadManifest){ sb.append("tag"); - sb.append(MessageFormatter.format(messages.getString("required_tag_manifest_type_not_present"), requiredManifestType).getMessage()); + if(isPayloadManifest){ + sb.append(MessageFormatter.format(messages.getString("required_manifest_type_not_present"), requiredManifestType).getMessage()); } else{ - sb.append(MessageFormatter.format(messages.getString("required_manifest_type_not_present"), requiredManifestType).getMessage()); + sb.append(MessageFormatter.format(messages.getString("required_tag_manifest_type_not_present"), requiredManifestType).getMessage()); } throw new RequiredManifestNotPresentException(sb.toString()); From 311e0f8a233e1e9cc820cf35aed642a01596b1ce Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 19 Dec 2025 14:51:37 +0100 Subject: [PATCH 065/104] [maven-release-plugin] prepare release v1.2.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cfbfa2bad..ec75b9dae 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.2.1-SNAPSHOT + 1.2.1 DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - 1.0.0-SNAPSHOT + v1.2.1 From 4c1046ef257494ebbffd1a5331873cc4bf36fdac Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 19 Dec 2025 14:51:41 +0100 Subject: [PATCH 066/104] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ec75b9dae..b76b17048 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.2.1 + 1.2.2-SNAPSHOT DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - v1.2.1 + 1.0.0-SNAPSHOT From 5a0ab45dfe6f311b2496569997743112979f8b42 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 19 Dec 2025 17:21:01 +0100 Subject: [PATCH 067/104] Upgraded parent pom to 1.11.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b76b17048..9d22d8063 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ nl.knaw.dans dd-parent - 1.10.0 + 1.11.0 dans-bagit-lib From 9a4b85b6eaecd9272b9c0f44d55e43d4369ed73b Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 5 Jan 2026 11:30:03 +0100 Subject: [PATCH 068/104] Updated doc deps --- .github/workflows/mkdocs/requirements.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mkdocs/requirements.txt b/.github/workflows/mkdocs/requirements.txt index 68afb21a1..f99e66035 100644 --- a/.github/workflows/mkdocs/requirements.txt +++ b/.github/workflows/mkdocs/requirements.txt @@ -1,3 +1,4 @@ -mkdocs==1.3.0 -pyyaml==6.0.1 -mkdocs-markdownextradata-plugin==0.2.5 +mkdocs==1.6.1 +pyyaml==6.0.3 +pymdown-extensions==10.16.1 +mkdocs-markdownextradata-plugin==0.2.6 From fc3878e21e3dff77909bca5a4fa725e9ad133bb7 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 9 Mar 2026 10:37:04 +0100 Subject: [PATCH 069/104] DD-2219 Handle multi-line and long bag-info.txt values (#12) According to the BagIt specs multi-line values in `bag-info.txt` should be continued with an indent on the next line. See: https://www.rfc-editor.org/rfc/rfc8493#section-2.2.2. This was previously not implemented correctly leading to invalid bags. --- .../dans/bagit/BagitSuiteComplanceTest.java | 315 +++++++++--------- .../dans/bagit/reader/KeyValueReader.java | 3 +- .../dans/bagit/writer/MetadataWriter.java | 72 +++- .../dans/bagit/reader/MetadataReaderTest.java | 4 +- .../dans/bagit/writer/MetadataWriterTest.java | 128 ++++++- 5 files changed, 355 insertions(+), 167 deletions(-) diff --git a/src/integration/java/nl/knaw/dans/bagit/BagitSuiteComplanceTest.java b/src/integration/java/nl/knaw/dans/bagit/BagitSuiteComplanceTest.java index c0a5364c2..15c6d676a 100644 --- a/src/integration/java/nl/knaw/dans/bagit/BagitSuiteComplanceTest.java +++ b/src/integration/java/nl/knaw/dans/bagit/BagitSuiteComplanceTest.java @@ -15,20 +15,10 @@ */ package nl.knaw.dans.bagit; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicLong; - +import nl.knaw.dans.bagit.conformance.BagLinter; +import nl.knaw.dans.bagit.conformance.BagitWarning; import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.Version; import nl.knaw.dans.bagit.exceptions.CorruptChecksumException; import nl.knaw.dans.bagit.exceptions.FileNotInPayloadDirectoryException; import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; @@ -39,172 +29,177 @@ import nl.knaw.dans.bagit.exceptions.UnparsableVersionException; import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; import nl.knaw.dans.bagit.exceptions.VerificationException; +import nl.knaw.dans.bagit.reader.BagReader; import nl.knaw.dans.bagit.verify.BagVerifier; +import nl.knaw.dans.bagit.writer.BagWriter; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import nl.knaw.dans.bagit.conformance.BagLinter; -import nl.knaw.dans.bagit.conformance.BagitWarning; -import nl.knaw.dans.bagit.domain.Version; -import nl.knaw.dans.bagit.reader.BagReader; -import nl.knaw.dans.bagit.writer.BagWriter; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicLong; /** * This class assumes that the compliance test suite repo has been cloned and is available locally */ public class BagitSuiteComplanceTest extends TempFolderTest { - private static final Logger logger = LoggerFactory.getLogger(BagitSuiteComplanceTest.class); - - private static final Path complianceRepoRootDir = Paths.get("bagit-conformance-suite"); - private static final BagTestCaseVistor visitor = new BagTestCaseVistor(); - private static final BagReader reader = new BagReader(); - private static final BagVerifier verifier = new BagVerifier(); - - @BeforeAll - public static void setupOnce() throws IOException{ - if(!Files.exists(complianceRepoRootDir)){ - throw new IOException("bagit-conformance-suite git repo was not found, did you clone it?"); - } - Files.walkFileTree(complianceRepoRootDir, visitor); - } - - @Test - public void testValidBags() throws Exception{ - Bag bag; - - for(final Path bagDir : visitor.getValidTestCases()){ - bag = reader.read(bagDir); - verifier.isValid(bag, true); + private static final Logger logger = LoggerFactory.getLogger(BagitSuiteComplanceTest.class); + + private static final Path complianceRepoRootDir = Paths.get("bagit-conformance-suite"); + private static final BagTestCaseVistor visitor = new BagTestCaseVistor(); + private static final BagReader reader = new BagReader(); + private static final BagVerifier verifier = new BagVerifier(); + + @BeforeAll + public static void setupOnce() throws IOException { + if (!Files.exists(complianceRepoRootDir)) { + throw new IOException("bagit-conformance-suite git repo was not found, did you clone it?"); + } + Files.walkFileTree(complianceRepoRootDir, visitor); } - } - - @Test - public void testInvalidBags(){ - int errorCount = 0; - Bag bag; - ConcurrentMap, AtomicLong> map = new ConcurrentHashMap<>(); - - for(Path invalidBagDir : visitor.getInvalidTestCases()){ - try{ - bag = reader.read(invalidBagDir); - verifier.isValid(bag, true); - System.err.println(bag.getRootDir() + " should have failed but didn't!"); - }catch(InvalidBagitFileFormatException | IOException | UnparsableVersionException | - MissingPayloadManifestException | MissingBagitFileException | MissingPayloadDirectoryException | - FileNotInPayloadDirectoryException | InterruptedException | MaliciousPathException | - CorruptChecksumException | VerificationException | UnsupportedAlgorithmException e){ - - logger.info("Found invalid os specific bag with message: {}", e.getMessage()); - map.putIfAbsent(e.getClass(), new AtomicLong(0)); - map.get(e.getClass()).incrementAndGet(); - errorCount++; - } + + @Test + public void testValidBags() throws Exception { + Bag bag; + + for (final Path bagDir : visitor.getValidTestCases()) { + bag = reader.read(bagDir); + verifier.isValid(bag, true); + } } - - Assertions.assertEquals(visitor.getInvalidTestCases().size(), errorCount, "every test case should throw an error"); - logger.debug("Count of all errors found in generic invalid cases: {}", map); - } - - @Test - public void testInvalidOperatingSystemSpecificBags(){ - int errorCount = 0; - Bag bag; - List osSpecificInvalidPaths = visitor.getLinuxOnlyTestCases(); - ConcurrentMap, AtomicLong> map = new ConcurrentHashMap<>(); - - if(TestUtils.isExecutingOnWindows()){ - osSpecificInvalidPaths = visitor.getWindowsOnlyTestCases(); + + @Test + public void testInvalidBags() { + int errorCount = 0; + Bag bag; + ConcurrentMap, AtomicLong> map = new ConcurrentHashMap<>(); + + for (Path invalidBagDir : visitor.getInvalidTestCases()) { + try { + bag = reader.read(invalidBagDir); + verifier.isValid(bag, true); + System.err.println(bag.getRootDir() + " should have failed but didn't!"); + } + catch (InvalidBagitFileFormatException | IOException | UnparsableVersionException | + MissingPayloadManifestException | MissingBagitFileException | MissingPayloadDirectoryException | + FileNotInPayloadDirectoryException | InterruptedException | MaliciousPathException | + CorruptChecksumException | VerificationException | UnsupportedAlgorithmException e) { + + logger.info("Found invalid os specific bag with message: {}", e.getMessage()); + map.putIfAbsent(e.getClass(), new AtomicLong(0)); + map.get(e.getClass()).incrementAndGet(); + errorCount++; + } + } + + Assertions.assertEquals(visitor.getInvalidTestCases().size(), errorCount, "every test case should throw an error"); + logger.debug("Count of all errors found in generic invalid cases: {}", map); } - - for(Path invalidBagDir : osSpecificInvalidPaths){ - try{ - bag = reader.read(invalidBagDir); - verifier.isValid(bag, true); - }catch(InvalidBagitFileFormatException | IOException | UnparsableVersionException | - MissingPayloadManifestException | MissingBagitFileException | MissingPayloadDirectoryException | - FileNotInPayloadDirectoryException | InterruptedException | MaliciousPathException | - CorruptChecksumException | VerificationException | UnsupportedAlgorithmException e){ - - logger.info("Found invalid os specific bag with message: {}", e.getMessage()); - map.putIfAbsent(e.getClass(), new AtomicLong(0)); - map.get(e.getClass()).incrementAndGet(); - errorCount++; - } + + @Test + public void testInvalidOperatingSystemSpecificBags() { + int errorCount = 0; + Bag bag; + List osSpecificInvalidPaths = visitor.getLinuxOnlyTestCases(); + ConcurrentMap, AtomicLong> map = new ConcurrentHashMap<>(); + + if (TestUtils.isExecutingOnWindows()) { + osSpecificInvalidPaths = visitor.getWindowsOnlyTestCases(); + } + + for (Path invalidBagDir : osSpecificInvalidPaths) { + try { + bag = reader.read(invalidBagDir); + verifier.isValid(bag, true); + } + catch (InvalidBagitFileFormatException | IOException | UnparsableVersionException | + MissingPayloadManifestException | MissingBagitFileException | MissingPayloadDirectoryException | + FileNotInPayloadDirectoryException | InterruptedException | MaliciousPathException | + CorruptChecksumException | VerificationException | UnsupportedAlgorithmException e) { + + logger.info("Found invalid os specific bag with message: {}", e.getMessage()); + map.putIfAbsent(e.getClass(), new AtomicLong(0)); + map.get(e.getClass()).incrementAndGet(); + errorCount++; + } + } + + Assertions.assertEquals(osSpecificInvalidPaths.size(), errorCount, "every test case should throw an error"); + logger.debug("Count of all errors found in os specific invalid cases: {}", map); } - - Assertions.assertEquals(osSpecificInvalidPaths.size(), errorCount, "every test case should throw an error"); - logger.debug("Count of all errors found in os specific invalid cases: {}", map); - } - - @Test - public void testWarnings() throws Exception{ - Set warnings; - - for(Path bagDir : visitor.getWarningTestCases()){ - warnings = BagLinter.lintBag(bagDir); - Assertions.assertTrue(warnings.size() > 0); + + @Test + public void testWarnings() throws Exception { + Set warnings; + + for (Path bagDir : visitor.getWarningTestCases()) { + warnings = BagLinter.lintBag(bagDir); + Assertions.assertFalse(warnings.isEmpty()); + } } - } - - @Test - public void testReadWriteProducesSameBag() throws Exception{ - Bag bag; - Path newBagDir; - - for(final Path bagDir : visitor.getValidTestCases()){ - newBagDir = folder.resolve("readWriteProducesSameBag"); - bag = reader.read(bagDir); - BagWriter.write(bag, newBagDir); - - testTagFileContents(bag, newBagDir); - - testBagsStructureAreEqual(bagDir, newBagDir); - delete(newBagDir); + + @Test + public void testReadWriteProducesSameBag() throws Exception { + Bag bag; + Path newBagDir; + + for (final Path bagDir : visitor.getValidTestCases()) { + newBagDir = folder.resolve("readWriteProducesSameBag"); + bag = reader.read(bagDir); + BagWriter.write(bag, newBagDir); + + testTagFileContents(bag, newBagDir); + + testBagsStructureAreEqual(bagDir, newBagDir); + delete(newBagDir); + } } - } - - private void testTagFileContents(final Bag originalBag, final Path newBagDir) throws IOException{ - Path original = originalBag.getRootDir().resolve("bagit.txt"); - Path newFile = newBagDir.resolve("bagit.txt"); - Assertions.assertTrue(compareFileContents(original, newFile, StandardCharsets.UTF_8), "bagit.txt files differ"); - - if(originalBag.getVersion().isSameOrOlder(new Version(0, 95))){ - original = originalBag.getRootDir().resolve("package-info.txt"); - newFile = newBagDir.resolve("package-info.txt"); - Assertions.assertTrue(compareFileContents(original, newFile, originalBag.getFileEncoding()), - original + " differs from " + newFile); + + private void testTagFileContents(final Bag originalBag, final Path newBagDir) throws IOException { + Path original = originalBag.getRootDir().resolve("bagit.txt"); + Path newFile = newBagDir.resolve("bagit.txt"); + Assertions.assertTrue(compareFileContents(original, newFile, StandardCharsets.UTF_8), "bagit.txt files differ"); + + if (originalBag.getVersion().isSameOrOlder(new Version(0, 95))) { + original = originalBag.getRootDir().resolve("package-info.txt"); + newFile = newBagDir.resolve("package-info.txt"); + Assertions.assertTrue(compareFileContents(original, newFile, originalBag.getFileEncoding()), + original + " differs from " + newFile); + } + else { + if (Files.exists(originalBag.getRootDir().resolve("bag-info.txt"))) { + original = originalBag.getRootDir().resolve("bag-info.txt"); + newFile = newBagDir.resolve("bag-info.txt"); + Assertions.assertTrue(compareFileContents(original, newFile, originalBag.getFileEncoding()), + original + " differs from " + newFile); + } + } + } - else{ - if(Files.exists(originalBag.getRootDir().resolve("bag-info.txt"))){ - original = originalBag.getRootDir().resolve("bag-info.txt"); - newFile = newBagDir.resolve("bag-info.txt"); - Assertions.assertTrue(compareFileContents(original,newFile, originalBag.getFileEncoding()), - original + " differs from " + newFile); - } + + //return true if the content is the same disregarding line endings and indentation + private boolean compareFileContents(final Path file1, final Path file2, final Charset encoding) throws IOException { + List lines1 = Files.readAllLines(file1, encoding); + List lines2 = Files.readAllLines(file2, encoding); + + String s1 = String.join("", lines1).replaceAll("\\r|\\n| |\t", "").replaceAll("Payload-Oxum:[0-9.]+", ""); + String s2 = String.join("", lines2).replaceAll("\\r|\\n| |\t", "").replaceAll("Payload-Oxum:[0-9.]+", ""); + + return s1.equals(s2); } - - } - - //return true if the content is the same disregarding line endings - private final boolean compareFileContents(final Path file1, final Path file2, final Charset encoding) throws IOException { - List lines1 = Files.readAllLines(file1, encoding); - List lines2 = Files.readAllLines(file2, encoding); - - List strippedLines1 = new ArrayList<>(lines1.size()); - List strippedLines2 = new ArrayList<>(lines2.size()); - - for(int index=0; index> readKeyValuesFromFile(f private static void mergeIndentedLine(final String line, final List> keyValues){ final SimpleImmutableEntry oldKeyValue = keyValues.remove(keyValues.size() -1); - final SimpleImmutableEntry newKeyValue = new SimpleImmutableEntry<>(oldKeyValue.getKey(), oldKeyValue.getValue() + System.lineSeparator() +line); + final String newContent = line.stripLeading(); + final SimpleImmutableEntry newKeyValue = new SimpleImmutableEntry<>(oldKeyValue.getKey(), oldKeyValue.getValue() + System.lineSeparator() + newContent); keyValues.add(newKeyValue); logger.debug(messages.getString("found_indented_line"), oldKeyValue.getKey()); diff --git a/src/main/java/nl/knaw/dans/bagit/writer/MetadataWriter.java b/src/main/java/nl/knaw/dans/bagit/writer/MetadataWriter.java index 1a887ceec..9467e2173 100644 --- a/src/main/java/nl/knaw/dans/bagit/writer/MetadataWriter.java +++ b/src/main/java/nl/knaw/dans/bagit/writer/MetadataWriter.java @@ -61,12 +61,80 @@ public static void writeBagMetadata(final Metadata metadata, final Version versi final StringBuilder lines = new StringBuilder(); for(final SimpleImmutableEntry entry : metadata.getAll()){ - final String line = entry.getKey() + ": " + entry.getValue() + System.lineSeparator(); - lines.append(line); + final String key = entry.getKey(); + String value = entry.getValue(); + value = value.replaceAll("[^\\x09\\x20-\\x7E\\x0A\\x0D\\x80-\\uFFFF]", ""); + lines.append(formatLine(key, value, version)).append(System.lineSeparator()); } logger.debug(messages.getString("writing_line_to_file"), lines.toString(), bagInfoFilePath); Files.write(bagInfoFilePath, lines.toString().getBytes(charsetName), StandardOpenOption.APPEND, StandardOpenOption.CREATE); } + + private static String formatLine(final String key, final String value, final Version version) { + if (version.isSameOrOlder(VERSION_0_95)) { + final String fullLine = key + ": " + value; + return fullLine.replaceAll("(\\r\\n|\\r|\\n)", "$1 "); + } + + final StringBuilder sb = new StringBuilder(); + final int keyPrefixLength = key.length() + 2; // "Key: " + sb.append(key).append(": "); + final String[] parts = value.split("(\\r\\n|\\r|\\n)", -1); + + for (int i = 0; i < parts.length; i++) { + String line = parts[i]; + if (i > 0) { + sb.append(System.lineSeparator()); + line = " " + line; + } + + final int maxLength = i == 0 ? 79 - keyPrefixLength : 78; // Continuation lines have " " prefix + // Check if it's already "well-wrapped" or short + if (line.length() > maxLength) { + sb.append(wrapLine(line, i == 0, keyPrefixLength)); + } else { + sb.append(line); + } + } + return sb.toString(); + } + + private static String wrapLine(final String line, final boolean isFirstLine, final int keyPrefixLength) { + final StringBuilder sb = new StringBuilder(); + int start = 0; + boolean firstWrap = true; + + while (start < line.length()) { + int maxLength = 78; // Indented line starts with " ", so 78 more chars = 79 + if (firstWrap && isFirstLine) { + maxLength = 79 - keyPrefixLength; + } + + int end = Math.min(start + maxLength, line.length()); + + if (end < line.length()) { + int lastSpace = line.lastIndexOf(' ', end); + if (lastSpace > start) { + end = lastSpace; + } + } + + String sub = line.substring(start, end); + if (start > 0) { + sb.append(System.lineSeparator()); + sb.append(" "); + } + sb.append(sub); + + start = end; + while (start < line.length() && line.charAt(start) == ' ') { + start++; + } + firstWrap = false; + } + + return sb.toString(); + } } diff --git a/src/test/java/nl/knaw/dans/bagit/reader/MetadataReaderTest.java b/src/test/java/nl/knaw/dans/bagit/reader/MetadataReaderTest.java index 9bf2027e9..2e308135a 100644 --- a/src/test/java/nl/knaw/dans/bagit/reader/MetadataReaderTest.java +++ b/src/test/java/nl/knaw/dans/bagit/reader/MetadataReaderTest.java @@ -44,7 +44,7 @@ public void testReadBagMetadata() throws Exception{ expectedValues.add(new SimpleImmutableEntry<>("Contact-Phone", "+1 408-555-1212")); expectedValues.add(new SimpleImmutableEntry<>("Contact-Email", "ej@spengler.edu")); expectedValues.add(new SimpleImmutableEntry<>("External-Description", "Uncompressed greyscale TIFF images from the" + System.lineSeparator() + - " Yoshimuri papers collection.")); + "Yoshimuri papers collection.")); expectedValues.add(new SimpleImmutableEntry<>("Bagging-Date", "2008-01-15")); expectedValues.add(new SimpleImmutableEntry<>("External-Identifier", "spengler_yoshimuri_001")); expectedValues.add(new SimpleImmutableEntry<>("Bag-Size", "260 GB")); @@ -52,7 +52,7 @@ public void testReadBagMetadata() throws Exception{ expectedValues.add(new SimpleImmutableEntry<>("Bag-Count", "1 of 15")); expectedValues.add(new SimpleImmutableEntry<>("Internal-Sender-Identifier", "/storage/images/yoshimuri")); expectedValues.add(new SimpleImmutableEntry<>("Internal-Sender-Description", "Uncompressed greyscale TIFFs created from" + System.lineSeparator() + - " microfilm.")); + "microfilm.")); expectedValues.add(new SimpleImmutableEntry<>("Bag-Count", "1 of 15")); //test duplicate Path bagInfoFile = Paths.get(getClass().getClassLoader().getResource("baginfoFiles").toURI()); diff --git a/src/test/java/nl/knaw/dans/bagit/writer/MetadataWriterTest.java b/src/test/java/nl/knaw/dans/bagit/writer/MetadataWriterTest.java index 21a5e080a..1864a7fa8 100644 --- a/src/test/java/nl/knaw/dans/bagit/writer/MetadataWriterTest.java +++ b/src/test/java/nl/knaw/dans/bagit/writer/MetadataWriterTest.java @@ -27,6 +27,11 @@ import nl.knaw.dans.bagit.domain.Metadata; import nl.knaw.dans.bagit.domain.Version; +import nl.knaw.dans.bagit.reader.KeyValueReader; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertTrue; public class MetadataWriterTest extends PrivateConstructorTest { @@ -49,9 +54,128 @@ public void testWriteBagitInfoFile() throws IOException{ Assertions.assertFalse(Files.exists(packageInfo)); MetadataWriter.writeBagMetadata(metadata, new Version(0,96), rootDir, StandardCharsets.UTF_8); - Assertions.assertTrue(Files.exists(bagInfo)); + assertTrue(Files.exists(bagInfo)); MetadataWriter.writeBagMetadata(metadata, new Version(0,95), rootDir, StandardCharsets.UTF_8); - Assertions.assertTrue(Files.exists(packageInfo)); + assertTrue(Files.exists(packageInfo)); + } + + @Test + public void testWriteAndReadMultilineMetadata() throws Exception { + Path rootDir = createDirectory("multilineTest"); + Metadata metadata = new Metadata(); + String multilineValue = "This is a" + System.lineSeparator() + "multi-line" + System.lineSeparator() + "value."; + metadata.add("Description", multilineValue); + metadata.add("Contact-Name", "John Doe"); + + MetadataWriter.writeBagMetadata(metadata, new Version(1, 0), rootDir, StandardCharsets.UTF_8); + + Path bagInfo = rootDir.resolve("bag-info.txt"); + String content = Files.readString(bagInfo, StandardCharsets.UTF_8); + + // Check if subsequent lines are indented + String[] lines = content.split("\\R"); + assertTrue(lines[0].startsWith("Description: ")); + assertTrue(lines[1].startsWith(" "), "Line 2 should be indented"); + assertTrue(lines[2].startsWith(" "), "Line 3 should be indented"); + + // Read it back + List> readMetadata = KeyValueReader.readKeyValuesFromFile(bagInfo, ":", StandardCharsets.UTF_8); + + boolean foundDescription = false; + for (SimpleImmutableEntry entry : readMetadata) { + if ("Description".equals(entry.getKey())) { + Assertions.assertEquals(multilineValue, entry.getValue()); + foundDescription = true; + } + } + assertTrue(foundDescription); + } + + @Test + public void testSanitizeMetadata() throws Exception { + Path rootDir = createDirectory("sanitizeTest"); + Metadata metadata = new Metadata(); + // \u0000 is a non-printable char, \u0007 is bell + String dirtyValue = "Value with\u0000 non-printable\u0007 chars."; + String cleanValue = "Value with non-printable chars."; + metadata.add("Custom-Key", dirtyValue); + + MetadataWriter.writeBagMetadata(metadata, new Version(1, 0), rootDir, StandardCharsets.UTF_8); + + Path bagInfo = rootDir.resolve("bag-info.txt"); + List> readMetadata = KeyValueReader.readKeyValuesFromFile(bagInfo, ":", StandardCharsets.UTF_8); + + boolean found = false; + for (SimpleImmutableEntry entry : readMetadata) { + if ("Custom-Key".equals(entry.getKey())) { + Assertions.assertEquals(cleanValue, entry.getValue()); + found = true; + } + } + assertTrue(found); + } + + @Test + public void testWrapLongLines() throws Exception { + Path rootDir = createDirectory("wrapLongLinesTest"); + Metadata metadata = new Metadata(); + String longValue = "This is a very long value that should definitely exceed the seventy-nine characters limit that is recommended by the BagIt RFC 8493 section two point two point two."; + // Length is ~166 chars. + metadata.add("Long-Key", longValue); + + MetadataWriter.writeBagMetadata(metadata, new Version(1, 0), rootDir, StandardCharsets.UTF_8); + + Path bagInfo = rootDir.resolve("bag-info.txt"); + String content = Files.readString(bagInfo, StandardCharsets.UTF_8); + + String[] lines = content.split("\\R"); + for (String line : lines) { + assertTrue(line.length() <= 79, "Line length should be <= 79: " + line.length()); + if (!line.startsWith("Long-Key: ")) { + assertTrue(line.startsWith(" "), "Wrapped lines should be indented"); + } + } + + // Read it back + List> readMetadata = KeyValueReader.readKeyValuesFromFile(bagInfo, ":", StandardCharsets.UTF_8); + boolean foundLongKey = false; + for (SimpleImmutableEntry entry : readMetadata) { + if ("Long-Key".equals(entry.getKey())) { + // Since it's wrapped at spaces, the space is replaced by newline in reading if it's joined by newline + // But wrapLine also preserves spaces? Let's check what it actually produces. + // It should match the longValue with some spaces replaced by newlines. + Assertions.assertEquals(longValue, entry.getValue().replace(System.lineSeparator(), " ")); + foundLongKey = true; + } + } + assertTrue(foundLongKey); + } + + @Test + public void testMultilineAndWrapping() throws Exception { + Path rootDir = createDirectory("multilineAndWrappingTest"); + Metadata metadata = new Metadata(); + // A multiline value where the first line is short and the second is long + String value = "Short first line\nThis is a very long second line that will definitely need wrapping because it's much longer than seventy-nine characters."; + metadata.add("Description", value); + + MetadataWriter.writeBagMetadata(metadata, new Version(1, 0), rootDir, StandardCharsets.UTF_8); + + Path bagInfo = rootDir.resolve("bag-info.txt"); + String content = Files.readString(bagInfo, StandardCharsets.UTF_8); + + // Check for double newlines or lines containing only a space + String[] lines = content.split("\\r?\\n"); + for (int i = 0; i < lines.length; i++) { + String line = lines[i]; + Assertions.assertFalse(line.isEmpty() && i < lines.length - 1, "Should not have empty lines in the middle (caused by double newlines)"); + if (i > 0) { + // All lines after the first one of an entry must be indented + assertTrue(line.startsWith(" "), "Continuation line must start with a space: [" + line + "]"); + // If it was a double newline, we'd see a line that is just " " followed by another line + Assertions.assertNotEquals(" ", line, "Should not have a line that is just a single space"); + } + } } } From d8675ed3cacfa4b578d0ac7f8e8bf85585be1d9d Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 9 Mar 2026 10:38:04 +0100 Subject: [PATCH 070/104] [maven-release-plugin] prepare release v1.2.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9d22d8063..cb69d08f8 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.2.2-SNAPSHOT + 1.2.2 DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - 1.0.0-SNAPSHOT + v1.2.2 From b62fcd8dcde1380ecc2bce9277f5acc12922d2a9 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 9 Mar 2026 10:38:08 +0100 Subject: [PATCH 071/104] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index cb69d08f8..668d4f093 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.2.2 + 1.2.3-SNAPSHOT DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - v1.2.2 + 1.0.0-SNAPSHOT From f53d4720c49c0a81547fe945141783cb7fe2aea2 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 13 Mar 2026 11:54:52 +0100 Subject: [PATCH 072/104] DD-2203 Holey bagit validation (#13) * Extends the `BagVerifier` with a method that allows validation that allows holey bags. In `allowHoley` mode the bag is considered valid even when containing a fetch.txt file provided that the files to be fetched can be validated. When calculating the hashes the files are *not* stored locally. Instead the bytes are streamed through the hasher and discarded. This means very large files can be in the fetch.txt without this requiring the client to maintain a large temp disk. * Furthermore, range requests are used if supported. And chunks are retried if the connection is interrupted. The following system properties can be used override the default parameters that control this processed: * `nl.knaw.dans.bagit.hash.chunkSize` - number of bytes to read into a chunk. * `nl.knaw.dans.bagit.hash.maxRetries` - maximum number of times to try to read a chunk. * `nl.knaw.dans.bagit.hash.retrySleepMs` - milliseconds to wait between retries. * Optional extra headers can be specified to send along with fetch requests, to support fetching items that require authentication. --- .../java/nl/knaw/dans/bagit/hash/Hasher.java | 473 ++++++++++++++---- .../knaw/dans/bagit/reader/TagFileReader.java | 2 +- .../knaw/dans/bagit/verify/BagVerifier.java | 76 ++- .../bagit/verify/CheckManifestHashesTask.java | 45 +- .../dans/bagit/verify/ManifestVerifier.java | 109 +++- .../knaw/dans/bagit/hash/HasherUrlTest.java | 278 ++++++++++ .../bagit/verify/BagVerifierRedirectTest.java | 141 ++++++ .../bagit/verify/BagVerifierRemoteTest.java | 186 +++++++ .../dans/bagit/verify/BagVerifierTest.java | 35 ++ 9 files changed, 1203 insertions(+), 142 deletions(-) create mode 100644 src/test/java/nl/knaw/dans/bagit/hash/HasherUrlTest.java create mode 100644 src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRedirectTest.java create mode 100644 src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRemoteTest.java diff --git a/src/main/java/nl/knaw/dans/bagit/hash/Hasher.java b/src/main/java/nl/knaw/dans/bagit/hash/Hasher.java index 407008d21..9f573cda9 100644 --- a/src/main/java/nl/knaw/dans/bagit/hash/Hasher.java +++ b/src/main/java/nl/knaw/dans/bagit/hash/Hasher.java @@ -15,115 +15,400 @@ */ package nl.knaw.dans.bagit.hash; +import nl.knaw.dans.bagit.domain.FetchItem; +import nl.knaw.dans.bagit.domain.Manifest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Formatter; import java.util.HashMap; import java.util.Map; -import java.util.ResourceBundle; import java.util.Map.Entry; - -import nl.knaw.dans.bagit.domain.Manifest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.util.ResourceBundle; /** - * Convenience class for generating a HEX formatted string of the checksum hash. + * Convenience class for generating a HEX formatted string of the checksum hash. */ public final class Hasher { - private static final Logger logger = LoggerFactory.getLogger(Hasher.class); - private static final int _64_KB = 1024 * 64; - private static final int CHUNK_SIZE = _64_KB; - private static final ResourceBundle messages = ResourceBundle.getBundle("MessageBundle"); - - private Hasher(){ - //intentionally left empty - } - - /** - * Create a HEX formatted string checksum hash of the file - * - * @param path the {@link Path} (file) to hash - * @param messageDigest the {@link MessageDigest} object representing the hashing algorithm - * @return the hash as a hex formated string - * @throws IOException if there is a problem reading the file - */ - public static String hash(final Path path, final MessageDigest messageDigest) throws IOException { - updateMessageDigests(path, Arrays.asList(messageDigest)); - - return formatMessageDigest(messageDigest); - } - - /** - * Update the Manifests with the file's hash - * - * @param path the {@link Path} (file) to hash - * @param manifestToMessageDigestMap the map between {@link Manifest} and {@link MessageDigest} - * @throws IOException if there is a problem reading the file - */ - public static void hash(final Path path, final Map manifestToMessageDigestMap) throws IOException { - updateMessageDigests(path, manifestToMessageDigestMap.values()); - addMessageDigestHashToManifest(path, manifestToMessageDigestMap); - } - - static void updateMessageDigests(final Path path, final Collection messageDigests) throws IOException{ - try(final InputStream is = new BufferedInputStream(Files.newInputStream(path, StandardOpenOption.READ))){ - final byte[] buffer = new byte[CHUNK_SIZE]; - int read = is.read(buffer); - - while(read != -1) { - for(final MessageDigest messageDigest : messageDigests){ - messageDigest.update(buffer, 0, read); - } - read = is.read(buffer); - } - } - } - - private static void addMessageDigestHashToManifest(final Path path, final Map manifestToMessageDigestMap){ - for(final Entry entry : manifestToMessageDigestMap.entrySet()){ - final String hash = formatMessageDigest(entry.getValue()); - logger.debug(messages.getString("adding_checksum"), path, hash); - entry.getKey().getFileToChecksumMap().put(path, hash); - } - } - - //Convert the byte to hex format - private static String formatMessageDigest(final MessageDigest messageDigest){ - try(final Formatter formatter = new Formatter()){ - for (final byte b : messageDigest.digest()) { - formatter.format("%02x", b); - } - - return formatter.toString(); - } - } - - /** - * create a mapping between {@link Manifest} and {@link MessageDigest} for each each supplied {@link SupportedAlgorithm} - * - * @param algorithms the {@link SupportedAlgorithm} that you which to map to {@link MessageDigest} - * @return mapping between {@link Manifest} and {@link MessageDigest} - * @throws NoSuchAlgorithmException if {@link MessageDigest} doesn't support the algorithm - */ - @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - public static Map createManifestToMessageDigestMap(final Collection algorithms) throws NoSuchAlgorithmException{ - final Map map = new HashMap<>(); - - for(final SupportedAlgorithm algorithm : algorithms){ - final MessageDigest messageDigest = MessageDigest.getInstance(algorithm.getMessageDigestName()); - final Manifest manifest = new Manifest(algorithm); - map.put(manifest, messageDigest); - } - - return map; - } + private static final Logger logger = LoggerFactory.getLogger(Hasher.class); + private static final int _64_KB = 1024 * 64; + private static final int CHUNK_SIZE = _64_KB; + private static final ResourceBundle messages = ResourceBundle.getBundle("MessageBundle"); + + private static final String CHUNK_SIZE_PROP = "nl.knaw.dans.bagit.hash.chunkSize"; + private static final String MAX_RETRIES_PROP = "nl.knaw.dans.bagit.hash.maxRetries"; + private static final String RETRY_SLEEP_MS_PROP = "nl.knaw.dans.bagit.hash.retrySleepMs"; + + private static final long DEFAULT_CHUNK_SIZE = 1024L * 1024L * 1024L; // 1 GiB + private static final int DEFAULT_MAX_RETRIES = 5; + private static final int DEFAULT_RETRY_SLEEP_MS = 5000; + + private Hasher() { + //intentionally left empty + } + + /** + * Create a HEX formatted string checksum hash of the file + * + * @param path the {@link Path} (file) to hash + * @param messageDigest the {@link MessageDigest} object representing the hashing algorithm + * @return the hash as a hex formated string + * @throws IOException if there is a problem reading the file + */ + public static String hash(final Path path, final MessageDigest messageDigest) throws IOException { + updateMessageDigests(path, Collections.singletonList(messageDigest)); + + return formatMessageDigest(messageDigest); + } + + /** + * Create a HEX formatted string checksum hash of the data from the URL + * + * @param url the {@link URL} to hash + * @param messageDigest the {@link MessageDigest} object representing the hashing algorithm + * @return the hash as a hex-formatted string + * @throws IOException if there is a problem reading from the URL + */ + public static String hash(final URL url, final MessageDigest messageDigest) throws IOException { + return hash(url, messageDigest, null); + } + + /** + * Create a HEX formatted string checksum hash of the data from the {@link FetchItem} + * + * @param item the {@link FetchItem} to hash + * @param messageDigest the {@link MessageDigest} object representing the hashing algorithm + * @param extraHeaders optional extra headers to send with the request + * @return the hash as a hex formatted string + * @throws IOException if there is a problem reading from the URL + */ + public static String hash(final FetchItem item, final MessageDigest messageDigest, final Map extraHeaders) throws IOException { + long totalSize = (item.length != null && item.length >= 0) ? item.length : -1; + URL currentUrl = item.url; + Map currentHeaders = extraHeaders; + + if (!currentUrl.getProtocol().startsWith("http")) { + return hashFullStream(currentUrl, messageDigest, currentHeaders); + } + + long chunkSize = Long.getLong(CHUNK_SIZE_PROP, DEFAULT_CHUNK_SIZE); + int maxRetries = Integer.getInteger(MAX_RETRIES_PROP, DEFAULT_MAX_RETRIES); + int retrySleepMs = Integer.getInteger(RETRY_SLEEP_MS_PROP, DEFAULT_RETRY_SLEEP_MS); + + long offset = 0; + while (totalSize < 0 || offset < totalSize) { + long end = (totalSize > 0) ? Math.min(offset + chunkSize - 1, totalSize - 1) : offset + chunkSize - 1; + String range = "bytes=" + offset + "-" + end; + + final URL finalUrl = currentUrl; + final Map finalHeaders = currentHeaders; + final long finalTotalSize = totalSize; + + try { + ChunkResult result = executeWithRetry(() -> { + HttpURLConnection conn = openRangedConnection(finalUrl, range, finalHeaders); + int code = conn.getResponseCode(); + + // Manual redirect following + if (code >= 300 && code < 400) { + return ChunkResult.redirect(conn.getHeaderField("Location")); + } + + if (code == 206) { + return handlePartialContent(conn, messageDigest, finalTotalSize); + } + else if (code == 200) { + logger.info("Server returned 200 OK for range request (probably range requests are not supported); downloading full stream from {}", finalUrl); + try (InputStream is = conn.getInputStream()) { + updateDigestFromStream(is, messageDigest); + } + return ChunkResult.fullStream(formatMessageDigest(messageDigest)); + } + else { + throw new IOException("Unexpected response code " + code + " for " + finalUrl); + } + }, "Error fetching range " + range + " from " + currentUrl, maxRetries, retrySleepMs); + + logger.debug("Processing chunk result for range {} from {}", range, currentUrl); + + if (result.type == ChunkResultType.FULL_STREAM_SUCCESS) { + logger.debug("Successfully processed full stream for range {} from {}", range, currentUrl); + return result.hash; + } + else if (result.type == ChunkResultType.REDIRECT) { + URL nextUrl = new URL(currentUrl, result.location); + if (!currentUrl.getAuthority().equals(nextUrl.getAuthority()) || !currentUrl.getProtocol().equals(nextUrl.getProtocol())) { + currentHeaders = null; + } + currentUrl = nextUrl; + logger.debug("Redirected to {}, currentHeaders stripped: {}", currentUrl, (currentHeaders == null)); + // Skip offset update and retry the current chunk with new URL + } + else if (result.type == ChunkResultType.SUCCESS) { + offset += result.bytesRead; + if (totalSize < 0 && result.totalSize > 0) { + totalSize = result.totalSize; + } + logger.debug("Successfully processed chunk for range {} from {}", range, currentUrl); + logger.debug("Read {} of {}{}", offset, totalSize > 0 ? totalSize : "Unknown", totalSize > 0 ? " (" + (offset * 100L / totalSize) + "%)" : ""); + } + } + catch (IOException e) { + logger.info("Falling back to full stream for {} after failed range requests", currentUrl); + messageDigest.reset(); + return hashFullStream(currentUrl, messageDigest, currentHeaders); + } + } + + return formatMessageDigest(messageDigest); + } + + private static String hashFullStream(final URL url, final MessageDigest messageDigest, final Map extraHeaders) throws IOException { + URL currentUrl = url; + Map currentHeaders = extraHeaders; + + while (true) { + URLConnection conn = currentUrl.openConnection(); + if (conn instanceof HttpURLConnection httpConn) { + httpConn.setInstanceFollowRedirects(false); + if (currentHeaders != null) { + for (Entry entry : currentHeaders.entrySet()) { + httpConn.setRequestProperty(entry.getKey(), entry.getValue()); + } + } + int code = httpConn.getResponseCode(); + if (code >= 300 && code < 400) { + String location = httpConn.getHeaderField("Location"); + URL nextUrl = new URL(currentUrl, location); + if (!currentUrl.getAuthority().equals(nextUrl.getAuthority()) || !currentUrl.getProtocol().equals(nextUrl.getProtocol())) { + currentHeaders = null; + } + currentUrl = nextUrl; + continue; + } + if (code != 200) { + throw new IOException("Unexpected response code " + code + " for " + currentUrl); + } + } + try (final InputStream is = conn.getInputStream()) { + updateDigestFromStream(is, messageDigest); + } + break; + } + return formatMessageDigest(messageDigest); + } + + /** + * Create a HEX formatted string checksum hash of the data from the URL + * + * @param url the {@link URL} to hash + * @param messageDigest the {@link MessageDigest} object representing the hashing algorithm + * @param extraHeaders optional extra headers to send with the request + * @return the hash as a hex formatted string + * @throws IOException if there is a problem reading from the URL + */ + public static String hash(final URL url, final MessageDigest messageDigest, final Map extraHeaders) throws IOException { + return hash(new FetchItem(url, -1L, null), messageDigest, extraHeaders); + } + + private static ChunkResult handlePartialContent(HttpURLConnection conn, MessageDigest messageDigest, long currentTotalSize) throws IOException { + long totalSize = currentTotalSize; + if (totalSize < 0) { + String contentRange = conn.getHeaderField("Content-Range"); + if (contentRange != null && contentRange.contains("/")) { + try { + totalSize = Long.parseLong(contentRange.substring(contentRange.lastIndexOf("/") + 1)); + } + catch (NumberFormatException e) { + logger.warn("Could not parse Content-Range: {}", contentRange); + } + } + } + try (InputStream is = conn.getInputStream()) { + int bytesRead = updateDigestFromStream(is, messageDigest); + if (bytesRead < 0) { + throw new IOException("Stream closed unexpectedly for " + conn.getURL()); + } + return ChunkResult.success(bytesRead, totalSize); + } + } + + private static ChunkResult executeWithRetry(RetryableOperation operation, String description, int maxRetries, int retrySleepMs) throws IOException { + for (int attempt = 0; attempt < maxRetries; attempt++) { + try { + ChunkResult result = operation.execute(); + if (result.type == ChunkResultType.REDIRECT) { + return result; // Break the retry loop for redirects + } + return result; + } + catch (IOException e) { + logger.warn("{} (attempt {}/{}): {}", description, attempt + 1, maxRetries, e.getMessage()); + if (attempt < maxRetries - 1) { + try { + Thread.sleep(retrySleepMs); + } + catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new IOException("Interrupted during retry sleep", ie); + } + } + else { + throw e; + } + } + } + throw new IOException("Max retries exceeded"); + } + + @FunctionalInterface + private interface RetryableOperation { + T execute() throws IOException; + } + + private enum ChunkResultType { + SUCCESS, REDIRECT, FULL_STREAM_SUCCESS + } + + /** + * Represents the result of a chunk operation when processing data for hashing. A chunk operation can have various results, such as a successful read, a redirection to another location, or + * successful processing of a complete stream. + */ + private static class ChunkResult { + final ChunkResultType type; + final int bytesRead; + final long totalSize; + final String location; + final String hash; + + private ChunkResult(ChunkResultType type, int bytesRead, long totalSize, String location, String hash) { + this.type = type; + this.bytesRead = bytesRead; + this.totalSize = totalSize; + this.location = location; + this.hash = hash; + } + + static ChunkResult success(int bytesRead, long totalSize) { + return new ChunkResult(ChunkResultType.SUCCESS, bytesRead, totalSize, null, null); + } + + static ChunkResult redirect(String location) { + return new ChunkResult(ChunkResultType.REDIRECT, 0, -1, location, null); + } + + static ChunkResult fullStream(String hash) { + return new ChunkResult(ChunkResultType.FULL_STREAM_SUCCESS, 0, -1, null, hash); + } + } + + /** + * Opens a HttpURLConnection for the given range. + */ + private static HttpURLConnection openRangedConnection(URL url, String range, final Map extraHeaders) throws IOException { + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + if (extraHeaders != null) { + for (Entry entry : extraHeaders.entrySet()) { + conn.setRequestProperty(entry.getKey(), entry.getValue()); + } + } + conn.setInstanceFollowRedirects(false); + if (range != null) { + conn.setRequestProperty("Range", range); + } + return conn; + } + + /** + * Reads from the InputStream and updates the MessageDigest. Returns the number of bytes read. + */ + private static int updateDigestFromStream(InputStream is, MessageDigest messageDigest) throws IOException { + byte[] buffer = new byte[CHUNK_SIZE]; + int totalRead = 0; + int read = is.read(buffer); + while (read != -1) { + messageDigest.update(buffer, 0, read); + totalRead += read; + read = is.read(buffer); + } + return totalRead; + } + + /** + * Update the Manifests with the file's hash + * + * @param path the {@link Path} (file) to hash + * @param manifestToMessageDigestMap the map between {@link Manifest} and {@link MessageDigest} + * @throws IOException if there is a problem reading the file + */ + public static void hash(final Path path, final Map manifestToMessageDigestMap) throws IOException { + updateMessageDigests(path, manifestToMessageDigestMap.values()); + addMessageDigestHashToManifest(path, manifestToMessageDigestMap); + } + + static void updateMessageDigests(final Path path, final Collection messageDigests) throws IOException { + try (final InputStream is = new BufferedInputStream(Files.newInputStream(path, StandardOpenOption.READ))) { + final byte[] buffer = new byte[CHUNK_SIZE]; + int read = is.read(buffer); + + while (read != -1) { + for (final MessageDigest messageDigest : messageDigests) { + messageDigest.update(buffer, 0, read); + } + read = is.read(buffer); + } + } + } + + private static void addMessageDigestHashToManifest(final Path path, final Map manifestToMessageDigestMap) { + for (final Entry entry : manifestToMessageDigestMap.entrySet()) { + final String hash = formatMessageDigest(entry.getValue()); + logger.debug(messages.getString("adding_checksum"), path, hash); + entry.getKey().getFileToChecksumMap().put(path, hash); + } + } + + //Convert the byte to hex format + private static String formatMessageDigest(final MessageDigest messageDigest) { + try (final Formatter formatter = new Formatter()) { + for (final byte b : messageDigest.digest()) { + formatter.format("%02x", b); + } + + return formatter.toString(); + } + } + + /** + * create a mapping between {@link Manifest} and {@link MessageDigest} for each each supplied {@link SupportedAlgorithm} + * + * @param algorithms the {@link SupportedAlgorithm} that you which to map to {@link MessageDigest} + * @return mapping between {@link Manifest} and {@link MessageDigest} + * @throws NoSuchAlgorithmException if {@link MessageDigest} doesn't support the algorithm + */ + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + public static Map createManifestToMessageDigestMap(final Collection algorithms) throws NoSuchAlgorithmException { + final Map map = new HashMap<>(); + + for (final SupportedAlgorithm algorithm : algorithms) { + final MessageDigest messageDigest = MessageDigest.getInstance(algorithm.getMessageDigestName()); + final Manifest manifest = new Manifest(algorithm); + map.put(manifest, messageDigest); + } + + return map; + } } diff --git a/src/main/java/nl/knaw/dans/bagit/reader/TagFileReader.java b/src/main/java/nl/knaw/dans/bagit/reader/TagFileReader.java index be88e30ac..c45e8b074 100644 --- a/src/main/java/nl/knaw/dans/bagit/reader/TagFileReader.java +++ b/src/main/java/nl/knaw/dans/bagit/reader/TagFileReader.java @@ -52,7 +52,7 @@ static Path createFileFromManifest(final Path bagRootDir, final String path) thr throw new InvalidBagitFileFormatException(MessageFormatter.format(formattedMessage, path).getMessage()); } - if(path.contains("~/")){ + if(path.contains("~/") || path.startsWith("~")){ final String formattedMessage = messages.getString("malicious_path_error"); throw new MaliciousPathException(MessageFormatter.format(formattedMessage, path).getMessage()); } diff --git a/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java b/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java index fdeb19ef1..38b3ee30f 100644 --- a/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java @@ -16,10 +16,13 @@ package nl.knaw.dans.bagit.verify; import java.io.IOException; +import java.net.URL; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Map.Entry; import java.util.ResourceBundle; import java.util.concurrent.CountDownLatch; @@ -27,6 +30,7 @@ import java.util.concurrent.Executors; import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.FetchItem; import nl.knaw.dans.bagit.domain.Manifest; import nl.knaw.dans.bagit.exceptions.CorruptChecksumException; import nl.knaw.dans.bagit.exceptions.FileNotInManifestException; @@ -147,43 +151,67 @@ public static void quicklyVerify(final Bag bag) throws IOException, InvalidPaylo * @throws InvalidBagitFileFormatException if the manifest is not formatted properly */ public void isValid(final Bag bag, final boolean ignoreHiddenFiles) throws IOException, FileNotInManifestException, MissingPayloadManifestException, MissingBagitFileException, MissingPayloadDirectoryException, FileNotInPayloadDirectoryException, InterruptedException, MaliciousPathException, CorruptChecksumException, VerificationException, UnsupportedAlgorithmException, InvalidBagitFileFormatException{ + isValid(bag, ignoreHiddenFiles, false); + } + + public void isValid(final Bag bag, final boolean ignoreHiddenFiles, final boolean allowHoley) throws IOException, FileNotInManifestException, MissingPayloadManifestException, MissingBagitFileException, MissingPayloadDirectoryException, FileNotInPayloadDirectoryException, InterruptedException, MaliciousPathException, CorruptChecksumException, VerificationException, UnsupportedAlgorithmException, InvalidBagitFileFormatException{ + isValid(bag, ignoreHiddenFiles, allowHoley, null); + } + + public void isValid(final Bag bag, final boolean ignoreHiddenFiles, final boolean allowHoley, final Map extraHeaders) throws IOException, FileNotInManifestException, MissingPayloadManifestException, MissingBagitFileException, MissingPayloadDirectoryException, FileNotInPayloadDirectoryException, InterruptedException, MaliciousPathException, CorruptChecksumException, VerificationException, UnsupportedAlgorithmException, InvalidBagitFileFormatException{ logger.info(messages.getString("checking_bag_is_valid"), bag.getRootDir()); - isComplete(bag, ignoreHiddenFiles); - + final boolean holey = allowHoley || !bag.getItemsToFetch().isEmpty(); + isComplete(bag, ignoreHiddenFiles, holey); + + final Map fetchItems = new HashMap<>(); + if (holey) { + for (final FetchItem item : bag.getItemsToFetch()) { + fetchItems.put(item.path, item); + } + } + logger.debug(messages.getString("checking_payload_checksums")); for(final Manifest payloadManifest : bag.getPayLoadManifests()){ - checkHashes(payloadManifest); + checkHashes(payloadManifest, fetchItems, holey, extraHeaders); } - + logger.debug(messages.getString("checking_tag_file_checksums")); for(final Manifest tagManifest : bag.getTagManifests()){ - checkHashes(tagManifest); + checkHashes(tagManifest, null, false, extraHeaders); } } - + /* * Check the supplied checksum hashes against the generated checksum hashes */ @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") void checkHashes(final Manifest manifest) throws CorruptChecksumException, InterruptedException, VerificationException{ + checkHashes(manifest, null, false); + } + + void checkHashes(final Manifest manifest, final Map fetchItems, final boolean holey) throws CorruptChecksumException, InterruptedException, VerificationException{ + checkHashes(manifest, fetchItems, holey, null); + } + + void checkHashes(final Manifest manifest, final Map fetchItems, final boolean holey, final Map extraHeaders) throws CorruptChecksumException, InterruptedException, VerificationException{ final CountDownLatch latch = new CountDownLatch( manifest.getFileToChecksumMap().size()); - + //TODO maybe return all of these at some point... final Collection exceptions = Collections.synchronizedCollection(new ArrayList<>()); - + for(final Entry entry : manifest.getFileToChecksumMap().entrySet()){ - executor.execute(new CheckManifestHashesTask(entry, manifest.getAlgorithm().getMessageDigestName(), latch, exceptions)); + executor.execute(new CheckManifestHashesTask(entry, manifest.getAlgorithm().getMessageDigestName(), latch, exceptions, fetchItems, holey, extraHeaders)); } - + latch.await(); - + if(!exceptions.isEmpty()){ final Exception e = exceptions.iterator().next(); if(e instanceof CorruptChecksumException){ logger.debug(messages.getString("checksums_not_matching_error"), exceptions.size()); throw (CorruptChecksumException)e; } - + throw new VerificationException(e); } } @@ -215,17 +243,27 @@ void checkHashes(final Manifest manifest) throws CorruptChecksumException, Inter public void isComplete(final Bag bag, final boolean ignoreHiddenFiles) throws IOException, MissingPayloadManifestException, MissingBagitFileException, MissingPayloadDirectoryException, FileNotInPayloadDirectoryException, InterruptedException, MaliciousPathException, UnsupportedAlgorithmException, InvalidBagitFileFormatException{ + isComplete(bag, ignoreHiddenFiles, false); + } + + public void isComplete(final Bag bag, final boolean ignoreHiddenFiles, final boolean allowHoley) throws + IOException, MissingPayloadManifestException, MissingBagitFileException, MissingPayloadDirectoryException, + FileNotInPayloadDirectoryException, InterruptedException, MaliciousPathException, UnsupportedAlgorithmException, InvalidBagitFileFormatException{ logger.info(messages.getString("checking_bag_is_complete"), bag.getRootDir()); - - MandatoryVerifier.checkFetchItemsExist(bag.getItemsToFetch(), bag.getRootDir()); - + + final boolean holey = allowHoley && !bag.getItemsToFetch().isEmpty(); + + if (!holey) { + MandatoryVerifier.checkFetchItemsExist(bag.getItemsToFetch(), bag.getRootDir()); + } + MandatoryVerifier.checkBagitFileExists(bag.getRootDir(), bag.getVersion()); - + MandatoryVerifier.checkPayloadDirectoryExists(bag); - + MandatoryVerifier.checkIfAtLeastOnePayloadManifestsExist(bag.getRootDir(), bag.getVersion()); - - manifestVerifier.verifyManifests(bag, ignoreHiddenFiles); + + manifestVerifier.verifyManifests(bag, ignoreHiddenFiles, holey); } public ExecutorService getExecutor() { diff --git a/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java b/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java index 79c847d2b..0c3c16546 100644 --- a/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java @@ -16,15 +16,18 @@ package nl.knaw.dans.bagit.verify; import java.io.IOException; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Collection; +import java.util.Map; import java.util.Map.Entry; import java.util.ResourceBundle; import java.util.concurrent.CountDownLatch; +import nl.knaw.dans.bagit.domain.FetchItem; import nl.knaw.dans.bagit.exceptions.CorruptChecksumException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,31 +47,61 @@ public class CheckManifestHashesTask implements Runnable { private transient final CountDownLatch latch; private transient final Collection exceptions; private transient final String algorithm; - + private transient final Map fetchItems; + private transient final boolean holey; + private transient final Map extraHeaders; + public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions) { + this(entry, algorithm, latch, exceptions, null, false, null); + } + + public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey) { + this(entry, algorithm, latch, exceptions, fetchItems, holey, null); + } + + public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey, final Map extraHeaders) { this.entry = entry; this.algorithm = algorithm; this.latch = latch; this.exceptions = exceptions; + this.fetchItems = fetchItems; + this.holey = holey; + this.extraHeaders = extraHeaders; } @Override public void run() { try { final MessageDigest messageDigest = MessageDigest.getInstance(algorithm); - checkManifestEntry(entry, messageDigest, algorithm); + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, holey, extraHeaders); } catch (IOException | CorruptChecksumException | NoSuchAlgorithmException e) { exceptions.add(e); } latch.countDown(); } - - protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm) throws IOException, CorruptChecksumException{ - if(Files.exists(entry.getKey())){ + + protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm) throws IOException, CorruptChecksumException { + checkManifestEntry(entry, messageDigest, algorithm, null, false, null); + } + + protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley) throws IOException, CorruptChecksumException { + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, null); + } + + protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley, final Map extraHeaders) throws IOException, CorruptChecksumException { + if (Files.exists(entry.getKey())) { logger.debug(messages.getString("checking_checksums"), entry.getKey(), entry.getValue()); final String hash = Hasher.hash(entry.getKey(), messageDigest); logger.debug("computed hash [{}] for file [{}]", hash, entry.getKey()); - if(!hash.equals(entry.getValue())){ + if (!hash.equals(entry.getValue())) { + throw new CorruptChecksumException(messages.getString("corrupt_checksum_error"), entry.getKey(), algorithm, entry.getValue(), hash); + } + } else if (allowHoley && fetchItems != null && fetchItems.containsKey(entry.getKey())) { + final FetchItem item = fetchItems.get(entry.getKey()); + logger.debug("File {} does not exist, but it is in fetch.txt, and allowHoley is true. Hashing from URL: {}", entry.getKey(), item.url); + final String hash = Hasher.hash(item, messageDigest, extraHeaders); + logger.debug("computed hash [{}] for url [{}]", hash, item.url); + if (!hash.equals(entry.getValue())) { throw new CorruptChecksumException(messages.getString("corrupt_checksum_error"), entry.getKey(), algorithm, entry.getValue(), hash); } } diff --git a/src/main/java/nl/knaw/dans/bagit/verify/ManifestVerifier.java b/src/main/java/nl/knaw/dans/bagit/verify/ManifestVerifier.java index 453e24cd2..1eb5d10a8 100644 --- a/src/main/java/nl/knaw/dans/bagit/verify/ManifestVerifier.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/ManifestVerifier.java @@ -16,10 +16,12 @@ package nl.knaw.dans.bagit.verify; import java.io.IOException; -import java.nio.file.DirectoryStream; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.ConcurrentSkipListSet; @@ -28,11 +30,11 @@ import java.util.concurrent.Executors; import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.domain.FetchItem; import nl.knaw.dans.bagit.exceptions.FileNotInPayloadDirectoryException; import nl.knaw.dans.bagit.exceptions.InvalidBagitFileFormatException; import nl.knaw.dans.bagit.exceptions.MaliciousPathException; import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; -import nl.knaw.dans.bagit.reader.ManifestReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.helpers.MessageFormatter; @@ -111,39 +113,82 @@ public void close() throws SecurityException{ * @throws FileNotInPayloadDirectoryException if a file is listed in a manifest but doesn't exist in the payload directory * @throws InterruptedException if a thread is interrupted while doing work */ + /* + * Verify that all the files in the payload directory are listed in the payload manifest and + * all files listed in all manifests exist. + * + * @param bag the bag to check to check + * @param ignoreHiddenFiles to ignore hidden files unless they are specifically listed in a manifest + * + * @throws IOException if there is a problem reading a file + * @throws MaliciousPathException the path in the manifest was specifically crafted to cause harm + * @throws UnsupportedAlgorithmException if the algorithm used for the manifest is unsupported + * @throws InvalidBagitFileFormatException if any of the manifests don't conform to the bagit specification + * @throws FileNotInPayloadDirectoryException if a file is listed in a manifest but doesn't exist in the payload directory + * @throws InterruptedException if a thread is interrupted while doing work + */ public void verifyManifests(final Bag bag, final boolean ignoreHiddenFiles) throws IOException, MaliciousPathException, UnsupportedAlgorithmException, InvalidBagitFileFormatException, FileNotInPayloadDirectoryException, InterruptedException { - - final Set allFilesListedInManifests = getAllFilesListedInManifests(bag); - checkAllFilesListedInManifestExist(allFilesListedInManifests); + verifyManifests(bag, ignoreHiddenFiles, false); + } + + public void verifyManifests(final Bag bag, final boolean ignoreHiddenFiles, final boolean holey) + throws IOException, MaliciousPathException, UnsupportedAlgorithmException, + InvalidBagitFileFormatException, FileNotInPayloadDirectoryException, InterruptedException { + + checkAlgorithms(bag.getPayLoadManifests()); + checkAlgorithms(bag.getTagManifests()); + + final Set payloadFiles = getFilesListedInPayloadManifests(bag); + final Set tagFiles = getFilesListedInTagManifests(bag); + + checkAllFilesListedInManifestExist(payloadFiles, holey, bag); + checkAllFilesListedInManifestExist(tagFiles, false, bag); + + final Set allFilesListedInManifests = new HashSet<>(payloadFiles); + allFilesListedInManifests.addAll(tagFiles); if (bag.getVersion().isOlder(new Version(1, 0))) { checkAllFilesInPayloadDirAreListedInAtLeastOneAManifest(allFilesListedInManifests, PathUtils.getDataDir(bag), ignoreHiddenFiles); } else { - CheckAllFilesInPayloadDirAreListedInAllManifests(bag.getPayLoadManifests(), PathUtils.getDataDir(bag), ignoreHiddenFiles); + checkAllFilesInPayloadDirAreListedInAllManifests(bag.getPayLoadManifests(), PathUtils.getDataDir(bag), ignoreHiddenFiles); + } + } + + private void checkAlgorithms(final Set manifests) throws UnsupportedAlgorithmException { + for (final Manifest manifest : manifests) { + if (nameMapping.getSupportedAlgorithm(manifest.getAlgorithm().getBagitName()) == null) { + throw new UnsupportedAlgorithmException(messages.getString("unsupported_algorithm_error"), manifest.getAlgorithm().getBagitName(), null); + } } } /* - * get all the files listed in all the manifests + * get all the files listed in the payload manifests */ - private Set getAllFilesListedInManifests(final Bag bag) + private Set getFilesListedInPayloadManifests(final Bag bag) throws IOException, MaliciousPathException, UnsupportedAlgorithmException, InvalidBagitFileFormatException { logger.debug(messages.getString("all_files_in_manifests")); final Set filesListedInManifests = new HashSet<>(); - try(DirectoryStream directoryStream = - Files.newDirectoryStream(PathUtils.getBagitDir(bag.getVersion(), bag.getRootDir()))){ - for (final Path path : directoryStream) { - final String filename = PathUtils.getFilename(path); - if (filename.startsWith("tagmanifest-") || filename.startsWith("manifest-")) { - logger.debug(messages.getString("get_listing_in_manifest"), path); - final Manifest manifest = ManifestReader.readManifest(nameMapping, path, bag.getRootDir(), - bag.getFileEncoding()); - filesListedInManifests.addAll(manifest.getFileToChecksumMap().keySet()); - } - } + for (final Manifest manifest : bag.getPayLoadManifests()) { + filesListedInManifests.addAll(manifest.getFileToChecksumMap().keySet()); + } + + return filesListedInManifests; + } + + /* + * get all the files listed in the tag manifests + */ + private Set getFilesListedInTagManifests(final Bag bag) + throws IOException, MaliciousPathException, UnsupportedAlgorithmException, InvalidBagitFileFormatException { + logger.debug(messages.getString("all_files_in_manifests")); + final Set filesListedInManifests = new HashSet<>(); + + for (final Manifest manifest : bag.getTagManifests()) { + filesListedInManifests.addAll(manifest.getFileToChecksumMap().keySet()); } return filesListedInManifests; @@ -153,13 +198,25 @@ private Set getAllFilesListedInManifests(final Bag bag) * Make sure all the listed files actually exist */ @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - private void checkAllFilesListedInManifestExist(final Set files) throws FileNotInPayloadDirectoryException, InterruptedException { + private void checkAllFilesListedInManifestExist(final Set files, final boolean holey, final Bag bag) throws FileNotInPayloadDirectoryException, InterruptedException { final CountDownLatch latch = new CountDownLatch(files.size()); final Set missingFiles = new ConcurrentSkipListSet<>(); + final Map fetchUrls = new HashMap<>(); + if (holey) { + for (final FetchItem item : bag.getItemsToFetch()) { + fetchUrls.put(item.path, item.url); + } + } + logger.info(messages.getString("check_all_files_in_manifests_exist")); for (final Path file : files) { - executor.execute(new CheckIfFileExistsTask(file, missingFiles, latch)); + if (holey && fetchUrls.containsKey(file)) { + // Not actually checking that the file can be downloaded. That will be done when calculating the checksums later on. + latch.countDown(); + } else { + executor.execute(new CheckIfFileExistsTask(file, missingFiles, latch)); + } } latch.await(); @@ -170,6 +227,14 @@ private void checkAllFilesListedInManifestExist(final Set files) throws Fi } } + /* + * Make sure all the listed files actually exist + */ + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + private void checkAllFilesListedInManifestExist(final Set files) throws FileNotInPayloadDirectoryException, InterruptedException { + checkAllFilesListedInManifestExist(files, false, null); + } + /* * Make sure all files in the directory are in at least 1 manifest */ @@ -185,7 +250,7 @@ private static void checkAllFilesInPayloadDirAreListedInAtLeastOneAManifest(fina /* * as per the bagit-spec 1.0+ all files have to be listed in all manifests */ - private static void CheckAllFilesInPayloadDirAreListedInAllManifests(final Set payLoadManifests, + private static void checkAllFilesInPayloadDirAreListedInAllManifests(final Set payLoadManifests, final Path payloadDir, final boolean ignoreHiddenFiles) throws IOException { logger.debug(messages.getString("checking_file_in_all_manifests"), payloadDir); if (Files.exists(payloadDir)) { diff --git a/src/test/java/nl/knaw/dans/bagit/hash/HasherUrlTest.java b/src/test/java/nl/knaw/dans/bagit/hash/HasherUrlTest.java new file mode 100644 index 000000000..1f3e67583 --- /dev/null +++ b/src/test/java/nl/knaw/dans/bagit/hash/HasherUrlTest.java @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.hash; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.atomic.AtomicInteger; + +public class HasherUrlTest { + + private HttpServer server; + private static final String TEST_DATA = "This is some test data that should be hashed correctly even with retries and range requests."; + private static final byte[] TEST_DATA_BYTES = TEST_DATA.getBytes(StandardCharsets.UTF_8); + private AtomicInteger requestCount = new AtomicInteger(0); + + @BeforeEach + public void setup() throws IOException { + server = HttpServer.create(new InetSocketAddress(0), 0); + server.createContext("/test", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + requestCount.incrementAndGet(); + String range = exchange.getRequestHeaders().getFirst("Range"); + + if (range == null) { + // Full request + exchange.getResponseHeaders().set("Accept-Ranges", "bytes"); + exchange.getResponseHeaders().set("Content-Length", String.valueOf(TEST_DATA_BYTES.length)); + exchange.sendResponseHeaders(200, TEST_DATA_BYTES.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(TEST_DATA_BYTES); + } + } else if (range.startsWith("bytes=")) { + // Range request + String[] parts = range.substring(6).split("-"); + int start = Integer.parseInt(parts[0]); + int end = parts.length > 1 && !parts[1].isEmpty() ? Integer.parseInt(parts[1]) : TEST_DATA_BYTES.length - 1; + + if (start >= TEST_DATA_BYTES.length) { + exchange.sendResponseHeaders(416, -1); + return; + } + + int length = end - start + 1; + exchange.getResponseHeaders().set("Content-Range", "bytes " + start + "-" + end + "/" + TEST_DATA_BYTES.length); + exchange.sendResponseHeaders(206, length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(TEST_DATA_BYTES, start, length); + } + } else { + exchange.sendResponseHeaders(400, -1); + } + } + }); + server.start(); + } + + @AfterEach + public void teardown() { + if (server != null) { + server.stop(0); + } + System.clearProperty("nl.knaw.dans.bagit.hash.chunkSize"); + System.clearProperty("nl.knaw.dans.bagit.hash.maxRetries"); + System.clearProperty("nl.knaw.dans.bagit.hash.retrySleepMs"); + } + + @Test + public void testHashWithRangeRequests() throws IOException, NoSuchAlgorithmException { + System.setProperty("nl.knaw.dans.bagit.hash.chunkSize", "10"); + URL url = new URL("http://localhost:" + server.getAddress().getPort() + "/test"); + MessageDigest md = MessageDigest.getInstance("SHA-1"); + + String hash = Hasher.hash(url, md); + + // Expected SHA-1 of TEST_DATA + MessageDigest expectedMd = MessageDigest.getInstance("SHA-1"); + expectedMd.update(TEST_DATA_BYTES); + byte[] digest = expectedMd.digest(); + StringBuilder sb = new StringBuilder(); + for (byte b : digest) { + sb.append(String.format("%02x", b)); + } + String expectedHash = sb.toString(); + + Assertions.assertEquals(expectedHash, hash); + // With chunk size 10 and total length ~90, we expect around 10-11 requests (1 HEAD/initial + chunks) + Assertions.assertTrue(requestCount.get() > 1); + } + + @Test + public void testHashWithRetries() throws IOException, NoSuchAlgorithmException { + server.removeContext("/test"); + AtomicInteger failCount = new AtomicInteger(0); + server.createContext("/test", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + int count = requestCount.incrementAndGet(); + if (count == 2 || count == 3) { + // Fail the second and third request (first chunk requests) + exchange.sendResponseHeaders(500, -1); + return; + } + + String range = exchange.getRequestHeaders().getFirst("Range"); + if (range == null) { + exchange.getResponseHeaders().set("Accept-Ranges", "bytes"); + exchange.getResponseHeaders().set("Content-Length", String.valueOf(TEST_DATA_BYTES.length)); + exchange.sendResponseHeaders(200, TEST_DATA_BYTES.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(TEST_DATA_BYTES); + } + } else { + String[] parts = range.substring(6).split("-"); + int start = Integer.parseInt(parts[0]); + int end = parts.length > 1 && !parts[1].isEmpty() ? Integer.parseInt(parts[1]) : TEST_DATA_BYTES.length - 1; + int length = end - start + 1; + exchange.getResponseHeaders().set("Content-Range", "bytes " + start + "-" + end + "/" + TEST_DATA_BYTES.length); + exchange.sendResponseHeaders(206, length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(TEST_DATA_BYTES, start, length); + } + } + } + }); + + System.setProperty("nl.knaw.dans.bagit.hash.chunkSize", "20"); + System.setProperty("nl.knaw.dans.bagit.hash.maxRetries", "3"); + System.setProperty("nl.knaw.dans.bagit.hash.retrySleepMs", "100"); + + URL url = new URL("http://localhost:" + server.getAddress().getPort() + "/test"); + MessageDigest md = MessageDigest.getInstance("SHA-1"); + + String hash = Hasher.hash(url, md); + + MessageDigest expectedMd = MessageDigest.getInstance("SHA-1"); + expectedMd.update(TEST_DATA_BYTES); + byte[] digest = expectedMd.digest(); + StringBuilder sb = new StringBuilder(); + for (byte b : digest) { + sb.append(String.format("%02x", b)); + } + String expectedHash = sb.toString(); + + Assertions.assertEquals(expectedHash, hash); + Assertions.assertTrue(requestCount.get() >= 5); // 1 initial + 2 failed + successful chunks + } + + @Test + public void testHashWithoutRangeSupport() throws IOException, NoSuchAlgorithmException { + server.removeContext("/test"); + server.createContext("/test", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + requestCount.incrementAndGet(); + // No Accept-Ranges header + exchange.getResponseHeaders().set("Content-Length", String.valueOf(TEST_DATA_BYTES.length)); + exchange.sendResponseHeaders(200, TEST_DATA_BYTES.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(TEST_DATA_BYTES); + } + } + }); + + URL url = new URL("http://localhost:" + server.getAddress().getPort() + "/test"); + MessageDigest md = MessageDigest.getInstance("SHA-1"); + + String hash = Hasher.hash(url, md); + + MessageDigest expectedMd = MessageDigest.getInstance("SHA-1"); + expectedMd.update(TEST_DATA_BYTES); + byte[] digest = expectedMd.digest(); + StringBuilder sb = new StringBuilder(); + for (byte b : digest) { + sb.append(String.format("%02x", b)); + } + String expectedHash = sb.toString(); + + Assertions.assertEquals(expectedHash, hash); + // Only 1 request: the first range request which returns 200 OK and is treated as the full stream + Assertions.assertEquals(1, requestCount.get()); + } + + @Test + public void testHashWithFailedFirstRangeRequest() throws IOException, NoSuchAlgorithmException { + server.removeContext("/test"); + server.createContext("/test", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + requestCount.incrementAndGet(); + if (exchange.getRequestHeaders().containsKey("Range")) { + // Fail range request after 5 attempts (to trigger fallback) + exchange.sendResponseHeaders(405, -1); + } else { + exchange.getResponseHeaders().set("Content-Length", String.valueOf(TEST_DATA_BYTES.length)); + exchange.sendResponseHeaders(200, TEST_DATA_BYTES.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(TEST_DATA_BYTES); + } + } + } + }); + + System.setProperty("nl.knaw.dans.bagit.hash.maxRetries", "1"); + try { + URL url = new URL("http://localhost:" + server.getAddress().getPort() + "/test"); + MessageDigest md = MessageDigest.getInstance("SHA-1"); + + String hash = Hasher.hash(url, md); + + MessageDigest expectedMd = MessageDigest.getInstance("SHA-1"); + expectedMd.update(TEST_DATA_BYTES); + byte[] digest = expectedMd.digest(); + StringBuilder sb = new StringBuilder(); + for (byte b : digest) { + sb.append(String.format("%02x", b)); + } + String expectedHash = sb.toString(); + + Assertions.assertEquals(expectedHash, hash); + // Expect 2 requests: 1 range request (failed) and 1 GET (fallback to full stream) + Assertions.assertEquals(2, requestCount.get()); + } finally { + System.clearProperty("nl.knaw.dans.bagit.hash.maxRetries"); + } + } + + @Test + public void testHashWithFileUrl() throws IOException, NoSuchAlgorithmException { + java.nio.file.Path tempFile = java.nio.file.Files.createTempFile("hasher-file-url-test", ".txt"); + try { + java.nio.file.Files.write(tempFile, TEST_DATA_BYTES); + URL fileUrl = tempFile.toUri().toURL(); + MessageDigest md = MessageDigest.getInstance("SHA-1"); + + String hash = Hasher.hash(fileUrl, md); + + MessageDigest expectedMd = MessageDigest.getInstance("SHA-1"); + expectedMd.update(TEST_DATA_BYTES); + byte[] digest = expectedMd.digest(); + StringBuilder sb = new StringBuilder(); + for (byte b : digest) { + sb.append(String.format("%02x", b)); + } + String expectedHash = sb.toString(); + + Assertions.assertEquals(expectedHash, hash); + } finally { + java.nio.file.Files.deleteIfExists(tempFile); + } + } +} diff --git a/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRedirectTest.java b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRedirectTest.java new file mode 100644 index 000000000..f7f5d7297 --- /dev/null +++ b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRedirectTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import nl.knaw.dans.bagit.TempFolderTest; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.reader.BagReader; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +public class BagVerifierRedirectTest extends TempFolderTest { + private HttpServer server1; + private HttpServer server2; + private BagVerifier sut; + private BagReader reader; + private int port1; + private int port2; + + @BeforeEach + public void setup() throws IOException { + sut = new BagVerifier(); + reader = new BagReader(); + + server1 = HttpServer.create(new InetSocketAddress(0), 0); + server1.setExecutor(null); + server1.start(); + port1 = server1.getAddress().getPort(); + + server2 = HttpServer.create(new InetSocketAddress(0), 0); + server2.setExecutor(null); + server2.start(); + port2 = server2.getAddress().getPort(); + } + + @AfterEach + public void tearDown() { + if (server1 != null) server1.stop(0); + if (server2 != null) server2.stop(0); + if (sut != null) sut.close(); + } + + @Test + public void testRedirectStripsHeadersOnHostChange() throws Exception { + final String content = "final content"; + final String authHeaderName = "X-Dataverse-key"; + final String authHeaderValue = "secret"; + final AtomicBoolean headersSentToServer2 = new AtomicBoolean(false); + + // Server 1 redirects to Server 2 + server1.createContext("/api/access/datafile/1", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + if (authHeaderValue.equals(exchange.getRequestHeaders().getFirst(authHeaderName))) { + exchange.getResponseHeaders().set("Location", "http://localhost:" + port2 + "/storage/file1?token=xyz"); + exchange.sendResponseHeaders(302, -1); + } else { + exchange.sendResponseHeaders(401, -1); + } + } + }); + + // Server 2 returns content but fails if auth header is present + server2.createContext("/storage/file1", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + if (exchange.getRequestHeaders().containsKey(authHeaderName)) { + headersSentToServer2.set(true); + // Simulate 403 Forbidden because S3 rejects requests with unknown/extra auth headers + exchange.sendResponseHeaders(403, -1); + } else { + byte[] response = content.getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(200, response.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(response); + } + } + } + }); + + Path bagDir = Files.createTempDirectory(folder, "redirect-headers-bag"); + Files.write(bagDir.resolve("bagit.txt"), "BagIt-Version: 0.97\nTag-File-Character-Encoding: UTF-8\n".getBytes()); + Files.createDirectory(bagDir.resolve("data")); + + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(content.getBytes(StandardCharsets.UTF_8)); + String hash = formatMessageDigest(md5.digest()); + + Files.write(bagDir.resolve("manifest-md5.txt"), (hash + " data/test.txt\n").getBytes()); + + URL remoteUrl = new URL("http://localhost:" + port1 + "/api/access/datafile/1"); + Files.write(bagDir.resolve("fetch.txt"), (remoteUrl.toString() + " - data/test.txt\n").getBytes()); + + Bag bag = reader.read(bagDir); + + Map headers = new HashMap<>(); + headers.put(authHeaderName, authHeaderValue); + + // This should now PASS because Hasher.java strips headers on host change + sut.isValid(bag, true, true, headers); + + Assertions.assertFalse(headersSentToServer2.get(), "Headers should NOT have been sent to server2"); + } + + private String formatMessageDigest(final byte[] digest) { + StringBuilder sb = new StringBuilder(); + for (byte b : digest) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } +} diff --git a/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRemoteTest.java b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRemoteTest.java new file mode 100644 index 000000000..88a9d3993 --- /dev/null +++ b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRemoteTest.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import nl.knaw.dans.bagit.TempFolderTest; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.reader.BagReader; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +public class BagVerifierRemoteTest extends TempFolderTest { + private HttpServer server; + private BagVerifier sut; + private BagReader reader; + private int port; + + @BeforeEach + public void setup() throws IOException { + sut = new BagVerifier(); + reader = new BagReader(); + server = HttpServer.create(new InetSocketAddress(0), 0); + server.setExecutor(null); + server.start(); + port = server.getAddress().getPort(); + } + + @AfterEach + public void tearDown() { + if (server != null) server.stop(0); + if (sut != null) sut.close(); + } + + @Test + public void testAuthenticatedRangeRequest() throws Exception { + final String content = "this is some test content for range requests"; + final String authHeaderName = "X-Dataverse-key"; + final String authHeaderValue = "secret-key"; + final AtomicInteger rangeRequestCount = new AtomicInteger(0); + + server.createContext("/datafile", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + if (!authHeaderValue.equals(exchange.getRequestHeaders().getFirst(authHeaderName))) { + exchange.sendResponseHeaders(401, -1); + return; + } + + String range = exchange.getRequestHeaders().getFirst("Range"); + if (range != null && range.startsWith("bytes=")) { + rangeRequestCount.incrementAndGet(); + String[] parts = range.substring(6).split("-"); + int start = Integer.parseInt(parts[0]); + int end = Integer.parseInt(parts[1]); + byte[] fullContent = content.getBytes(StandardCharsets.UTF_8); + int actualEnd = Math.min(end, fullContent.length - 1); + int length = actualEnd - start + 1; + + exchange.getResponseHeaders().set("Content-Range", "bytes " + start + "-" + actualEnd + "/" + fullContent.length); + exchange.sendResponseHeaders(206, length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(fullContent, start, length); + } + } else { + byte[] response = content.getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(200, response.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(response); + } + } + } + }); + + Path bagDir = Files.createTempDirectory(folder, "remote-bag"); + Files.write(bagDir.resolve("bagit.txt"), "BagIt-Version: 0.97\nTag-File-Character-Encoding: UTF-8\n".getBytes()); + Files.createDirectory(bagDir.resolve("data")); + + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + sha1.update(content.getBytes(StandardCharsets.UTF_8)); + String hash = formatMessageDigest(sha1.digest()); + + Files.write(bagDir.resolve("manifest-sha1.txt"), (hash + " data/test.txt\n").getBytes()); + + URL remoteUrl = new URL("http://localhost:" + port + "/datafile"); + Files.write(bagDir.resolve("fetch.txt"), (remoteUrl.toString() + " " + content.length() + " data/test.txt\n").getBytes()); + + Bag bag = reader.read(bagDir); + + Map headers = new HashMap<>(); + headers.put(authHeaderName, authHeaderValue); + + // Use a small chunk size to force multiple range requests + System.setProperty("nl.knaw.dans.bagit.hash.chunkSize", "10"); + try { + sut.isValid(bag, true, true, headers); + } finally { + System.clearProperty("nl.knaw.dans.bagit.hash.chunkSize"); + } + + Assertions.assertTrue(rangeRequestCount.get() > 1, "Should have used multiple range requests, but used: " + rangeRequestCount.get()); + } + + @Test + public void testFallbackToFullDownloadWhenRangeNotSupported() throws Exception { + final String content = "this content will be downloaded in one go"; + final AtomicBoolean rangeAttempted = new AtomicBoolean(false); + + server.createContext("/datafile", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + if (exchange.getRequestHeaders().containsKey("Range")) { + rangeAttempted.set(true); + // Server ignores range and returns 200 OK + byte[] response = content.getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(200, response.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(response); + } + } else { + byte[] response = content.getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(200, response.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(response); + } + } + } + }); + + Path bagDir = Files.createTempDirectory(folder, "remote-bag-fallback"); + Files.write(bagDir.resolve("bagit.txt"), "BagIt-Version: 0.97\nTag-File-Character-Encoding: UTF-8\n".getBytes()); + Files.createDirectory(bagDir.resolve("data")); + + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + sha1.update(content.getBytes(StandardCharsets.UTF_8)); + String hash = formatMessageDigest(sha1.digest()); + + Files.write(bagDir.resolve("manifest-sha1.txt"), (hash + " data/test.txt\n").getBytes()); + + URL remoteUrl = new URL("http://localhost:" + port + "/datafile"); + Files.write(bagDir.resolve("fetch.txt"), (remoteUrl.toString() + " " + content.length() + " data/test.txt\n").getBytes()); + + Bag bag = reader.read(bagDir); + + sut.isValid(bag, true, true, null); + + Assertions.assertTrue(rangeAttempted.get(), "Should have attempted a range request"); + } + + private String formatMessageDigest(final byte[] digest) { + StringBuilder sb = new StringBuilder(); + for (byte b : digest) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } +} diff --git a/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java index e3b57e35e..7ed41a3fc 100644 --- a/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java +++ b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java @@ -16,6 +16,8 @@ package nl.knaw.dans.bagit.verify; import java.io.File; +import java.net.URL; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -33,6 +35,7 @@ import nl.knaw.dans.bagit.domain.Manifest; import nl.knaw.dans.bagit.exceptions.CorruptChecksumException; import nl.knaw.dans.bagit.exceptions.FileNotInManifestException; +import nl.knaw.dans.bagit.exceptions.FileNotInPayloadDirectoryException; import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; import nl.knaw.dans.bagit.exceptions.VerificationException; import nl.knaw.dans.bagit.hash.StandardSupportedAlgorithms; @@ -236,4 +239,36 @@ public void testQuickVerify() throws Exception{ BagVerifier.quicklyVerify(bag); } + + @Test + public void testHoleyBag() throws Exception { + Path bagDir = Paths.get("src", "test", "resources", "md5Bag"); + Path copyDir = copyBagToTempFolder(bagDir); + Path readme = copyDir.resolve("data/readme.txt"); + byte[] content = Files.readAllBytes(readme); + Files.delete(readme); + + // Create a local file to serve as "remote" resource + Path remoteFile = copyDir.resolve("remote-readme.txt"); + Files.write(remoteFile, content); + URL remoteUrl = remoteFile.toUri().toURL(); + + // Create fetch.txt + Path fetchFile = copyDir.resolve("fetch.txt"); + // Format of fetch.txt: url length path + // IMPORTANT: BagReader uses relative paths from root for fetch items, + // but they should NOT have 'data/' prefix if they are in data directory? + // Actually, BagIt spec says it's the path relative to the bag root. + String fetchLine = remoteUrl.toString() + " " + content.length + " data/readme.txt\n"; + Files.write(fetchFile, fetchLine.getBytes()); + + Bag bag = reader.read(copyDir); + + // With the new logic, it should be valid even without explicitly passing true, + // because fetch.txt is present and contains data/readme.txt + sut.isValid(bag, true); + + // It should also be valid if we explicitly pass true + sut.isValid(bag, true, true); + } } From 93ca69072601782732c23ab9736e10c22ad9ee8d Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 13 Mar 2026 11:56:12 +0100 Subject: [PATCH 073/104] [maven-release-plugin] prepare release v1.3.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 668d4f093..7b63dcf03 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.2.3-SNAPSHOT + 1.3.0 DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - 1.0.0-SNAPSHOT + v1.3.0 From a746157931144bdd660d58c1576bdaa438503930 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 13 Mar 2026 11:56:16 +0100 Subject: [PATCH 074/104] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7b63dcf03..5f5409726 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.3.0 + 1.3.1-SNAPSHOT DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - v1.3.0 + 1.0.0-SNAPSHOT From 1282d82fa2d9e3c07602f1eaffeba89429326e16 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 13 Mar 2026 12:54:02 +0100 Subject: [PATCH 075/104] Allow per-server config of extra headers for fetch operations --- .../knaw/dans/bagit/verify/BagVerifier.java | 14 +- .../bagit/verify/CheckManifestHashesTask.java | 43 +++- .../verify/BagVerifierUrlConfigTest.java | 238 ++++++++++++++++++ 3 files changed, 286 insertions(+), 9 deletions(-) create mode 100644 src/test/java/nl/knaw/dans/bagit/verify/BagVerifierUrlConfigTest.java diff --git a/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java b/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java index 38b3ee30f..7088b8585 100644 --- a/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java @@ -159,6 +159,10 @@ public void isValid(final Bag bag, final boolean ignoreHiddenFiles, final boolea } public void isValid(final Bag bag, final boolean ignoreHiddenFiles, final boolean allowHoley, final Map extraHeaders) throws IOException, FileNotInManifestException, MissingPayloadManifestException, MissingBagitFileException, MissingPayloadDirectoryException, FileNotInPayloadDirectoryException, InterruptedException, MaliciousPathException, CorruptChecksumException, VerificationException, UnsupportedAlgorithmException, InvalidBagitFileFormatException{ + isValid(bag, ignoreHiddenFiles, allowHoley, extraHeaders, null); + } + + public void isValid(final Bag bag, final boolean ignoreHiddenFiles, final boolean allowHoley, final Map extraHeaders, final Map> urlConfigs) throws IOException, FileNotInManifestException, MissingPayloadManifestException, MissingBagitFileException, MissingPayloadDirectoryException, FileNotInPayloadDirectoryException, InterruptedException, MaliciousPathException, CorruptChecksumException, VerificationException, UnsupportedAlgorithmException, InvalidBagitFileFormatException{ logger.info(messages.getString("checking_bag_is_valid"), bag.getRootDir()); final boolean holey = allowHoley || !bag.getItemsToFetch().isEmpty(); isComplete(bag, ignoreHiddenFiles, holey); @@ -172,12 +176,12 @@ public void isValid(final Bag bag, final boolean ignoreHiddenFiles, final boolea logger.debug(messages.getString("checking_payload_checksums")); for(final Manifest payloadManifest : bag.getPayLoadManifests()){ - checkHashes(payloadManifest, fetchItems, holey, extraHeaders); + checkHashes(payloadManifest, fetchItems, holey, extraHeaders, urlConfigs); } logger.debug(messages.getString("checking_tag_file_checksums")); for(final Manifest tagManifest : bag.getTagManifests()){ - checkHashes(tagManifest, null, false, extraHeaders); + checkHashes(tagManifest, null, false, extraHeaders, urlConfigs); } } @@ -194,13 +198,17 @@ void checkHashes(final Manifest manifest, final Map fetchItems, } void checkHashes(final Manifest manifest, final Map fetchItems, final boolean holey, final Map extraHeaders) throws CorruptChecksumException, InterruptedException, VerificationException{ + checkHashes(manifest, fetchItems, holey, extraHeaders, null); + } + + void checkHashes(final Manifest manifest, final Map fetchItems, final boolean holey, final Map extraHeaders, final Map> urlConfigs) throws CorruptChecksumException, InterruptedException, VerificationException{ final CountDownLatch latch = new CountDownLatch( manifest.getFileToChecksumMap().size()); //TODO maybe return all of these at some point... final Collection exceptions = Collections.synchronizedCollection(new ArrayList<>()); for(final Entry entry : manifest.getFileToChecksumMap().entrySet()){ - executor.execute(new CheckManifestHashesTask(entry, manifest.getAlgorithm().getMessageDigestName(), latch, exceptions, fetchItems, holey, extraHeaders)); + executor.execute(new CheckManifestHashesTask(entry, manifest.getAlgorithm().getMessageDigestName(), latch, exceptions, fetchItems, holey, extraHeaders, urlConfigs)); } latch.await(); diff --git a/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java b/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java index 0c3c16546..3d66dd7a4 100644 --- a/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java @@ -50,16 +50,21 @@ public class CheckManifestHashesTask implements Runnable { private transient final Map fetchItems; private transient final boolean holey; private transient final Map extraHeaders; + private transient final Map> urlConfigs; public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions) { - this(entry, algorithm, latch, exceptions, null, false, null); + this(entry, algorithm, latch, exceptions, null, false, null, null); } public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey) { - this(entry, algorithm, latch, exceptions, fetchItems, holey, null); + this(entry, algorithm, latch, exceptions, fetchItems, holey, null, null); } public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey, final Map extraHeaders) { + this(entry, algorithm, latch, exceptions, fetchItems, holey, extraHeaders, null); + } + + public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey, final Map extraHeaders, final Map> urlConfigs) { this.entry = entry; this.algorithm = algorithm; this.latch = latch; @@ -67,13 +72,14 @@ public CheckManifestHashesTask(final Entry entry, final String alg this.fetchItems = fetchItems; this.holey = holey; this.extraHeaders = extraHeaders; + this.urlConfigs = urlConfigs; } @Override public void run() { try { final MessageDigest messageDigest = MessageDigest.getInstance(algorithm); - checkManifestEntry(entry, messageDigest, algorithm, fetchItems, holey, extraHeaders); + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, holey, extraHeaders, urlConfigs); } catch (IOException | CorruptChecksumException | NoSuchAlgorithmException e) { exceptions.add(e); } @@ -81,14 +87,18 @@ public void run() { } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm) throws IOException, CorruptChecksumException { - checkManifestEntry(entry, messageDigest, algorithm, null, false, null); + checkManifestEntry(entry, messageDigest, algorithm, null, false, null, null); } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley) throws IOException, CorruptChecksumException { - checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, null); + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, null, null); } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley, final Map extraHeaders) throws IOException, CorruptChecksumException { + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, extraHeaders, null); + } + + protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley, final Map extraHeaders, final Map> urlConfigs) throws IOException, CorruptChecksumException { if (Files.exists(entry.getKey())) { logger.debug(messages.getString("checking_checksums"), entry.getKey(), entry.getValue()); final String hash = Hasher.hash(entry.getKey(), messageDigest); @@ -99,7 +109,8 @@ protected static void checkManifestEntry(final Entry entry, final } else if (allowHoley && fetchItems != null && fetchItems.containsKey(entry.getKey())) { final FetchItem item = fetchItems.get(entry.getKey()); logger.debug("File {} does not exist, but it is in fetch.txt, and allowHoley is true. Hashing from URL: {}", entry.getKey(), item.url); - final String hash = Hasher.hash(item, messageDigest, extraHeaders); + final Map mergedHeaders = mergeHeaders(item.url, extraHeaders, urlConfigs); + final String hash = Hasher.hash(item, messageDigest, mergedHeaders); logger.debug("computed hash [{}] for url [{}]", hash, item.url); if (!hash.equals(entry.getValue())) { throw new CorruptChecksumException(messages.getString("corrupt_checksum_error"), entry.getKey(), algorithm, entry.getValue(), hash); @@ -107,4 +118,24 @@ protected static void checkManifestEntry(final Entry entry, final } //if the file doesn't exist it will be caught by checkAllFilesListedInManifestExist method } + + private static Map mergeHeaders(final URL url, final Map extraHeaders, final Map> urlConfigs) { + if (urlConfigs == null || urlConfigs.isEmpty()) { + return extraHeaders; + } + + final Map mergedHeaders = new java.util.HashMap<>(); + if (extraHeaders != null) { + mergedHeaders.putAll(extraHeaders); + } + + final String urlString = url.toString(); + for (final Entry> configEntry : urlConfigs.entrySet()) { + if (urlString.startsWith(configEntry.getKey())) { + mergedHeaders.putAll(configEntry.getValue()); + } + } + + return mergedHeaders; + } } diff --git a/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierUrlConfigTest.java b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierUrlConfigTest.java new file mode 100644 index 000000000..06a129dd7 --- /dev/null +++ b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierUrlConfigTest.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.knaw.dans.bagit.verify; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import nl.knaw.dans.bagit.TempFolderTest; +import nl.knaw.dans.bagit.domain.Bag; +import nl.knaw.dans.bagit.reader.BagReader; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +public class BagVerifierUrlConfigTest extends TempFolderTest { + private HttpServer server; + private BagVerifier sut; + private BagReader reader; + private int port; + + @BeforeEach + public void setup() throws IOException { + sut = new BagVerifier(); + reader = new BagReader(); + server = HttpServer.create(new InetSocketAddress(0), 0); + server.setExecutor(null); + server.start(); + port = server.getAddress().getPort(); + } + + @AfterEach + public void tearDown() { + if (server != null) server.stop(0); + if (sut != null) sut.close(); + } + + @Test + public void testUrlSpecificHeaders() throws Exception { + final String content = "test content"; + final String globalHeaderName = "X-Global"; + final String globalHeaderValue = "global-val"; + final String urlHeaderName = "X-Url-Specific"; + final String urlHeaderValue = "url-val"; + + final AtomicBoolean globalReceived = new AtomicBoolean(false); + final AtomicBoolean urlSpecificReceived = new AtomicBoolean(false); + + server.createContext("/specific", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + if (globalHeaderValue.equals(exchange.getRequestHeaders().getFirst(globalHeaderName))) { + globalReceived.set(true); + } + if (urlHeaderValue.equals(exchange.getRequestHeaders().getFirst(urlHeaderName))) { + urlSpecificReceived.set(true); + } + + byte[] response = content.getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(200, response.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(response); + } + } + }); + + Path bagDir = Files.createTempDirectory(folder, "url-config-bag"); + Files.write(bagDir.resolve("bagit.txt"), "BagIt-Version: 0.97\nTag-File-Character-Encoding: UTF-8\n".getBytes()); + Files.createDirectory(bagDir.resolve("data")); + + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + sha1.update(content.getBytes(StandardCharsets.UTF_8)); + String hash = formatMessageDigest(sha1.digest()); + + Files.write(bagDir.resolve("manifest-sha1.txt"), (hash + " data/test.txt\n").getBytes()); + + URL remoteUrl = new URL("http://localhost:" + port + "/specific"); + Files.write(bagDir.resolve("fetch.txt"), (remoteUrl.toString() + " " + content.length() + " data/test.txt\n").getBytes()); + + Bag bag = reader.read(bagDir); + + Map globalHeaders = new HashMap<>(); + globalHeaders.put(globalHeaderName, globalHeaderValue); + + Map> urlConfigs = new HashMap<>(); + Map specificHeaders = new HashMap<>(); + specificHeaders.put(urlHeaderName, urlHeaderValue); + urlConfigs.put("http://localhost:" + port + "/specific", specificHeaders); + + sut.isValid(bag, true, true, globalHeaders, urlConfigs); + + Assertions.assertTrue(globalReceived.get(), "Global header should have been received"); + Assertions.assertTrue(urlSpecificReceived.get(), "URL specific header should have been received"); + } + + @Test + public void testUrlPrefixMatching() throws Exception { + final String content = "test content"; + final String headerName = "X-Url-Specific"; + final String headerValue = "url-val"; + + final AtomicBoolean headerReceived = new AtomicBoolean(false); + + server.createContext("/prefix/specific", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + if (headerValue.equals(exchange.getRequestHeaders().getFirst(headerName))) { + headerReceived.set(true); + } + + byte[] response = content.getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(200, response.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(response); + } + } + }); + + Path bagDir = Files.createTempDirectory(folder, "url-prefix-bag"); + Files.write(bagDir.resolve("bagit.txt"), "BagIt-Version: 0.97\nTag-File-Character-Encoding: UTF-8\n".getBytes()); + Files.createDirectory(bagDir.resolve("data")); + + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + sha1.update(content.getBytes(StandardCharsets.UTF_8)); + String hash = formatMessageDigest(sha1.digest()); + + Files.write(bagDir.resolve("manifest-sha1.txt"), (hash + " data/test.txt\n").getBytes()); + + URL remoteUrl = new URL("http://localhost:" + port + "/prefix/specific"); + Files.write(bagDir.resolve("fetch.txt"), (remoteUrl.toString() + " " + content.length() + " data/test.txt\n").getBytes()); + + Bag bag = reader.read(bagDir); + + Map> urlConfigs = new HashMap<>(); + Map specificHeaders = new HashMap<>(); + specificHeaders.put(headerName, headerValue); + // Using a prefix + urlConfigs.put("http://localhost:" + port + "/prefix", specificHeaders); + + sut.isValid(bag, true, true, null, urlConfigs); + + Assertions.assertTrue(headerReceived.get(), "URL specific header should have been received because of prefix match"); + } + + @Test + public void testMultipleUrlConfigs() throws Exception { + final String content1 = "content 1"; + final String content2 = "content 2"; + final String header1 = "X-H1"; + final String header2 = "X-H2"; + + final AtomicBoolean h1Received = new AtomicBoolean(false); + final AtomicBoolean h2Received = new AtomicBoolean(false); + + server.createContext("/url1", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + if ("v1".equals(exchange.getRequestHeaders().getFirst(header1))) h1Received.set(true); + byte[] response = content1.getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(200, response.length); + try (OutputStream os = exchange.getResponseBody()) { os.write(response); } + } + }); + server.createContext("/url2", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + if ("v2".equals(exchange.getRequestHeaders().getFirst(header2))) h2Received.set(true); + byte[] response = content2.getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(200, response.length); + try (OutputStream os = exchange.getResponseBody()) { os.write(response); } + } + }); + + Path bagDir = Files.createTempDirectory(folder, "multi-url-bag"); + Files.write(bagDir.resolve("bagit.txt"), "BagIt-Version: 0.97\nTag-File-Character-Encoding: UTF-8\n".getBytes()); + Files.createDirectory(bagDir.resolve("data")); + + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + + sha1.update(content1.getBytes(StandardCharsets.UTF_8)); + String hash1 = formatMessageDigest(sha1.digest()); + sha1.reset(); + sha1.update(content2.getBytes(StandardCharsets.UTF_8)); + String hash2 = formatMessageDigest(sha1.digest()); + + Files.write(bagDir.resolve("manifest-sha1.txt"), (hash1 + " data/t1.txt\n" + hash2 + " data/t2.txt\n").getBytes()); + + URL url1 = new URL("http://localhost:" + port + "/url1"); + URL url2 = new URL("http://localhost:" + port + "/url2"); + Files.write(bagDir.resolve("fetch.txt"), (url1 + " " + content1.length() + " data/t1.txt\n" + url2 + " " + content2.length() + " data/t2.txt\n").getBytes()); + + Bag bag = reader.read(bagDir); + + Map> urlConfigs = new HashMap<>(); + Map s1 = new HashMap<>(); s1.put(header1, "v1"); + Map s2 = new HashMap<>(); s2.put(header2, "v2"); + urlConfigs.put(url1.toString(), s1); + urlConfigs.put(url2.toString(), s2); + + sut.isValid(bag, true, true, null, urlConfigs); + + Assertions.assertTrue(h1Received.get(), "Header 1 should have been received"); + Assertions.assertTrue(h2Received.get(), "Header 2 should have been received"); + } + + private String formatMessageDigest(final byte[] digest) { + StringBuilder sb = new StringBuilder(); + for (byte b : digest) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } +} From 051e4f14e9a3f071e7badb4d4aabc93de36c3c41 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 13 Mar 2026 12:54:47 +0100 Subject: [PATCH 076/104] [maven-release-plugin] prepare release v1.4.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 5f5409726..48fceb084 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.3.1-SNAPSHOT + 1.4.0 DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - 1.0.0-SNAPSHOT + v1.4.0 From 88d975c74b0520478a9f6d738c0da1ffa784a561 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 13 Mar 2026 12:54:50 +0100 Subject: [PATCH 077/104] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 48fceb084..e7d32114f 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.4.0 + 1.4.1-SNAPSHOT DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - v1.4.0 + 1.0.0-SNAPSHOT From b6858172c0450b5891478d48ccef7d62b5daa91c Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 30 Mar 2026 14:10:11 +0200 Subject: [PATCH 078/104] Updated Codecov action version --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index d77ab23b3..a0276a888 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -17,4 +17,4 @@ jobs: - name: Run tests and collect coverage run: mvn -B test - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 From cdaf2a0360ce36a6c2ab22ddd33ec6febb03dd8d Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 30 Mar 2026 15:29:36 +0200 Subject: [PATCH 079/104] update github actions --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 06a827b80..38d7c0280 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,9 +13,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 17 distribution: 'adopt' From 3107a4aef701076b9c36016bcafed019348b990f Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 30 Mar 2026 15:33:45 +0200 Subject: [PATCH 080/104] Updated checkout and setup-java action versions --- .github/workflows/codeql.yml | 2 +- .github/workflows/coverage.yml | 2 +- .github/workflows/docs.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c5b7a575b..5e04fa505 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -43,7 +43,7 @@ jobs: uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'adopt' diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index a0276a888..5a5ea06cb 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -7,7 +7,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: 17 distribution: 'adopt' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d7eae048b..ea00b352c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,7 +19,7 @@ jobs: cache: 'pip' - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: adopt java-version: 17 From 082d40d894963eb8fb8d2645d374407a0add130a Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 30 Mar 2026 15:49:57 +0200 Subject: [PATCH 081/104] Updated checkout and setup-java action versions --- .github/workflows/codeql.yml | 2 +- .github/workflows/coverage.yml | 2 +- .github/workflows/docs.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 5e04fa505..4b524f80c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up JDK 17 uses: actions/setup-java@v5 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5a5ea06cb..a96533231 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -5,7 +5,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up JDK 17 uses: actions/setup-java@v5 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ea00b352c..e71917a97 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v5 From 30fb91a79591abd274a0862fba3ff6f0f864e298 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Sun, 17 May 2026 17:14:25 +0200 Subject: [PATCH 082/104] Reformat docs site --- docs/api.md | 6 ++--- docs/dev.md | 16 ++++--------- docs/getting-started.md | 26 ++++++-------------- docs/index.md | 53 ++++++++++++++++++++++------------------- docs/javadoc.md | 6 ----- mkdocs.yml | 11 +++++---- 6 files changed, 49 insertions(+), 69 deletions(-) delete mode 100644 docs/javadoc.md diff --git a/docs/api.md b/docs/api.md index 0e56014f8..d50195310 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,6 +1,6 @@ -Maven project info +JavaDocs ======== -Open the [Maven project info]{:target=_blank:} in a new tab. +Open the [JavaDocs]{:target=_blank:} in a new tab. -[Maven project info]: mvnsite/index.html +[JavaDocs]: mvnsite/apidocs/index.html diff --git a/docs/dev.md b/docs/dev.md index 396f7b526..5ccb8beda 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -22,16 +22,8 @@ General JavaDoc ------- -Since this is a library, the JavaDocs should be relatively extensive, although there is no need to go -overboard with this. At a minimum: - -* The JavaDocs must be generated successfully. As of today this is a standard part of the build; the build - will fail if doc generation fails. -* Every API endpoint method needs JavaDocs that documents the parameters and exceptions and has - a deep link to the Dataverse docs for the end-point that is called. This must be a link to target - "_blank". See existing code for examples. -* If an example program for the end-point method is available (which _should_ be the case) also add - a deep link to (the latest commit of) the example code. -* [Run the documentation site locally](https://dans-knaw.github.io/dans-datastation-architecture/dev/#documentation-with-mkdocs){:target=_blank} - to check how it renders. +Since this is a library, the JavaDocs should be relatively extensive, although there is no need to go overboard with this. At a minimum: + +* The JavaDocs must be generated successfully. As of today this is a standard part of the build; the build will fail if doc generation fails. +* [Run the documentation site locally](https://dans-knaw.github.io/dans-datastation-architecture/dev/#documentation-with-mkdocs){:target=_blank} to check how it renders. diff --git a/docs/getting-started.md b/docs/getting-started.md index 09c24f77c..58ec9619d 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,6 +1,13 @@ Getting started =============== +Basic usage +----------- + +For including the library as a dependency in a Maven project, see [the installation instructions](./index.md#using-the-library) + +A basic usage example follows: + ### Create a bag from a folder using version 0.97 ```java @@ -103,24 +110,5 @@ Path rootDir=Paths.get("RootDirectoryOfExistingBag"); List warnings=linter.lintBag(rootDir,Arrays.asList(BagitWarning.OLD_BAGIT_VERSION); ``` -### Serialization - -The dans-bagit-lib does not support directly -serializing a bag to an archive file. The examples show how to implement a -custom serializer for the -[zip](https://github.com/DANS-KNAW/dans-bagit-lib/blob/master/src/test/java/nl/knaw/dans/bagit/examples/serialization/CreateZipBagExample.java){:target=_blank:} -and -[tar](https://github.com/DANS-KNAW/dans-bagit-lib/blob/master/src/test/java/nl/knaw/dans/bagit/examples/serialization/CreateTarBagExample.java){:target=_blank:} -formats. - -### Fetching - -If you need `fetch.txt` functionality, the -[`FetchHttpFileExample` example](https://github.com/DANS-KNAW/dans-bagit-lib/blob/master/src/test/java/nl/knaw/dans/bagit/examples/fetching/FetchHttpFileExample.java){:target=_blank:} -demonstrates how you can implement this feature with your additional application -and workflow requirements. - -### Internationalization - All logging and error messages have been put into a [ResourceBundle](https://docs.oracle.com/javase/7/docs/api/java/util/ResourceBundle.html){:target=_blank:}. This allows for all the messages to be translated to multiple languages and automatically used during runtime. diff --git a/docs/index.md b/docs/index.md index 4ea194e31..de77b8ae6 100755 --- a/docs/index.md +++ b/docs/index.md @@ -1,9 +1,8 @@ -MANUAL -====== +Description +=========== + Library with classes and functions for working with the BagIt format -DESCRIPTION ------------ BagIt is a set of hierarchical file layout conventions designed to support storage and transfer of arbitrary digital content. A "bag" consists of a directory containing the payload files and other @@ -22,28 +21,34 @@ See: {:target=_blank}. This library was first developed by the [LibraryOfCongress](https://github.com/LibraryOfCongress/bagit-java/){:target=_blank:} and forked by DANS-KNAW. -INSTALLATION ------------- +Using the library +----------------- + +To use this library in a Maven-based project, add the following to your `pom.xml`. + +### 1. Declare the DANS maven repository -To use this library in a Maven-based project: +```xml -1. Include in your `pom.xml` a declaration for the DANS maven repository: + + + + DANS + + true + + https://maven.dans.knaw.nl/releases/ + + +``` - - - - DANS - - true - - https://maven.dans.knaw.nl/releases/ - - +### 2. Include a dependency on this library -2. Include a dependency on this library. +```xml - - nl.knaw.dans - dans-bagit-lib - {version} - + + nl.knaw.dans + dans-bagit-lib + {version} + +``` diff --git a/docs/javadoc.md b/docs/javadoc.md deleted file mode 100644 index 398ca46f2..000000000 --- a/docs/javadoc.md +++ /dev/null @@ -1,6 +0,0 @@ -JavaDocs -======== - -Open the [JavaDoc]{:target=_blank:} in a new tab. - -[JavaDoc]: mvnsite/apidocs/index.html diff --git a/mkdocs.yml b/mkdocs.yml index daa3a11e6..41163e9e8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,11 +22,12 @@ repo_name: DANS-KNAW/dans-bagit-lib repo_url: https://github.com/DANS-KNAW/dans-bagit-lib nav: - - Manual: index.md - - Getting started: getting-started.md - - JavaDoc: javadoc.md - - Maven project info: api.md - - Development: dev.md + - Manual: + - Description: index.md + - Getting started: getting-started.md + - JavaDocs: api.md + - Development: + - Overview: dev.md extra: deposit_directory: https://dans-knaw.github.io/dans-datastation-architecture/deposit-directory/ From 870a687418c3bd818facbba8fd87ed7a44ad8658 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Sun, 17 May 2026 17:22:16 +0200 Subject: [PATCH 083/104] update jaxb dep --- pom.xml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index e7d32114f..e126fd793 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,8 @@ limitations under the License. --> - + 4.0.0 @@ -139,10 +140,12 @@ coveralls-maven-plugin 4.3.0 + - javax.xml.bind - jaxb-api - ${jaxb2-maven-plugin.version} + jakarta.xml.bind + jakarta.xml.bind-api + 4.1.0-M1 + compile From 342cb184c34c60b849579b66c07832275895595b Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Sun, 17 May 2026 17:28:55 +0200 Subject: [PATCH 084/104] Removed coveralls --- pom.xml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/pom.xml b/pom.xml index e126fd793..405e29c01 100644 --- a/pom.xml +++ b/pom.xml @@ -134,22 +134,6 @@ 3.9.0 - - - org.eluder.coveralls - coveralls-maven-plugin - 4.3.0 - - - - jakarta.xml.bind - jakarta.xml.bind-api - 4.1.0-M1 - compile - - - - org.jacoco From 1488c3a12e718fe91862ea425d0b6afbe58bc17f Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Sun, 17 May 2026 17:32:31 +0200 Subject: [PATCH 085/104] remove coveralls --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e71917a97..a7c22ae69 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -26,7 +26,7 @@ jobs: cache: 'maven' - name: Generate maven site - run: mvn -B clean test site jacoco:report coveralls:report --file pom.xml -DrepoToken=${{ secrets.COVERALLS_REPO_TOKEN }} -Djarsigner.skip=true + run: mvn -B clean test site - name: Copy javadocs to site folder run: cp -r target/site/ docs/mvnsite/ From 3e921d2d879950bf9a175590fe3a9c6d131c09f6 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 18 May 2026 08:54:20 +0200 Subject: [PATCH 086/104] Added context page --- docs/context.md | 6 ++++++ mkdocs.yml | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 docs/context.md diff --git a/docs/context.md b/docs/context.md new file mode 100644 index 000000000..b24a4e036 --- /dev/null +++ b/docs/context.md @@ -0,0 +1,6 @@ +Context +======= + +This module is a component in the [DANS Data Station Architecture]{:target=_blank}. + +[DANS Data Station Architecture]: https://dans-knaw.github.io/dans-datastation-architecture/ diff --git a/mkdocs.yml b/mkdocs.yml index 41163e9e8..9bd6aaa30 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -25,7 +25,8 @@ nav: - Manual: - Description: index.md - Getting started: getting-started.md - - JavaDocs: api.md + - JavaDocs: api.md + - Context: context.md - Development: - Overview: dev.md From 4be91fd1125f05810ae84e43989d8ef71c153aa3 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Wed, 20 May 2026 09:46:10 +0200 Subject: [PATCH 087/104] Upgrade pymdown-extensions to version 10.21.3 in all modules --- .github/workflows/mkdocs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mkdocs/requirements.txt b/.github/workflows/mkdocs/requirements.txt index f99e66035..c3c8aec95 100644 --- a/.github/workflows/mkdocs/requirements.txt +++ b/.github/workflows/mkdocs/requirements.txt @@ -1,4 +1,4 @@ mkdocs==1.6.1 pyyaml==6.0.3 -pymdown-extensions==10.16.1 +pymdown-extensions==10.21.3 mkdocs-markdownextradata-plugin==0.2.6 From d1b48f1443d31965fa60699a35c9b8877c016677 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 1 Jun 2026 17:51:08 +0200 Subject: [PATCH 088/104] Upgrade to actions/setup-python@v6 because of deprecation warnings --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index a7c22ae69..309e06cfd 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.9 cache: 'pip' From 61f43ac01f5448ab9598fabf6fe492f683fc9894 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Wed, 15 Jul 2026 11:15:27 +0200 Subject: [PATCH 089/104] NO JIRA Configurable hashing (#15) # Description of changes Several thinks were made configurable about the Hasher, all related to hashing items fetched through the `fetch.txt` file. This only applies when holey bags are allowed. * Maximum number of redirects, to prevent a redirect-loop. * Whether to fall back to reading the whole file if chunked download fails even after retries. By default this is now turned off, as typically chunked download is used for very large files and it is unlikely that reading the whole file will succeed if chunks fail. * The existing options chunksize, maxretries, and retrysleep can now be overridden per call. The default chunksize is now smaller, 128Mb, because chunks are now ready into memory before updating the digest, to prevent corruption if a chunk read fails midway. 1Gb chunks is too large unless you give the JVM a lot of memory, because if the garbage collector cannot keep up you will run out of heap space because of memory fragmentation. Furthermore the code contained a bug. When a redirect was followed, the subsequent chunks were all downloaded based on the first redirect URL (with the range header updated for the new start and end). This worked fine until the timeout of the authorization expired (e.g. `X-Amz-Date` was more than one hour in the past). We now start with the original URL every time, so that a fresh authorization will be triggered. The code handling a 200 response after a range request was also improved. A 200 response after the first chunk should mean that the server does not support ranges but instead will send the whole file. The code now checks that the bytes sent are indeed the length of the file. Also, we do not accept a 200 after the first chunk anymore, as this would mean the server changed its mind about supporting ranges. --- pom.xml | 2 +- .../java/nl/knaw/dans/bagit/hash/Hasher.java | 321 +++++++++-- .../knaw/dans/bagit/verify/BagVerifier.java | 33 +- .../bagit/verify/CheckManifestHashesTask.java | 30 +- .../knaw/dans/bagit/hash/HasherUrlTest.java | 43 ++ .../bagit/verify/BagVerifierRemoteTest.java | 8 +- .../dans/bagit/verify/BagVerifierTest.java | 522 ++++++++++-------- 7 files changed, 645 insertions(+), 314 deletions(-) diff --git a/pom.xml b/pom.xml index 405e29c01..0581cfb9a 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ dans-bagit-lib - 1.4.1-SNAPSHOT + 1.5.0-SNAPSHOT DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib diff --git a/src/main/java/nl/knaw/dans/bagit/hash/Hasher.java b/src/main/java/nl/knaw/dans/bagit/hash/Hasher.java index 9f573cda9..a17e0b2ba 100644 --- a/src/main/java/nl/knaw/dans/bagit/hash/Hasher.java +++ b/src/main/java/nl/knaw/dans/bagit/hash/Hasher.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; +import java.net.ProtocolException; import java.net.URL; import java.net.URLConnection; import java.nio.file.Files; @@ -48,13 +49,17 @@ public final class Hasher { private static final int CHUNK_SIZE = _64_KB; private static final ResourceBundle messages = ResourceBundle.getBundle("MessageBundle"); - private static final String CHUNK_SIZE_PROP = "nl.knaw.dans.bagit.hash.chunkSize"; - private static final String MAX_RETRIES_PROP = "nl.knaw.dans.bagit.hash.maxRetries"; - private static final String RETRY_SLEEP_MS_PROP = "nl.knaw.dans.bagit.hash.retrySleepMs"; + public static final String CHUNK_SIZE_PROP = "nl.knaw.dans.bagit.hash.chunkSize"; + public static final String MAX_RETRIES_PROP = "nl.knaw.dans.bagit.hash.maxRetries"; + public static final String RETRY_SLEEP_MS_PROP = "nl.knaw.dans.bagit.hash.retrySleepMs"; + public static final String MAX_REDIRECTS_PROP = "nl.knaw.dans.bagit.hash.maxRedirects"; + public static final String FALL_BACK_TO_FULL_STREAM_ON_RANGE_FAIL_PROP = "nl.knaw.dans.bagit.hash.fallBackToFullStreamOnRangeFail"; - private static final long DEFAULT_CHUNK_SIZE = 1024L * 1024L * 1024L; // 1 GiB + private static final int DEFAULT_CHUNK_SIZE = 1024 * 1024 * 128; // 128 MB private static final int DEFAULT_MAX_RETRIES = 5; private static final int DEFAULT_RETRY_SLEEP_MS = 5000; + private static final int DEFAULT_MAX_REDIRECTS = 20; + private static final boolean DEFAULT_FALL_BACK_TO_FULL_STREAM_ON_RANGE_FAIL = false; private Hasher() { //intentionally left empty @@ -87,26 +92,43 @@ public static String hash(final URL url, final MessageDigest messageDigest) thro } /** - * Create a HEX formatted string checksum hash of the data from the {@link FetchItem} + * Create a HEX-formatted string checksum hash of the data from the {@link FetchItem} * * @param item the {@link FetchItem} to hash * @param messageDigest the {@link MessageDigest} object representing the hashing algorithm * @param extraHeaders optional extra headers to send with the request - * @return the hash as a hex formatted string + * @return the hash as a hex-formatted string * @throws IOException if there is a problem reading from the URL */ public static String hash(final FetchItem item, final MessageDigest messageDigest, final Map extraHeaders) throws IOException { + return hash(item, messageDigest, extraHeaders, HashOptions.systemProperties()); + } + + /** + * Create a HEX-formatted string checksum hash of the data from the {@link FetchItem} + * + * @param item the {@link FetchItem} to hash + * @param messageDigest the {@link MessageDigest} object representing the hashing algorithm + * @param extraHeaders optional extra headers to send with the request + * @param hashOptions optional settings for ranged HTTP requests + * @return the hash as a hex-formatted string + * @throws IOException if there is a problem reading from the URL + */ + public static String hash(final FetchItem item, final MessageDigest messageDigest, final Map extraHeaders, final HashOptions hashOptions) throws IOException { long totalSize = (item.length != null && item.length >= 0) ? item.length : -1; URL currentUrl = item.url; Map currentHeaders = extraHeaders; if (!currentUrl.getProtocol().startsWith("http")) { - return hashFullStream(currentUrl, messageDigest, currentHeaders); + return hashFullStream(currentUrl, messageDigest, currentHeaders, hashOptions); } - long chunkSize = Long.getLong(CHUNK_SIZE_PROP, DEFAULT_CHUNK_SIZE); - int maxRetries = Integer.getInteger(MAX_RETRIES_PROP, DEFAULT_MAX_RETRIES); - int retrySleepMs = Integer.getInteger(RETRY_SLEEP_MS_PROP, DEFAULT_RETRY_SLEEP_MS); + HashOptions effectiveOptions = hashOptions == null ? HashOptions.systemProperties() : hashOptions; + long chunkSize = effectiveOptions.getChunkSize(); + int maxRetries = effectiveOptions.getMaxRetries(); + int retrySleepMs = effectiveOptions.getRetrySleepMs(); + int maxRedirects = effectiveOptions.getMaxRedirects(); + int redirectCount = 0; long offset = 0; while (totalSize < 0 || offset < totalSize) { @@ -115,6 +137,7 @@ public static String hash(final FetchItem item, final MessageDigest messageDiges final URL finalUrl = currentUrl; final Map finalHeaders = currentHeaders; + final long finalOffset = offset; final long finalTotalSize = totalSize; try { @@ -128,12 +151,19 @@ public static String hash(final FetchItem item, final MessageDigest messageDiges } if (code == 206) { - return handlePartialContent(conn, messageDigest, finalTotalSize); + return handlePartialContent(conn, messageDigest, finalTotalSize, range); } else if (code == 200) { - logger.info("Server returned 200 OK for range request (probably range requests are not supported); downloading full stream from {}", finalUrl); + if (finalOffset > 0) { + throw new ProtocolException("Server returned 200 OK for range request " + range + " from " + finalUrl); + } + logger.info("Server returned 200 OK for first range request (probably range requests are not supported); downloading full stream from {}", finalUrl); + messageDigest.reset(); // Reset in case we got here after a retry try (InputStream is = conn.getInputStream()) { - updateDigestFromStream(is, messageDigest); + long totalRead = updateDigestFromStream(is, messageDigest); + if (finalTotalSize >= 0 && totalRead != finalTotalSize) { + throw new IOException("Expected to read " + finalTotalSize + " bytes but read " + totalRead + " bytes from " + finalUrl); + } } return ChunkResult.fullStream(formatMessageDigest(messageDigest)); } @@ -145,10 +175,14 @@ else if (code == 200) { logger.debug("Processing chunk result for range {} from {}", range, currentUrl); if (result.type == ChunkResultType.FULL_STREAM_SUCCESS) { + redirectCount = 0; logger.debug("Successfully processed full stream for range {} from {}", range, currentUrl); return result.hash; } else if (result.type == ChunkResultType.REDIRECT) { + if (++redirectCount > maxRedirects) { + throw new ProtocolException("Too many redirects"); + } URL nextUrl = new URL(currentUrl, result.location); if (!currentUrl.getAuthority().equals(nextUrl.getAuthority()) || !currentUrl.getProtocol().equals(nextUrl.getProtocol())) { currentHeaders = null; @@ -158,6 +192,9 @@ else if (result.type == ChunkResultType.REDIRECT) { // Skip offset update and retry the current chunk with new URL } else if (result.type == ChunkResultType.SUCCESS) { + redirectCount = 0; + currentUrl = item.url; + currentHeaders = extraHeaders; offset += result.bytesRead; if (totalSize < 0 && result.totalSize > 0) { totalSize = result.totalSize; @@ -166,49 +203,90 @@ else if (result.type == ChunkResultType.SUCCESS) { logger.debug("Read {} of {}{}", offset, totalSize > 0 ? totalSize : "Unknown", totalSize > 0 ? " (" + (offset * 100L / totalSize) + "%)" : ""); } } + catch (ProtocolException e) { + throw e; + } catch (IOException e) { - logger.info("Falling back to full stream for {} after failed range requests", currentUrl); - messageDigest.reset(); - return hashFullStream(currentUrl, messageDigest, currentHeaders); + if (effectiveOptions.isFallBackToFullStreamOnRangeFail()) { + logger.info("Falling back to full stream for {} after failed range requests", currentUrl); + messageDigest.reset(); + return hashFullStream(currentUrl, messageDigest, currentHeaders, hashOptions); + } + else { + logger.error("Failed range requests for {}, and fallback to full stream is disabled", currentUrl); + throw e; + } } } return formatMessageDigest(messageDigest); } - private static String hashFullStream(final URL url, final MessageDigest messageDigest, final Map extraHeaders) throws IOException { - URL currentUrl = url; - Map currentHeaders = extraHeaders; + private static String hashFullStream(final URL url, final MessageDigest messageDigest, final Map extraHeaders, final HashOptions hashOptions) throws IOException { + HashOptions effectiveOptions = hashOptions == null ? HashOptions.systemProperties() : hashOptions; + int maxRetries = effectiveOptions.getMaxRetries(); + int retrySleepMs = effectiveOptions.getRetrySleepMs(); + int maxRedirects = effectiveOptions.getMaxRedirects(); - while (true) { - URLConnection conn = currentUrl.openConnection(); - if (conn instanceof HttpURLConnection httpConn) { - httpConn.setInstanceFollowRedirects(false); - if (currentHeaders != null) { - for (Entry entry : currentHeaders.entrySet()) { - httpConn.setRequestProperty(entry.getKey(), entry.getValue()); + for (int attempt = 0; attempt < maxRetries; attempt++) { + URL currentUrl = url; + Map currentHeaders = extraHeaders; + try { + int redirectCount = 0; + while (true) { + URLConnection conn = currentUrl.openConnection(); + if (conn instanceof HttpURLConnection httpConn) { + httpConn.setInstanceFollowRedirects(false); + if (currentHeaders != null) { + for (Entry entry : currentHeaders.entrySet()) { + httpConn.setRequestProperty(entry.getKey(), entry.getValue()); + } + } + int code = httpConn.getResponseCode(); + if (code >= 300 && code < 400) { + if (++redirectCount > maxRedirects) { + throw new ProtocolException("Too many redirects"); + } + String location = httpConn.getHeaderField("Location"); + URL nextUrl = new URL(currentUrl, location); + if (!currentUrl.getAuthority().equals(nextUrl.getAuthority()) || !currentUrl.getProtocol().equals(nextUrl.getProtocol())) { + currentHeaders = null; + } + currentUrl = nextUrl; + continue; + } + if (code != 200) { + throw new IOException("Unexpected response code " + code + " for " + currentUrl); + } + } + try (final InputStream is = conn.getInputStream()) { + updateDigestFromStream(is, messageDigest); } + break; } - int code = httpConn.getResponseCode(); - if (code >= 300 && code < 400) { - String location = httpConn.getHeaderField("Location"); - URL nextUrl = new URL(currentUrl, location); - if (!currentUrl.getAuthority().equals(nextUrl.getAuthority()) || !currentUrl.getProtocol().equals(nextUrl.getProtocol())) { - currentHeaders = null; + return formatMessageDigest(messageDigest); + } + catch (ProtocolException e) { + throw e; + } + catch (IOException e) { + logger.warn("Error fetching full stream from {} (attempt {}/{}): {}", url, attempt + 1, maxRetries, e.getMessage()); + messageDigest.reset(); + if (attempt < maxRetries - 1) { + try { + Thread.sleep(retrySleepMs); + } + catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new IOException("Interrupted during retry sleep", ie); } - currentUrl = nextUrl; - continue; } - if (code != 200) { - throw new IOException("Unexpected response code " + code + " for " + currentUrl); + else { + throw e; } } - try (final InputStream is = conn.getInputStream()) { - updateDigestFromStream(is, messageDigest); - } - break; } - return formatMessageDigest(messageDigest); + throw new IOException("Max retries exceeded"); } /** @@ -224,7 +302,21 @@ public static String hash(final URL url, final MessageDigest messageDigest, fina return hash(new FetchItem(url, -1L, null), messageDigest, extraHeaders); } - private static ChunkResult handlePartialContent(HttpURLConnection conn, MessageDigest messageDigest, long currentTotalSize) throws IOException { + /** + * Create a HEX formatted string checksum hash of the data from the URL + * + * @param url the {@link URL} to hash + * @param messageDigest the {@link MessageDigest} object representing the hashing algorithm + * @param extraHeaders optional extra headers to send with the request + * @param hashOptions optional settings for ranged HTTP requests + * @return the hash as a hex formatted string + * @throws IOException if there is a problem reading from the URL + */ + public static String hash(final URL url, final MessageDigest messageDigest, final Map extraHeaders, final HashOptions hashOptions) throws IOException { + return hash(new FetchItem(url, -1L, null), messageDigest, extraHeaders, hashOptions); + } + + private static ChunkResult handlePartialContent(HttpURLConnection conn, MessageDigest messageDigest, long currentTotalSize, String range) throws IOException { long totalSize = currentTotalSize; if (totalSize < 0) { String contentRange = conn.getHeaderField("Content-Range"); @@ -238,22 +330,24 @@ private static ChunkResult handlePartialContent(HttpURLConnection conn, MessageD } } try (InputStream is = conn.getInputStream()) { - int bytesRead = updateDigestFromStream(is, messageDigest); - if (bytesRead < 0) { - throw new IOException("Stream closed unexpectedly for " + conn.getURL()); + byte[] bytes = is.readAllBytes(); + if (bytes.length == 0) { + // N.B. getting the range from the connection at this point may throw an exception, so we use the range we already have. + throw new IOException("Received empty response for range request " + range + " from " + conn.getURL()); } - return ChunkResult.success(bytesRead, totalSize); + messageDigest.update(bytes); + logger.debug("Updated message digest with range {}", range); + return ChunkResult.success(bytes.length, totalSize); } } private static ChunkResult executeWithRetry(RetryableOperation operation, String description, int maxRetries, int retrySleepMs) throws IOException { for (int attempt = 0; attempt < maxRetries; attempt++) { try { - ChunkResult result = operation.execute(); - if (result.type == ChunkResultType.REDIRECT) { - return result; // Break the retry loop for redirects - } - return result; + return operation.execute(); + } + catch (ProtocolException e) { + throw e; } catch (IOException e) { logger.warn("{} (attempt {}/{}): {}", description, attempt + 1, maxRetries, e.getMessage()); @@ -289,12 +383,12 @@ private enum ChunkResultType { */ private static class ChunkResult { final ChunkResultType type; - final int bytesRead; + final long bytesRead; final long totalSize; final String location; final String hash; - private ChunkResult(ChunkResultType type, int bytesRead, long totalSize, String location, String hash) { + private ChunkResult(ChunkResultType type, long bytesRead, long totalSize, String location, String hash) { this.type = type; this.bytesRead = bytesRead; this.totalSize = totalSize; @@ -302,7 +396,7 @@ private ChunkResult(ChunkResultType type, int bytesRead, long totalSize, String this.hash = hash; } - static ChunkResult success(int bytesRead, long totalSize) { + static ChunkResult success(long bytesRead, long totalSize) { return new ChunkResult(ChunkResultType.SUCCESS, bytesRead, totalSize, null, null); } @@ -335,9 +429,9 @@ private static HttpURLConnection openRangedConnection(URL url, String range, fin /** * Reads from the InputStream and updates the MessageDigest. Returns the number of bytes read. */ - private static int updateDigestFromStream(InputStream is, MessageDigest messageDigest) throws IOException { + private static long updateDigestFromStream(InputStream is, MessageDigest messageDigest) throws IOException { byte[] buffer = new byte[CHUNK_SIZE]; - int totalRead = 0; + long totalRead = 0; int read = is.read(buffer); while (read != -1) { messageDigest.update(buffer, 0, read); @@ -411,4 +505,119 @@ public static Map createManifestToMessageDigestMap(fina return map; } + + /** + * Represents configuration options for hashing operations. This class is immutable and provides various configurable parameters such as chunk size, retry behavior, and redirection limits. + */ + public static final class HashOptions { + private final int chunkSize; + private final int maxRetries; + private final int retrySleepMs; + private final int maxRedirects; + private final boolean fallBackToFullStreamOnRangeFail; + + /** + * Constructs an instance of {@code HashOptions} with specific configuration settings for chunk size, retry behavior, and redirection limits. + * + * @param chunkSize the size of data chunks in bytes; must be greater than 0 + * @param maxRetries the maximum number of retry attempts; must be greater than 0 + * @param retrySleepMs the time in milliseconds to sleep between retries + * @param maxRedirects the maximum number of redirects allowed; must be greater than or equal to 0 + * @throws IllegalArgumentException if any of the provided values are invalid (e.g., negative or zero where not allowed) + */ + public HashOptions(final int chunkSize, final int maxRetries, final int retrySleepMs, final int maxRedirects, boolean fallBackToFullStreamOnRangeFail) { + if (chunkSize <= 0) { + throw new IllegalArgumentException("chunkSize must be greater than 0"); + } + if (maxRetries <= 0) { + throw new IllegalArgumentException("maxRetries must be greater than 0"); + } + if (maxRedirects < 0) { + throw new IllegalArgumentException("maxRedirects must be greater than or equal to 0"); + } + this.chunkSize = chunkSize; + this.maxRetries = maxRetries; + this.retrySleepMs = retrySleepMs; + this.maxRedirects = maxRedirects; + this.fallBackToFullStreamOnRangeFail = fallBackToFullStreamOnRangeFail; + } + + /** + * Creates an instance of {@code HashOptions} using system properties to determine the configuration values for chunk size, maximum retries, retry sleep time, and maximum redirects. Defaults + * are used if the respective system properties are not set. + * + * The following system properties are used: - {@code CHUNK_SIZE_PROP}: Configures the chunk size in bytes (default: {@code DEFAULT_CHUNK_SIZE}). - {@code MAX_RETRIES_PROP}: Configures the + * maximum number of retry attempts (default: {@code DEFAULT_MAX_RETRIES}). - {@code RETRY_SLEEP_MS_PROP}: Configures the sleep time in milliseconds between retries (default: + * {@code DEFAULT_RETRY_SLEEP_MS}). - {@code MAX_REDIRECTS_PROP}: Configures the maximum allowable redirects (default: {@code DEFAULT_MAX_REDIRECTS}). + * + * @return a {@code HashOptions} instance populated with configuration values derived from system properties or their default values. + * @throws IllegalArgumentException if any of the retrieved values are invalid (e.g., negative or zero where not allowed) + */ + public static HashOptions systemProperties() { + return new HashOptions( + Integer.getInteger(CHUNK_SIZE_PROP, DEFAULT_CHUNK_SIZE), + Integer.getInteger(MAX_RETRIES_PROP, DEFAULT_MAX_RETRIES), + Integer.getInteger(RETRY_SLEEP_MS_PROP, DEFAULT_RETRY_SLEEP_MS), + Integer.getInteger(MAX_REDIRECTS_PROP, DEFAULT_MAX_REDIRECTS), + Boolean.parseBoolean(System.getProperty(FALL_BACK_TO_FULL_STREAM_ON_RANGE_FAIL_PROP, String.valueOf(DEFAULT_FALL_BACK_TO_FULL_STREAM_ON_RANGE_FAIL))) + ); + } + + /** + * Returns a new {@code HashOptions} instance with the specified overrides for configuration parameters. If a parameter is {@code null}, the existing value from the current {@code HashOptions} + * instance is used. + * + * @param chunkSize the size of data chunks in bytes; if {@code null}, the existing chunk size is retained + * @param maxRetries the maximum number of retry attempts; if {@code null}, the existing max retries value is retained + * @param retrySleepMs the time in milliseconds to sleep between retries; if {@code null}, the existing retry sleep time is retained + * @param maxRedirects the maximum number of redirects allowed; if {@code null}, the existing max redirects value is retained + * @return a new {@code HashOptions} instance with the specified values or the existing values if overrides are {@code null} + * @throws IllegalArgumentException if any of the provided values are invalid (e.g., negative or zero where not allowed) + */ + public HashOptions withOverrides(final Integer chunkSize, final Integer maxRetries, final Integer retrySleepMs, final Integer maxRedirects) { + return withOverrides(chunkSize, maxRetries, retrySleepMs, maxRedirects, null); + } + + /** + * Returns a new {@code HashOptions} instance with the specified overrides for configuration parameters. If a parameter is {@code null}, the existing value from the current {@code HashOptions} + * instance is used. + * + * @param chunkSize the size of data chunks in bytes; if {@code null}, the existing chunk size is retained + * @param maxRetries the maximum number of retry attempts; if {@code null}, the existing max retries value is retained + * @param retrySleepMs the time in milliseconds to sleep between retries; if {@code null}, the existing retry sleep time is retained + * @param maxRedirects the maximum number of redirects allowed; if {@code null}, the existing max redirects value is retained + * @param fallBackToFullStreamOnRangeFail whether to fall back to full stream; if {@code null}, the existing value is retained + * @return a new {@code HashOptions} instance with the specified values or the existing values if overrides are {@code null} + * @throws IllegalArgumentException if any of the provided values are invalid (e.g., negative or zero where not allowed) + */ + public HashOptions withOverrides(final Integer chunkSize, final Integer maxRetries, final Integer retrySleepMs, final Integer maxRedirects, final Boolean fallBackToFullStreamOnRangeFail) { + return new HashOptions( + chunkSize == null ? this.chunkSize : chunkSize, + maxRetries == null ? this.maxRetries : maxRetries, + retrySleepMs == null ? this.retrySleepMs : retrySleepMs, + maxRedirects == null ? this.maxRedirects : maxRedirects, + fallBackToFullStreamOnRangeFail == null ? this.fallBackToFullStreamOnRangeFail : fallBackToFullStreamOnRangeFail + ); + } + + public int getChunkSize() { + return chunkSize; + } + + public int getMaxRetries() { + return maxRetries; + } + + public int getRetrySleepMs() { + return retrySleepMs; + } + + public int getMaxRedirects() { + return maxRedirects; + } + + public boolean isFallBackToFullStreamOnRangeFail() { + return fallBackToFullStreamOnRangeFail; + } + } } diff --git a/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java b/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java index 7088b8585..cb2a28e8a 100644 --- a/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java @@ -45,6 +45,7 @@ import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; import nl.knaw.dans.bagit.exceptions.VerificationException; import nl.knaw.dans.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.hash.Hasher.HashOptions; import nl.knaw.dans.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,6 +59,11 @@ public final class BagVerifier implements AutoCloseable{ private final ManifestVerifier manifestVerifier; private final ExecutorService executor; + private Integer chunkSize; + private Integer maxRetries; + private Integer retrySleepMs; + private Integer maxRedirects; + private Boolean fallBackToFullStreamOnRangeFail; /** * Create a BagVerifier with a cached thread pool and a @@ -103,6 +109,26 @@ public void close() throws SecurityException{ executor.shutdown(); manifestVerifier.close(); } + + public void setChunkSize(final int chunkSize) { + this.chunkSize = chunkSize; + } + + public void setMaxRetries(final int maxRetries) { + this.maxRetries = maxRetries; + } + + public void setRetrySleepMs(final int retrySleepMs) { + this.retrySleepMs = retrySleepMs; + } + + public void setMaxRedirects(final int maxRedirects) { + this.maxRedirects = maxRedirects; + } + + public void setFallBackToFullStreamOnRangeFail(final boolean fallBackToFullStreamOnRangeFail) { + this.fallBackToFullStreamOnRangeFail = fallBackToFullStreamOnRangeFail; + } /** * Determine if we can quickly verify by comparing the number of files and the total number of bytes expected @@ -206,9 +232,10 @@ void checkHashes(final Manifest manifest, final Map fetchItems, //TODO maybe return all of these at some point... final Collection exceptions = Collections.synchronizedCollection(new ArrayList<>()); + final HashOptions hashOptions = getHashOptions(); for(final Entry entry : manifest.getFileToChecksumMap().entrySet()){ - executor.execute(new CheckManifestHashesTask(entry, manifest.getAlgorithm().getMessageDigestName(), latch, exceptions, fetchItems, holey, extraHeaders, urlConfigs)); + executor.execute(new CheckManifestHashesTask(entry, manifest.getAlgorithm().getMessageDigestName(), latch, exceptions, fetchItems, holey, extraHeaders, urlConfigs, hashOptions)); } latch.await(); @@ -223,6 +250,10 @@ void checkHashes(final Manifest manifest, final Map fetchItems, throw new VerificationException(e); } } + + HashOptions getHashOptions() { + return HashOptions.systemProperties().withOverrides(chunkSize, maxRetries, retrySleepMs, maxRedirects, fallBackToFullStreamOnRangeFail); + } /** * See https://tools.ietf.org/html/draft-kunze-bagit-13#section-3
diff --git a/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java b/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java index 3d66dd7a4..509c966d8 100644 --- a/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java @@ -22,6 +22,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Collection; +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.ResourceBundle; @@ -33,6 +34,7 @@ import org.slf4j.LoggerFactory; import nl.knaw.dans.bagit.hash.Hasher; +import nl.knaw.dans.bagit.hash.Hasher.HashOptions; /** * Checks a give file to make sure the given checksum hash matches the computed checksum hash. @@ -51,20 +53,25 @@ public class CheckManifestHashesTask implements Runnable { private transient final boolean holey; private transient final Map extraHeaders; private transient final Map> urlConfigs; + private transient final HashOptions hashOptions; public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions) { - this(entry, algorithm, latch, exceptions, null, false, null, null); + this(entry, algorithm, latch, exceptions, null, false, null, null, null); } public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey) { - this(entry, algorithm, latch, exceptions, fetchItems, holey, null, null); + this(entry, algorithm, latch, exceptions, fetchItems, holey, null, null, null); } public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey, final Map extraHeaders) { - this(entry, algorithm, latch, exceptions, fetchItems, holey, extraHeaders, null); + this(entry, algorithm, latch, exceptions, fetchItems, holey, extraHeaders, null, null); } public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey, final Map extraHeaders, final Map> urlConfigs) { + this(entry, algorithm, latch, exceptions, fetchItems, holey, extraHeaders, urlConfigs, null); + } + + public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey, final Map extraHeaders, final Map> urlConfigs, final HashOptions hashOptions) { this.entry = entry; this.algorithm = algorithm; this.latch = latch; @@ -73,13 +80,14 @@ public CheckManifestHashesTask(final Entry entry, final String alg this.holey = holey; this.extraHeaders = extraHeaders; this.urlConfigs = urlConfigs; + this.hashOptions = hashOptions; } @Override public void run() { try { final MessageDigest messageDigest = MessageDigest.getInstance(algorithm); - checkManifestEntry(entry, messageDigest, algorithm, fetchItems, holey, extraHeaders, urlConfigs); + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, holey, extraHeaders, urlConfigs, hashOptions); } catch (IOException | CorruptChecksumException | NoSuchAlgorithmException e) { exceptions.add(e); } @@ -87,18 +95,22 @@ public void run() { } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm) throws IOException, CorruptChecksumException { - checkManifestEntry(entry, messageDigest, algorithm, null, false, null, null); + checkManifestEntry(entry, messageDigest, algorithm, null, false, null, null, null); } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley) throws IOException, CorruptChecksumException { - checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, null, null); + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, null, null, null); } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley, final Map extraHeaders) throws IOException, CorruptChecksumException { - checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, extraHeaders, null); + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, extraHeaders, null, null); } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley, final Map extraHeaders, final Map> urlConfigs) throws IOException, CorruptChecksumException { + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, extraHeaders, urlConfigs, null); + } + + protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley, final Map extraHeaders, final Map> urlConfigs, final HashOptions hashOptions) throws IOException, CorruptChecksumException { if (Files.exists(entry.getKey())) { logger.debug(messages.getString("checking_checksums"), entry.getKey(), entry.getValue()); final String hash = Hasher.hash(entry.getKey(), messageDigest); @@ -110,7 +122,7 @@ protected static void checkManifestEntry(final Entry entry, final final FetchItem item = fetchItems.get(entry.getKey()); logger.debug("File {} does not exist, but it is in fetch.txt, and allowHoley is true. Hashing from URL: {}", entry.getKey(), item.url); final Map mergedHeaders = mergeHeaders(item.url, extraHeaders, urlConfigs); - final String hash = Hasher.hash(item, messageDigest, mergedHeaders); + final String hash = Hasher.hash(item, messageDigest, mergedHeaders, hashOptions); logger.debug("computed hash [{}] for url [{}]", hash, item.url); if (!hash.equals(entry.getValue())) { throw new CorruptChecksumException(messages.getString("corrupt_checksum_error"), entry.getKey(), algorithm, entry.getValue(), hash); @@ -124,7 +136,7 @@ private static Map mergeHeaders(final URL url, final Map mergedHeaders = new java.util.HashMap<>(); + final Map mergedHeaders = new HashMap<>(); if (extraHeaders != null) { mergedHeaders.putAll(extraHeaders); } diff --git a/src/test/java/nl/knaw/dans/bagit/hash/HasherUrlTest.java b/src/test/java/nl/knaw/dans/bagit/hash/HasherUrlTest.java index 1f3e67583..8b179a3ca 100644 --- a/src/test/java/nl/knaw/dans/bagit/hash/HasherUrlTest.java +++ b/src/test/java/nl/knaw/dans/bagit/hash/HasherUrlTest.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; +import java.net.ProtocolException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -89,6 +90,7 @@ public void teardown() { System.clearProperty("nl.knaw.dans.bagit.hash.chunkSize"); System.clearProperty("nl.knaw.dans.bagit.hash.maxRetries"); System.clearProperty("nl.knaw.dans.bagit.hash.retrySleepMs"); + System.clearProperty("nl.knaw.dans.bagit.hash.fallBackToFullStreamOnRangeFail"); } @Test @@ -207,6 +209,45 @@ public void handle(HttpExchange exchange) throws IOException { Assertions.assertEquals(1, requestCount.get()); } + @Test + public void testHashThrowsProtocolExceptionWhenLaterChunkReturnsOk() throws IOException, NoSuchAlgorithmException { + server.removeContext("/test"); + server.createContext("/test", new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + int count = requestCount.incrementAndGet(); + String range = exchange.getRequestHeaders().getFirst("Range"); + if (count == 2) { + exchange.sendResponseHeaders(200, TEST_DATA_BYTES.length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(TEST_DATA_BYTES); + } + return; + } + + String[] parts = range.substring(6).split("-"); + int start = Integer.parseInt(parts[0]); + int end = parts.length > 1 && !parts[1].isEmpty() ? Integer.parseInt(parts[1]) : TEST_DATA_BYTES.length - 1; + int actualEnd = Math.min(end, TEST_DATA_BYTES.length - 1); + int length = actualEnd - start + 1; + exchange.getResponseHeaders().set("Content-Range", "bytes " + start + "-" + actualEnd + "/" + TEST_DATA_BYTES.length); + exchange.sendResponseHeaders(206, length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(TEST_DATA_BYTES, start, length); + } + } + }); + + System.setProperty("nl.knaw.dans.bagit.hash.chunkSize", "10"); + System.setProperty("nl.knaw.dans.bagit.hash.maxRetries", "3"); + + URL url = new URL("http://localhost:" + server.getAddress().getPort() + "/test"); + MessageDigest md = MessageDigest.getInstance("SHA-1"); + + Assertions.assertThrows(ProtocolException.class, () -> Hasher.hash(url, md)); + Assertions.assertEquals(2, requestCount.get()); + } + @Test public void testHashWithFailedFirstRangeRequest() throws IOException, NoSuchAlgorithmException { server.removeContext("/test"); @@ -228,6 +269,7 @@ public void handle(HttpExchange exchange) throws IOException { }); System.setProperty("nl.knaw.dans.bagit.hash.maxRetries", "1"); + System.setProperty("nl.knaw.dans.bagit.hash.fallBackToFullStreamOnRangeFail", "true"); try { URL url = new URL("http://localhost:" + server.getAddress().getPort() + "/test"); MessageDigest md = MessageDigest.getInstance("SHA-1"); @@ -248,6 +290,7 @@ public void handle(HttpExchange exchange) throws IOException { Assertions.assertEquals(2, requestCount.get()); } finally { System.clearProperty("nl.knaw.dans.bagit.hash.maxRetries"); + System.clearProperty("nl.knaw.dans.bagit.hash.fallBackToFullStreamOnRangeFail"); } } diff --git a/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRemoteTest.java b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRemoteTest.java index 88a9d3993..6096097c1 100644 --- a/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRemoteTest.java +++ b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierRemoteTest.java @@ -120,12 +120,8 @@ public void handle(HttpExchange exchange) throws IOException { headers.put(authHeaderName, authHeaderValue); // Use a small chunk size to force multiple range requests - System.setProperty("nl.knaw.dans.bagit.hash.chunkSize", "10"); - try { - sut.isValid(bag, true, true, headers); - } finally { - System.clearProperty("nl.knaw.dans.bagit.hash.chunkSize"); - } + sut.setChunkSize(10); + sut.isValid(bag, true, true, headers); Assertions.assertTrue(rangeRequestCount.get() > 1, "Should have used multiple range requests, but used: " + rangeRequestCount.get()); } diff --git a/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java index 7ed41a3fc..dcff289c5 100644 --- a/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java +++ b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java @@ -15,260 +15,300 @@ */ package nl.knaw.dans.bagit.verify; -import java.io.File; -import java.net.URL; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.security.Security; -import java.util.Arrays; -import java.util.List; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - import nl.knaw.dans.bagit.TempFolderTest; import nl.knaw.dans.bagit.TestUtils; import nl.knaw.dans.bagit.domain.Bag; import nl.knaw.dans.bagit.domain.Manifest; import nl.knaw.dans.bagit.exceptions.CorruptChecksumException; import nl.knaw.dans.bagit.exceptions.FileNotInManifestException; -import nl.knaw.dans.bagit.exceptions.FileNotInPayloadDirectoryException; import nl.knaw.dans.bagit.exceptions.UnsupportedAlgorithmException; import nl.knaw.dans.bagit.exceptions.VerificationException; +import nl.knaw.dans.bagit.hash.Hasher; +import nl.knaw.dans.bagit.hash.Hasher.HashOptions; import nl.knaw.dans.bagit.hash.StandardSupportedAlgorithms; import nl.knaw.dans.bagit.hash.SupportedAlgorithm; import nl.knaw.dans.bagit.reader.BagReader; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.Security; +import java.util.Arrays; +import java.util.List; + +public class BagVerifierTest extends TempFolderTest { + static { + if (Security.getProvider("BC") == null) { + Security.addProvider(new BouncyCastleProvider()); + } + } + + private Path rootDir = Paths.get(new File("src/test/resources/bags/v0_97/bag").toURI()); + + private final BagVerifier sut = new BagVerifier(); + private final BagReader reader = new BagReader(); + + @Test + public void testValidWhenHiddenFolderNotIncluded() throws Exception { + Path copyDir = copyBagToTempFolder(rootDir); + Files.createDirectory(copyDir.resolve("data").resolve(".someHiddenFolder")); + TestUtils.makeFilesHiddenOnWindows(copyDir); + + Bag bag = reader.read(copyDir); + sut.isValid(bag, true); + } + + @Test + public void testValidWithHiddenFile() throws Exception { + Path copyDir = copyBagToTempFolder(rootDir); + Files.createFile(copyDir.resolve("data").resolve(".someHiddenFile")); + TestUtils.makeFilesHiddenOnWindows(copyDir); + + Bag bag = reader.read(copyDir); + sut.isValid(bag, true); + } + + @Test + public void testInvalidWithHiddenFile() throws Exception { + Path copyDir = copyBagToTempFolder(rootDir); + Files.createFile(copyDir.resolve("data").resolve(".someHiddenFile")); + TestUtils.makeFilesHiddenOnWindows(copyDir); + + Bag bag = reader.read(copyDir); + Assertions.assertThrows(FileNotInManifestException.class, () -> { + sut.isValid(bag, false); + }); + } + + @Test + public void testStandardSupportedAlgorithms() throws Exception { + List algorithms = Arrays.asList("md5", "sha1", "sha256", "sha512"); + for (String alg : algorithms) { + StandardSupportedAlgorithms algorithm = StandardSupportedAlgorithms.valueOf(alg.toUpperCase()); + Manifest manifest = new Manifest(algorithm); + sut.checkHashes(manifest); + } + } + + @Test + public void hash_options_on_bag_verifier_override_system_properties() { + System.setProperty(Hasher.CHUNK_SIZE_PROP, "10"); + System.setProperty(Hasher.MAX_RETRIES_PROP, "2"); + System.setProperty(Hasher.RETRY_SLEEP_MS_PROP, "30"); + try (BagVerifier sut = new BagVerifier()) { + HashOptions systemHashOptions = sut.getHashOptions(); + + Assertions.assertEquals(10, systemHashOptions.getChunkSize()); + Assertions.assertEquals(2, systemHashOptions.getMaxRetries()); + Assertions.assertEquals(30, systemHashOptions.getRetrySleepMs()); + + sut.setChunkSize(20); + sut.setMaxRetries(3); + sut.setRetrySleepMs(40); + + HashOptions hashOptions = sut.getHashOptions(); + + Assertions.assertEquals(20, hashOptions.getChunkSize()); + Assertions.assertEquals(3, hashOptions.getMaxRetries()); + Assertions.assertEquals(40, hashOptions.getRetrySleepMs()); + } + finally { + System.clearProperty(Hasher.CHUNK_SIZE_PROP); + System.clearProperty(Hasher.MAX_RETRIES_PROP); + System.clearProperty(Hasher.RETRY_SLEEP_MS_PROP); + } + } + + @Test + public void testMD5Bag() throws Exception { + Path bagDir = Paths.get("src", "test", "resources", "md5Bag"); + Bag bag = reader.read(bagDir); + sut.isValid(bag, true); + } + + @Test + public void testSHA1Bag() throws Exception { + Path bagDir = Paths.get("src", "test", "resources", "sha1Bag"); + Bag bag = reader.read(bagDir); + sut.isValid(bag, true); + } + + @Test + public void testSHA224Bag() throws Exception { + Path bagDir = Paths.get("src", "test", "resources", "sha224Bag"); + Bag bag = reader.read(bagDir); + sut.isValid(bag, true); + } + + @Test + public void testSHA256Bag() throws Exception { + Path bagDir = Paths.get("src", "test", "resources", "sha256Bag"); + Bag bag = reader.read(bagDir); + sut.isValid(bag, true); + } + + @Test + public void testSHA512Bag() throws Exception { + Path bagDir = Paths.get("src", "test", "resources", "sha512Bag"); + Bag bag = reader.read(bagDir); + sut.isValid(bag, true); + } + + @Test + public void testVersion0_97IsValid() throws Exception { + Bag bag = reader.read(rootDir); + + sut.isValid(bag, true); + } -public class BagVerifierTest extends TempFolderTest{ - static { - if (Security.getProvider("BC") == null) { - Security.addProvider(new BouncyCastleProvider()); + @Test + public void testVersion2_0IsValid() throws Exception { + rootDir = Paths.get(new File("src/test/resources/bags/v2_0/bag").toURI()); + Bag bag = reader.read(rootDir); + + sut.isValid(bag, true); + } + + @Test + public void testIsComplete() throws Exception { + Bag bag = reader.read(rootDir); + + sut.isComplete(bag, true); + } + + @Test + public void testCorruptPayloadFile() throws Exception { + rootDir = Paths.get(new File("src/test/resources/corruptPayloadFile").toURI()); + Bag bag = reader.read(rootDir); + + Assertions.assertThrows(CorruptChecksumException.class, () -> { + sut.isValid(bag, true); + }); + } + + @Test + public void testCorruptTagFile() throws Exception { + rootDir = Paths.get(new File("src/test/resources/corruptTagFile").toURI()); + Bag bag = reader.read(rootDir); + + Assertions.assertThrows(CorruptChecksumException.class, () -> { + sut.isValid(bag, true); + }); } - } - - private Path rootDir = Paths.get(new File("src/test/resources/bags/v0_97/bag").toURI()); - - private BagVerifier sut = new BagVerifier(); - private BagReader reader = new BagReader(); - - @Test - public void testValidWhenHiddenFolderNotIncluded() throws Exception{ - Path copyDir = copyBagToTempFolder(rootDir); - Files.createDirectory(copyDir.resolve("data").resolve(".someHiddenFolder")); - TestUtils.makeFilesHiddenOnWindows(copyDir); - - Bag bag = reader.read(copyDir); - sut.isValid(bag, true); - } - - @Test - public void testValidWithHiddenFile() throws Exception{ - Path copyDir = copyBagToTempFolder(rootDir); - Files.createFile(copyDir.resolve("data").resolve(".someHiddenFile")); - TestUtils.makeFilesHiddenOnWindows(copyDir); - - Bag bag = reader.read(copyDir); - sut.isValid(bag, true); - } - - @Test - public void testInvalidWithHiddenFile() throws Exception{ - Path copyDir = copyBagToTempFolder(rootDir); - Files.createFile(copyDir.resolve("data").resolve(".someHiddenFile")); - TestUtils.makeFilesHiddenOnWindows(copyDir); - - Bag bag = reader.read(copyDir); - Assertions.assertThrows(FileNotInManifestException.class, () -> { sut.isValid(bag, false); }); - } - - @Test - public void testStandardSupportedAlgorithms() throws Exception{ - List algorithms = Arrays.asList("md5", "sha1", "sha256", "sha512"); - for(String alg : algorithms){ - StandardSupportedAlgorithms algorithm = StandardSupportedAlgorithms.valueOf(alg.toUpperCase()); - Manifest manifest = new Manifest(algorithm); - sut.checkHashes(manifest); + + @Test + public void testErrorWhenUnspportedAlgorithmException() throws Exception { + Path sha3BagDir = Paths.get(getClass().getClassLoader().getResource("sha3Bag").toURI()); + MySupportedNameToAlgorithmMapping mapping = new MySupportedNameToAlgorithmMapping(); + BagReader extendedReader = new BagReader(mapping); + Bag bag = extendedReader.read(sha3BagDir); + + Assertions.assertThrows(UnsupportedAlgorithmException.class, () -> { + sut.isValid(bag, true); + }); } - } - - @Test - public void testMD5Bag() throws Exception{ - Path bagDir = Paths.get("src", "test", "resources", "md5Bag"); - Bag bag = reader.read(bagDir); - sut.isValid(bag, true); - } - - @Test - public void testSHA1Bag() throws Exception{ - Path bagDir = Paths.get("src", "test", "resources", "sha1Bag"); - Bag bag = reader.read(bagDir); - sut.isValid(bag, true); - } - - @Test - public void testSHA224Bag() throws Exception{ - Path bagDir = Paths.get("src", "test", "resources", "sha224Bag"); - Bag bag = reader.read(bagDir); - sut.isValid(bag, true); - } - - @Test - public void testSHA256Bag() throws Exception{ - Path bagDir = Paths.get("src", "test", "resources", "sha256Bag"); - Bag bag = reader.read(bagDir); - sut.isValid(bag, true); - } - - @Test - public void testSHA512Bag() throws Exception{ - Path bagDir = Paths.get("src", "test", "resources", "sha512Bag"); - Bag bag = reader.read(bagDir); - sut.isValid(bag, true); - } - - @Test - public void testVersion0_97IsValid() throws Exception{ - Bag bag = reader.read(rootDir); - - sut.isValid(bag, true); - } - - @Test - public void testVersion2_0IsValid() throws Exception{ - rootDir = Paths.get(new File("src/test/resources/bags/v2_0/bag").toURI()); - Bag bag = reader.read(rootDir); - - sut.isValid(bag, true); - } - - @Test - public void testIsComplete() throws Exception{ - Bag bag = reader.read(rootDir); - - sut.isComplete(bag, true); - } - - @Test - public void testCorruptPayloadFile() throws Exception{ - rootDir = Paths.get(new File("src/test/resources/corruptPayloadFile").toURI()); - Bag bag = reader.read(rootDir); - - Assertions.assertThrows(CorruptChecksumException.class, () -> { sut.isValid(bag, true); }); - } - - @Test - public void testCorruptTagFile() throws Exception{ - rootDir = Paths.get(new File("src/test/resources/corruptTagFile").toURI()); - Bag bag = reader.read(rootDir); - - Assertions.assertThrows(CorruptChecksumException.class, () -> { sut.isValid(bag, true); }); - } - - @Test - public void testErrorWhenUnspportedAlgorithmException() throws Exception{ - Path sha3BagDir = Paths.get(getClass().getClassLoader().getResource("sha3Bag").toURI()); - MySupportedNameToAlgorithmMapping mapping = new MySupportedNameToAlgorithmMapping(); - BagReader extendedReader = new BagReader(mapping); - Bag bag = extendedReader.read(sha3BagDir); - - Assertions.assertThrows(UnsupportedAlgorithmException.class, () -> { sut.isValid(bag, true); }); - } - - @Test - public void testVerificationExceptionIsThrownForNoSuchAlgorithmException() throws Exception{ - Path unreadableFile = createFile("newFile"); - - Manifest manifest = new Manifest(new SupportedAlgorithm() { - @Override - public String getMessageDigestName() { - return "FOO"; - } - @Override - public String getBagitName() { - return "foo"; - } - }); - manifest.getFileToChecksumMap().put(unreadableFile, "foo"); - - Assertions.assertThrows(VerificationException.class, () -> { sut.checkHashes(manifest); }); - } - - @Test - public void testAddSHA3SupportViaExtension() throws Exception{ - Path sha3BagDir = Paths.get(new File("src/test/resources/sha3Bag").toURI()); - MySupportedNameToAlgorithmMapping mapping = new MySupportedNameToAlgorithmMapping(); - BagReader extendedReader = new BagReader(mapping); - Bag bag = extendedReader.read(sha3BagDir); - try(BagVerifier extendedSut = new BagVerifier(mapping)){ - extendedSut.isValid(bag, true); + + @Test + public void testVerificationExceptionIsThrownForNoSuchAlgorithmException() throws Exception { + Path unreadableFile = createFile("newFile"); + + Manifest manifest = new Manifest(new SupportedAlgorithm() { + + @Override + public String getMessageDigestName() { + return "FOO"; + } + + @Override + public String getBagitName() { + return "foo"; + } + }); + manifest.getFileToChecksumMap().put(unreadableFile, "foo"); + + Assertions.assertThrows(VerificationException.class, () -> { + sut.checkHashes(manifest); + }); + } + + @Test + public void testAddSHA3SupportViaExtension() throws Exception { + Path sha3BagDir = Paths.get(new File("src/test/resources/sha3Bag").toURI()); + MySupportedNameToAlgorithmMapping mapping = new MySupportedNameToAlgorithmMapping(); + BagReader extendedReader = new BagReader(mapping); + Bag bag = extendedReader.read(sha3BagDir); + try (BagVerifier extendedSut = new BagVerifier(mapping)) { + extendedSut.isValid(bag, true); + } + } + + /* + * Technically valid but highly discouraged + */ + @Test + public void testManifestsWithLeadingDotSlash() throws Exception { + Path bagPath = Paths.get(new File("src/test/resources/bag-with-leading-dot-slash-in-manifest").toURI()); + Bag bag = reader.read(bagPath); + + sut.isValid(bag, true); + } + + @Test + public void testCanQuickVerify() throws Exception { + Bag bag = reader.read(rootDir); + boolean canQuickVerify = BagVerifier.canQuickVerify(bag); + Assertions.assertFalse(canQuickVerify, + "Since " + bag.getRootDir() + " DOES NOT contain the metadata Payload-Oxum then it should return false!"); + + Path passingRootDir = Paths.get(new File("src/test/resources/bags/v0_94/bag").toURI()); + bag = reader.read(passingRootDir); + canQuickVerify = BagVerifier.canQuickVerify(bag); + Assertions.assertTrue(canQuickVerify, + "Since " + bag.getRootDir() + " DOES contain the metadata Payload-Oxum then it should return true!"); + } + + @Test + public void testQuickVerify() throws Exception { + Path passingRootDir = Paths.get(new File("src/test/resources/bags/v0_94/bag").toURI()); + Bag bag = reader.read(passingRootDir); + + BagVerifier.quicklyVerify(bag); + } + + @Test + public void testHoleyBag() throws Exception { + Path bagDir = Paths.get("src", "test", "resources", "md5Bag"); + Path copyDir = copyBagToTempFolder(bagDir); + Path readme = copyDir.resolve("data/readme.txt"); + byte[] content = Files.readAllBytes(readme); + Files.delete(readme); + + // Create a local file to serve as "remote" resource + Path remoteFile = copyDir.resolve("remote-readme.txt"); + Files.write(remoteFile, content); + URL remoteUrl = remoteFile.toUri().toURL(); + + // Create fetch.txt + Path fetchFile = copyDir.resolve("fetch.txt"); + // Format of fetch.txt: url length path + // IMPORTANT: BagReader uses relative paths from root for fetch items, + // but they should NOT have 'data/' prefix if they are in data directory? + // Actually, BagIt spec says it's the path relative to the bag root. + String fetchLine = remoteUrl.toString() + " " + content.length + " data/readme.txt\n"; + Files.write(fetchFile, fetchLine.getBytes()); + + Bag bag = reader.read(copyDir); + + // With the new logic, it should be valid even without explicitly passing true, + // because fetch.txt is present and contains data/readme.txt + sut.isValid(bag, true); + + // It should also be valid if we explicitly pass true + sut.isValid(bag, true, true); } - } - - /* - * Technically valid but highly discouraged - */ - @Test - public void testManifestsWithLeadingDotSlash() throws Exception{ - Path bagPath = Paths.get(new File("src/test/resources/bag-with-leading-dot-slash-in-manifest").toURI()); - Bag bag = reader.read(bagPath); - - sut.isValid(bag, true); - } - - @Test - public void testCanQuickVerify() throws Exception{ - Bag bag = reader.read(rootDir); - boolean canQuickVerify = BagVerifier.canQuickVerify(bag); - Assertions.assertFalse(canQuickVerify, - "Since " + bag.getRootDir() + " DOES NOT contain the metadata Payload-Oxum then it should return false!"); - - Path passingRootDir = Paths.get(new File("src/test/resources/bags/v0_94/bag").toURI()); - bag = reader.read(passingRootDir); - canQuickVerify = BagVerifier.canQuickVerify(bag); - Assertions.assertTrue(canQuickVerify, - "Since " + bag.getRootDir() + " DOES contain the metadata Payload-Oxum then it should return true!"); - } - - @Test - public void testQuickVerify() throws Exception{ - Path passingRootDir = Paths.get(new File("src/test/resources/bags/v0_94/bag").toURI()); - Bag bag = reader.read(passingRootDir); - - BagVerifier.quicklyVerify(bag); - } - - @Test - public void testHoleyBag() throws Exception { - Path bagDir = Paths.get("src", "test", "resources", "md5Bag"); - Path copyDir = copyBagToTempFolder(bagDir); - Path readme = copyDir.resolve("data/readme.txt"); - byte[] content = Files.readAllBytes(readme); - Files.delete(readme); - - // Create a local file to serve as "remote" resource - Path remoteFile = copyDir.resolve("remote-readme.txt"); - Files.write(remoteFile, content); - URL remoteUrl = remoteFile.toUri().toURL(); - - // Create fetch.txt - Path fetchFile = copyDir.resolve("fetch.txt"); - // Format of fetch.txt: url length path - // IMPORTANT: BagReader uses relative paths from root for fetch items, - // but they should NOT have 'data/' prefix if they are in data directory? - // Actually, BagIt spec says it's the path relative to the bag root. - String fetchLine = remoteUrl.toString() + " " + content.length + " data/readme.txt\n"; - Files.write(fetchFile, fetchLine.getBytes()); - - Bag bag = reader.read(copyDir); - - // With the new logic, it should be valid even without explicitly passing true, - // because fetch.txt is present and contains data/readme.txt - sut.isValid(bag, true); - - // It should also be valid if we explicitly pass true - sut.isValid(bag, true, true); - } } From ba71f4eb2152cbca7bb9181510c886f2194d6aaa Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Wed, 15 Jul 2026 11:16:19 +0200 Subject: [PATCH 090/104] [maven-release-plugin] prepare release v1.5.0 --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 0581cfb9a..a21f27af9 100644 --- a/pom.xml +++ b/pom.xml @@ -16,8 +16,7 @@ limitations under the License. --> - + 4.0.0 @@ -27,7 +26,7 @@ dans-bagit-lib - 1.5.0-SNAPSHOT + 1.5.0 DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -36,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - 1.0.0-SNAPSHOT + v1.5.0 From 6478814129aead55fe840f2c6a87f6e8624b19d2 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Wed, 15 Jul 2026 11:16:23 +0200 Subject: [PATCH 091/104] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a21f27af9..8b6d86466 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.5.0 + 1.5.1-SNAPSHOT DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - v1.5.0 + 1.0.0-SNAPSHOT From fd495422e7a74e0425e049a2ae822fe87ad2d2b0 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 20 Jul 2026 14:37:06 +0200 Subject: [PATCH 092/104] DD-2359 Ignore list for payloadmanifest verification per algorithm (#16) Added optional map of checksum algorithm to fetch items to skip. This is useful when you have externally established that certain fetch item checksum are valid and don't want the library to recalculate them. --- .../knaw/dans/bagit/verify/BagVerifier.java | 21 +++++++++-- .../bagit/verify/CheckManifestHashesTask.java | 32 +++++++++++----- .../dans/bagit/verify/BagVerifierTest.java | 37 +++++++++++++++++++ 3 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java b/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java index cb2a28e8a..3cafaa4f6 100644 --- a/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/BagVerifier.java @@ -22,9 +22,11 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.ResourceBundle; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -47,6 +49,7 @@ import nl.knaw.dans.bagit.hash.BagitAlgorithmNameToSupportedAlgorithmMapping; import nl.knaw.dans.bagit.hash.Hasher.HashOptions; import nl.knaw.dans.bagit.hash.StandardBagitAlgorithmNameToSupportedAlgorithmMapping; +import nl.knaw.dans.bagit.hash.SupportedAlgorithm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -189,6 +192,10 @@ public void isValid(final Bag bag, final boolean ignoreHiddenFiles, final boolea } public void isValid(final Bag bag, final boolean ignoreHiddenFiles, final boolean allowHoley, final Map extraHeaders, final Map> urlConfigs) throws IOException, FileNotInManifestException, MissingPayloadManifestException, MissingBagitFileException, MissingPayloadDirectoryException, FileNotInPayloadDirectoryException, InterruptedException, MaliciousPathException, CorruptChecksumException, VerificationException, UnsupportedAlgorithmException, InvalidBagitFileFormatException{ + isValid(bag, ignoreHiddenFiles, allowHoley, extraHeaders, urlConfigs, null); + } + + public void isValid(final Bag bag, final boolean ignoreHiddenFiles, final boolean allowHoley, final Map extraHeaders, final Map> urlConfigs, final Map> ignoredFetchItems) throws IOException, FileNotInManifestException, MissingPayloadManifestException, MissingBagitFileException, MissingPayloadDirectoryException, FileNotInPayloadDirectoryException, InterruptedException, MaliciousPathException, CorruptChecksumException, VerificationException, UnsupportedAlgorithmException, InvalidBagitFileFormatException{ logger.info(messages.getString("checking_bag_is_valid"), bag.getRootDir()); final boolean holey = allowHoley || !bag.getItemsToFetch().isEmpty(); isComplete(bag, ignoreHiddenFiles, holey); @@ -202,12 +209,13 @@ public void isValid(final Bag bag, final boolean ignoreHiddenFiles, final boolea logger.debug(messages.getString("checking_payload_checksums")); for(final Manifest payloadManifest : bag.getPayLoadManifests()){ - checkHashes(payloadManifest, fetchItems, holey, extraHeaders, urlConfigs); + final Collection ignoredItemsForAlgorithm = ignoredFetchItems != null ? ignoredFetchItems.get(payloadManifest.getAlgorithm()) : null; + checkHashes(payloadManifest, fetchItems, holey, extraHeaders, urlConfigs, ignoredItemsForAlgorithm); } logger.debug(messages.getString("checking_tag_file_checksums")); for(final Manifest tagManifest : bag.getTagManifests()){ - checkHashes(tagManifest, null, false, extraHeaders, urlConfigs); + checkHashes(tagManifest, null, false, extraHeaders, urlConfigs, null); } } @@ -228,14 +236,21 @@ void checkHashes(final Manifest manifest, final Map fetchItems, } void checkHashes(final Manifest manifest, final Map fetchItems, final boolean holey, final Map extraHeaders, final Map> urlConfigs) throws CorruptChecksumException, InterruptedException, VerificationException{ + checkHashes(manifest, fetchItems, holey, extraHeaders, urlConfigs, null); + } + + void checkHashes(final Manifest manifest, final Map fetchItems, final boolean holey, final Map extraHeaders, final Map> urlConfigs, final Collection ignoredFetchItems) throws CorruptChecksumException, InterruptedException, VerificationException{ final CountDownLatch latch = new CountDownLatch( manifest.getFileToChecksumMap().size()); //TODO maybe return all of these at some point... final Collection exceptions = Collections.synchronizedCollection(new ArrayList<>()); final HashOptions hashOptions = getHashOptions(); + final Collection optimizedIgnoredFetchItems = ignoredFetchItems == null ? null : + (ignoredFetchItems instanceof Set ? ignoredFetchItems : new HashSet<>(ignoredFetchItems)); + for(final Entry entry : manifest.getFileToChecksumMap().entrySet()){ - executor.execute(new CheckManifestHashesTask(entry, manifest.getAlgorithm().getMessageDigestName(), latch, exceptions, fetchItems, holey, extraHeaders, urlConfigs, hashOptions)); + executor.execute(new CheckManifestHashesTask(entry, manifest.getAlgorithm().getMessageDigestName(), latch, exceptions, fetchItems, holey, extraHeaders, urlConfigs, hashOptions, optimizedIgnoredFetchItems)); } latch.await(); diff --git a/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java b/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java index 509c966d8..ab657a36b 100644 --- a/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java +++ b/src/main/java/nl/knaw/dans/bagit/verify/CheckManifestHashesTask.java @@ -54,24 +54,29 @@ public class CheckManifestHashesTask implements Runnable { private transient final Map extraHeaders; private transient final Map> urlConfigs; private transient final HashOptions hashOptions; + private transient final Collection ignoredFetchItems; public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions) { - this(entry, algorithm, latch, exceptions, null, false, null, null, null); + this(entry, algorithm, latch, exceptions, null, false, null, null, null, null); } public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey) { - this(entry, algorithm, latch, exceptions, fetchItems, holey, null, null, null); + this(entry, algorithm, latch, exceptions, fetchItems, holey, null, null, null, null); } public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey, final Map extraHeaders) { - this(entry, algorithm, latch, exceptions, fetchItems, holey, extraHeaders, null, null); + this(entry, algorithm, latch, exceptions, fetchItems, holey, extraHeaders, null, null, null); } public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey, final Map extraHeaders, final Map> urlConfigs) { - this(entry, algorithm, latch, exceptions, fetchItems, holey, extraHeaders, urlConfigs, null); + this(entry, algorithm, latch, exceptions, fetchItems, holey, extraHeaders, urlConfigs, null, null); } public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey, final Map extraHeaders, final Map> urlConfigs, final HashOptions hashOptions) { + this(entry, algorithm, latch, exceptions, fetchItems, holey, extraHeaders, urlConfigs, hashOptions, null); + } + + public CheckManifestHashesTask(final Entry entry, final String algorithm, final CountDownLatch latch, final Collection exceptions, final Map fetchItems, final boolean holey, final Map extraHeaders, final Map> urlConfigs, final HashOptions hashOptions, final Collection ignoredFetchItems) { this.entry = entry; this.algorithm = algorithm; this.latch = latch; @@ -81,13 +86,14 @@ public CheckManifestHashesTask(final Entry entry, final String alg this.extraHeaders = extraHeaders; this.urlConfigs = urlConfigs; this.hashOptions = hashOptions; + this.ignoredFetchItems = ignoredFetchItems; } @Override public void run() { try { final MessageDigest messageDigest = MessageDigest.getInstance(algorithm); - checkManifestEntry(entry, messageDigest, algorithm, fetchItems, holey, extraHeaders, urlConfigs, hashOptions); + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, holey, extraHeaders, urlConfigs, hashOptions, ignoredFetchItems); } catch (IOException | CorruptChecksumException | NoSuchAlgorithmException e) { exceptions.add(e); } @@ -95,22 +101,26 @@ public void run() { } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm) throws IOException, CorruptChecksumException { - checkManifestEntry(entry, messageDigest, algorithm, null, false, null, null, null); + checkManifestEntry(entry, messageDigest, algorithm, null, false, null, null, null, null); } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley) throws IOException, CorruptChecksumException { - checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, null, null, null); + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, null, null, null, null); } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley, final Map extraHeaders) throws IOException, CorruptChecksumException { - checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, extraHeaders, null, null); + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, extraHeaders, null, null, null); } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley, final Map extraHeaders, final Map> urlConfigs) throws IOException, CorruptChecksumException { - checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, extraHeaders, urlConfigs, null); + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, extraHeaders, urlConfigs, null, null); } protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley, final Map extraHeaders, final Map> urlConfigs, final HashOptions hashOptions) throws IOException, CorruptChecksumException { + checkManifestEntry(entry, messageDigest, algorithm, fetchItems, allowHoley, extraHeaders, urlConfigs, hashOptions, null); + } + + protected static void checkManifestEntry(final Entry entry, final MessageDigest messageDigest, final String algorithm, final Map fetchItems, final boolean allowHoley, final Map extraHeaders, final Map> urlConfigs, final HashOptions hashOptions, final Collection ignoredFetchItems) throws IOException, CorruptChecksumException { if (Files.exists(entry.getKey())) { logger.debug(messages.getString("checking_checksums"), entry.getKey(), entry.getValue()); final String hash = Hasher.hash(entry.getKey(), messageDigest); @@ -120,6 +130,10 @@ protected static void checkManifestEntry(final Entry entry, final } } else if (allowHoley && fetchItems != null && fetchItems.containsKey(entry.getKey())) { final FetchItem item = fetchItems.get(entry.getKey()); + if (ignoredFetchItems != null && ignoredFetchItems.contains(item)) { + logger.debug("skipping hashing for file [{}] because it is in the ignored fetch items list", entry.getKey()); + return; + } logger.debug("File {} does not exist, but it is in fetch.txt, and allowHoley is true. Hashing from URL: {}", entry.getKey(), item.url); final Map mergedHeaders = mergeHeaders(item.url, extraHeaders, urlConfigs); final String hash = Hasher.hash(item, messageDigest, mergedHeaders, hashOptions); diff --git a/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java index dcff289c5..e43e1da69 100644 --- a/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java +++ b/src/test/java/nl/knaw/dans/bagit/verify/BagVerifierTest.java @@ -39,7 +39,12 @@ import java.nio.file.Paths; import java.security.Security; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import nl.knaw.dans.bagit.domain.FetchItem; public class BagVerifierTest extends TempFolderTest { static { @@ -311,4 +316,36 @@ public void testHoleyBag() throws Exception { // It should also be valid if we explicitly pass true sut.isValid(bag, true, true); } + + @Test + public void testIgnoredFetchItemsSkipped() throws Exception { + Path bagDir = Paths.get("src", "test", "resources", "md5Bag"); + Path copyDir = copyBagToTempFolder(bagDir); + Path readme = copyDir.resolve("data/readme.txt"); + byte[] content = Files.readAllBytes(readme); + Files.delete(readme); + + // Create a local file to serve as "remote" resource with CORRUPT content + Path remoteFile = copyDir.resolve("remote-readme.txt"); + Files.write(remoteFile, "corrupt content".getBytes()); + URL remoteUrl = remoteFile.toUri().toURL(); + + Path fetchFile = copyDir.resolve("fetch.txt"); + String fetchLine = remoteUrl.toString() + " " + content.length + " data/readme.txt\n"; + Files.write(fetchFile, fetchLine.getBytes()); + + Bag bag = reader.read(copyDir); + + // If we don't ignore it, it should fail due to corrupt checksum + Assertions.assertThrows(CorruptChecksumException.class, () -> { + sut.isValid(bag, true); + }); + + // Now ignore it + Map> ignored = new HashMap<>(); + ignored.put(StandardSupportedAlgorithms.MD5, Collections.singletonList(bag.getItemsToFetch().get(0))); + + // Verification should pass because the hashing is skipped + sut.isValid(bag, true, true, null, null, ignored); + } } From dd928307dc7ec578caeeb00c90f4e5e06517d9f5 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 20 Jul 2026 14:38:07 +0200 Subject: [PATCH 093/104] [maven-release-plugin] prepare release v1.6.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8b6d86466..7b3deee08 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.5.1-SNAPSHOT + 1.6.0 DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - 1.0.0-SNAPSHOT + v1.6.0 From 90cdcc649097592e7256056b757ffbea3d9810fc Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 20 Jul 2026 14:38:11 +0200 Subject: [PATCH 094/104] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7b3deee08..0efc033ae 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ dans-bagit-lib - 1.6.0 + 1.6.1-SNAPSHOT DANS BagIt Library https://github.com/DANS-KNAW/dans-bagit-lib @@ -35,7 +35,7 @@ scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - v1.6.0 + 1.0.0-SNAPSHOT From 16648c4f356f8faf7b445b998abe2c1fdb5f6bf4 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Tue, 28 Jul 2026 12:43:29 +0200 Subject: [PATCH 095/104] Updated pymdown-extensions version to 11.0 in all modules. --- .github/workflows/mkdocs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mkdocs/requirements.txt b/.github/workflows/mkdocs/requirements.txt index c3c8aec95..ae6342a6c 100644 --- a/.github/workflows/mkdocs/requirements.txt +++ b/.github/workflows/mkdocs/requirements.txt @@ -1,4 +1,4 @@ mkdocs==1.6.1 pyyaml==6.0.3 -pymdown-extensions==10.21.3 +pymdown-extensions==11.0 mkdocs-markdownextradata-plugin==0.2.6 From a39e8ea7bb5c603c6c6e6bc7a2378396cf25c521 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Tue, 28 Jul 2026 12:51:35 +0200 Subject: [PATCH 096/104] Updated pymdown-extensions version to 11.0 in all modules. --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 309e06cfd..61a8eecd2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v6 with: - python-version: 3.9 + python-version: '3.10' cache: 'pip' - name: Set up JDK 17 From 12c4f6a165f23610915afaf2cb582cdc148d3531 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 10 Aug 2026 14:33:06 +0200 Subject: [PATCH 097/104] Updated pymdown-extensions version to 11.0.1 in all modules. --- .github/workflows/mkdocs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mkdocs/requirements.txt b/.github/workflows/mkdocs/requirements.txt index ae6342a6c..97789bedf 100644 --- a/.github/workflows/mkdocs/requirements.txt +++ b/.github/workflows/mkdocs/requirements.txt @@ -1,4 +1,4 @@ mkdocs==1.6.1 pyyaml==6.0.3 -pymdown-extensions==11.0 +pymdown-extensions==11.0.1 mkdocs-markdownextradata-plugin==0.2.6 From 5563fc5af380a20d375b9f63d95ee227d99ab87c Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Thu, 27 Aug 2026 18:46:31 +0200 Subject: [PATCH 098/104] Upgrade to Java 21 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0efc033ae..f8f482732 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ nl.knaw.dans dd-parent - 1.11.0 + 1.12.0 dans-bagit-lib @@ -137,7 +137,7 @@ org.jacoco jacoco-maven-plugin - 0.8.8 + 0.8.15 gov/loc/repository/bagit/domain/** From ce2d0afa60023c98ab848a3505201bdb3ecc1943 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Tue, 1 Sep 2026 14:43:27 +0200 Subject: [PATCH 099/104] Upgrade GitHub actions to Java 21 --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 38d7c0280..d35153f89 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v6 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v5 with: - java-version: 17 + java-version: 21 distribution: 'adopt' cache: 'maven' - name: Build with Maven From 56e5bc773c19cb783896242451343993323f0cc1 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Tue, 1 Sep 2026 14:52:20 +0200 Subject: [PATCH 100/104] Upgrade GitHub actions to Java 21 --- .github/workflows/build.yml | 4 ++-- .github/workflows/codeql.yml | 6 +++--- .github/workflows/coverage.yml | 6 +++--- .github/workflows/docs.yml | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d35153f89..171bb959e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,8 +17,8 @@ jobs: - name: Set up JDK 21 uses: actions/setup-java@v5 with: - java-version: 21 - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' cache: 'maven' - name: Build with Maven run: mvn -B clean package --file pom.xml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4b524f80c..ef4961bd5 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -42,11 +42,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v5 with: - java-version: '17' - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' cache: 'maven' # Initializes the CodeQL tools for scanning. diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index a96533231..50cbb46ee 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -6,11 +6,11 @@ jobs: steps: - name: Checkout uses: actions/checkout@v6 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v5 with: - java-version: 17 - distribution: 'adopt' + java-version: '21' + distribution: 'temurin' cache: 'maven' - name: Install dependencies run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 61a8eecd2..133cb768a 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -18,11 +18,11 @@ jobs: python-version: '3.10' cache: 'pip' - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v5 with: - distribution: adopt - java-version: 17 + distribution: 'temurin' + java-version: '21' cache: 'maven' - name: Generate maven site From 96b4125a38163595b57f6fa25808af42d8ac2ea6 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 4 Sep 2026 14:13:43 +0200 Subject: [PATCH 101/104] Added Copilot setup steps --- .github/workflows/copilot-setup-steps.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/copilot-setup-steps.yml diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 000000000..36df92f82 --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,13 @@ +name: Copilot Setup Steps +on: workflow_dispatch +jobs: + copilot-setup-steps: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: 'maven' + - run: mvn -B dependency:go-offline From 83fa557461901ae5f21999410474892f4a6b6b7a Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 7 Sep 2026 12:14:10 +0200 Subject: [PATCH 102/104] Resolved problems with docs --- .gitignore | 2 +- pom.xml | 53 ++++++++++++++++------------------------------------- 2 files changed, 17 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index df7549894..2dd6d1b42 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,7 @@ pom.xml.releaseBackup pom.xml.tag pom.xml.versionsBackup release.properties -site +/site target/ venv/ docs/mvnsite/ diff --git a/pom.xml b/pom.xml index f8f482732..9179124cd 100644 --- a/pom.xml +++ b/pom.xml @@ -119,7 +119,6 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.5.0 private true @@ -130,14 +129,13 @@ org.apache.maven.plugins maven-site-plugin - 3.9.0 + 3.21.0 org.jacoco jacoco-maven-plugin - 0.8.15 gov/loc/repository/bagit/domain/** @@ -186,49 +184,25 @@ maven-release-plugin - - - - - - - - - - - - - - - - - - - - - - - - + + org.apache.maven.plugins + maven-jxr-plugin + 3.6.0 + + org.apache.maven.plugins maven-pmd-plugin - 3.20.0 + 3.28.0 - /category/java/bestpractices.xml - /category/java/codestyle.xml - /category/java/design.xml - /category/java/documentation.xml - /category/java/errorprone.xml - /category/java/performance.xml - /category/java/multithreading.xml + src/main/resources/ruleset.xml @@ -257,18 +231,23 @@ com.github.spotbugs spotbugs-maven-plugin - 4.7.2.1 + 4.10.4.0 org.apache.maven.plugins maven-javadoc-plugin - 3.5.0 private true + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.9.0 + From 33c0b5d1caf5e378957fc8177e6646aaf7c30648 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Mon, 7 Sep 2026 12:14:27 +0200 Subject: [PATCH 103/104] Resolved problems with docs --- src/main/resources/ruleset.xml | 35 ++++++++++++++++++++++++++++++++++ src/site/site.xml | 30 +++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/main/resources/ruleset.xml create mode 100644 src/site/site.xml diff --git a/src/main/resources/ruleset.xml b/src/main/resources/ruleset.xml new file mode 100644 index 000000000..1cc563184 --- /dev/null +++ b/src/main/resources/ruleset.xml @@ -0,0 +1,35 @@ + + + + + PMD ruleset for dans-bagit-lib + + + + + + + + + + + diff --git a/src/site/site.xml b/src/site/site.xml new file mode 100644 index 000000000..de0a41e31 --- /dev/null +++ b/src/site/site.xml @@ -0,0 +1,30 @@ + + + + + org.apache.maven.skins + maven-fluido-skin + 2.0.0-M9 + + + + + From 1c6d110b41966cf9866a3765975bc45463b58604 Mon Sep 17 00:00:00 2001 From: Jan van Mansum Date: Fri, 11 Sep 2026 22:38:44 +0200 Subject: [PATCH 104/104] wip --- .github/workflows/mkdocs/requirements.txt | 1 + .gitignore | 1 + add-javadocs.sh | 30 +++++++++++ docs/.gitignore | 1 + docs/api.md | 2 +- docs/dev.md | 15 +++--- docs/getting-started.md | 15 ++++-- docs/index.md | 55 ++++--------------- main.py | 25 +++++++++ mkdocs.yml | 1 + pom.xml | 65 +++-------------------- 11 files changed, 97 insertions(+), 114 deletions(-) create mode 100755 add-javadocs.sh create mode 100644 docs/.gitignore create mode 100644 main.py diff --git a/.github/workflows/mkdocs/requirements.txt b/.github/workflows/mkdocs/requirements.txt index 97789bedf..d45cf2c73 100644 --- a/.github/workflows/mkdocs/requirements.txt +++ b/.github/workflows/mkdocs/requirements.txt @@ -2,3 +2,4 @@ mkdocs==1.6.1 pyyaml==6.0.3 pymdown-extensions==11.0.1 mkdocs-markdownextradata-plugin==0.2.6 +mkdocs-macros-plugin==1.3.7 diff --git a/.gitignore b/.gitignore index 2dd6d1b42..85ae6c7ae 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ release.properties target/ venv/ docs/mvnsite/ +/__pycache__* diff --git a/add-javadocs.sh b/add-javadocs.sh new file mode 100755 index 000000000..59d44dc6d --- /dev/null +++ b/add-javadocs.sh @@ -0,0 +1,30 @@ +# +# Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +echo "Delomboking first, so that getters and setters will appear in JavaDocs" +mvn clean lombok:delombok +echo "Calling JavaDoc" +mvn javadoc:javadoc +echo "Removing existing JavaDocs if present" +if [ -d "docs/javadocs" ]; then rm -fr docs/javadocs; fi +echo "Moving newly generated JavaDocs in place" +mv target/reports/apidocs docs/javadocs +echo "DONE build and add javadocs" + + + diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..1b2360211 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +javadocs/ \ No newline at end of file diff --git a/docs/api.md b/docs/api.md index d50195310..5b59ada30 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3,4 +3,4 @@ JavaDocs Open the [JavaDocs]{:target=_blank:} in a new tab. -[JavaDocs]: mvnsite/apidocs/index.html +[JavaDocs]: javadocs/index.html diff --git a/docs/dev.md b/docs/dev.md index 5ccb8beda..4de4de9a1 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -1,29 +1,30 @@ Development =========== + This page contains information for developers about how to contribute to this project. Requirements ------------ -* Java 11 +* Java 21 * Maven Running tests and code quality checks ------------------------------------- -Inside the bagit-java root directory, run `mvn verify`. +Inside the project root directory, run `mvn verify`. General ------- -* When extending the library follow the established patterns, to keep it easy to understand for any - new user. +* When extending the library follow the established patterns to keep it easy to understand for any new user. JavaDoc ------- -Since this is a library, the JavaDocs should be relatively extensive, although there is no need to go overboard with this. At a minimum: +Since this is a library, the Javadocs should be relatively extensive, although there is no need to go overboard with this. At a minimum: -* The JavaDocs must be generated successfully. As of today this is a standard part of the build; the build will fail if doc generation fails. -* [Run the documentation site locally](https://dans-knaw.github.io/dans-datastation-architecture/dev/#documentation-with-mkdocs){:target=_blank} to check how it renders. +* The Javadocs must be generated successfully. As of today this is a standard part of the build; the build will fail if doc generation fails. +* [Run the documentation site locally](https://dans-knaw.github.io/dans-datastation-architecture/dev/#documentation-with-mkdocs){:target=_blank} to check how it + renders. diff --git a/docs/getting-started.md b/docs/getting-started.md index 58ec9619d..276ee7cf6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,10 +1,19 @@ Getting started =============== -Basic usage ------------ +Adding the dependency +--------------------- -For including the library as a dependency in a Maven project, see [the installation instructions](./index.md#using-the-library) +To use this parent POM in a Maven project, add the following to your `pom.xml`: + +```xml + + + nl.datastations + dans-bagit-lib + {{ project_version }} + +``` A basic usage example follows: diff --git a/docs/index.md b/docs/index.md index de77b8ae6..7d05b21a4 100755 --- a/docs/index.md +++ b/docs/index.md @@ -1,54 +1,17 @@ Description =========== -Library with classes and functions for working with the BagIt format +Library with classes and functions for working with the BagIt format. -BagIt is a set of hierarchical file layout conventions designed to -support storage and transfer of arbitrary digital content. A "bag" -consists of a directory containing the payload files and other -accompanying metadata files known as "tag" files. The "tags" are -metadata files intended to facilitate and document the storage and -transfer of the bag. Processing a bag does not require any -understanding of the payload file contents, and the payload files can -be accessed without processing the BagIt metadata. +BagIt is a set of hierarchical file layout conventions designed to support storage and transfer of arbitrary digital content. A "bag" consists of a directory +containing the payload files and other accompanying metadata files known as "tag" files. The "tags" are metadata files intended to facilitate and document the +storage and transfer of the bag. Processing a bag does not require any understanding of the payload file contents, and the payload files can be accessed without +processing the BagIt metadata. -This BagIt library is a software library intended to support the creation, -manipulation, and validation of bags. Its current version is 0.97. It is version aware with the earliest -supported version being 0.93. +This BagIt library is a software library intended to support the creation, manipulation, and validation of bags. Its current version is 0.97. It is +version-aware with the earliest supported version being 0.93. See: {:target=_blank}. -This library was first developed by the [LibraryOfCongress](https://github.com/LibraryOfCongress/bagit-java/){:target=_blank:} and -forked by DANS-KNAW. - -Using the library ------------------ - -To use this library in a Maven-based project, add the following to your `pom.xml`. - -### 1. Declare the DANS maven repository - -```xml - - - - - DANS - - true - - https://maven.dans.knaw.nl/releases/ - - -``` - -### 2. Include a dependency on this library - -```xml - - - nl.knaw.dans - dans-bagit-lib - {version} - -``` +This library was first developed by the [LibraryOfCongress](https://github.com/LibraryOfCongress/bagit-java/){:target=_blank:} and forked by DANS-KNAW. The +original project does not seem to be actively maintained anymore, and DANS-KNAW has made some changes to the library to support its own use cases. diff --git a/main.py b/main.py new file mode 100644 index 000000000..d56729d3c --- /dev/null +++ b/main.py @@ -0,0 +1,25 @@ +# +# Copyright (C) 2026 DANS - Data Archiving and Networked Services (info@dans.knaw.nl) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import xml.etree.ElementTree as ET + +def define_env(env): + tree = ET.parse("pom.xml") + root = tree.getroot() + ns = {"m": "http://maven.apache.org/POM/4.0.0"} + version = root.findtext("m:version", namespaces=ns) or "" + + env.variables["project_version"] = version diff --git a/mkdocs.yml b/mkdocs.yml index 9bd6aaa30..eb8081afc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -37,6 +37,7 @@ extra: plugins: - markdownextradata - search + - macros markdown_extensions: - attr_list diff --git a/pom.xml b/pom.xml index 9179124cd..4986016d5 100644 --- a/pom.xml +++ b/pom.xml @@ -20,22 +20,23 @@ 4.0.0 - nl.knaw.dans - dd-parent - 1.12.0 + nl.datastations + dans-core-systems-parent + 0.2.0 dans-bagit-lib - 1.6.1-SNAPSHOT + 2.0.0-SNAPSHOT DANS BagIt Library - https://github.com/DANS-KNAW/dans-bagit-lib The BAGIT LIBRARY is a software library intended to support the creation, manipulation, and validation of bags. Its current version is 0.97. It is version aware with the earliest supported version being 0.93. + https://github.com/DANS-KNAW/dans-bagit-lib 2023 + scm:git:https://github.com/DANS-KNAW/${project.artifactId} scm:git:ssh://github.com/DANS-KNAW/${project.artifactId} - 1.0.0-SNAPSHOT + HEAD @@ -102,10 +103,10 @@ add-integration-test-source - generate-test-sources add-test-source + generate-test-sources src/integration/java @@ -251,54 +252,4 @@ - - - dans-releases - - true - - - false - - https://maven.dans.knaw.nl/releases/ - - - dans-snapshots - - false - - - true - - https://maven.dans.knaw.nl/snapshots/ - - - - - - dans-releases - - true - - - false - - https://maven.dans.knaw.nl/releases/ - - - dans-snapshots - - false - - - true - - https://maven.dans.knaw.nl/snapshots/ - - - - - - -