diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fc197f2..f52ec2b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## [Unreleased] - ReleaseDate +### Added + +- Support of file size based fixity entries: https://github.com/OCFL/ocfl-java/issues/116 + +### Changed + +- **Breaking:** Builtin digest algorithms must now be specified like `DigestAlgorithRegistry.sha256` instead of + `DigestAlgorithm.sha256`. + ## [2.1.0] - 2024-04-11 ### Fixed diff --git a/docs/USAGE.md b/docs/USAGE.md index 3807943d..da96e650 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -320,7 +320,7 @@ inventory bloat: as much space to store. If you are concerned about space, you can change the algorithm by setting `OcflRepositoryBuilder.ocflConfig(config -> - config.setDefaultDigestAlgorithm(DigestAlgorithm.sha256))`. Note, this + config.setDefaultDigestAlgorithm(DigestAlgorithmRegistry.sha256))`. Note, this only changes the digest algorithm used for *new* OCFL objects. It is not possible to modify existing objects. diff --git a/ocfl-java-api/src/main/java/io/ocfl/api/DigestAlgorithmRegistry.java b/ocfl-java-api/src/main/java/io/ocfl/api/DigestAlgorithmRegistry.java index 572b8b65..774819e4 100644 --- a/ocfl-java-api/src/main/java/io/ocfl/api/DigestAlgorithmRegistry.java +++ b/ocfl-java-api/src/main/java/io/ocfl/api/DigestAlgorithmRegistry.java @@ -25,6 +25,8 @@ package io.ocfl.api; import io.ocfl.api.model.DigestAlgorithm; +import io.ocfl.api.model.SizeDigestAlgorithm; +import io.ocfl.api.model.StandardDigestAlgorithm; import io.ocfl.api.util.Enforce; import java.util.HashMap; import java.util.Map; @@ -35,17 +37,35 @@ */ public final class DigestAlgorithmRegistry { + /* + * From spec + */ + public static final DigestAlgorithm md5 = new StandardDigestAlgorithm("md5", "md5"); + public static final DigestAlgorithm sha1 = new StandardDigestAlgorithm("sha1", "sha-1"); + public static final DigestAlgorithm sha256 = new StandardDigestAlgorithm("sha256", "sha-256"); + public static final DigestAlgorithm sha512 = new StandardDigestAlgorithm("sha512", "sha-512"); + public static final DigestAlgorithm blake2b512 = new StandardDigestAlgorithm("blake2b-512", "blake2b-512"); + + /* + * From extensions: https://ocfl.github.io/extensions/0009-digest-algorithms + */ + public static final DigestAlgorithm blake2b160 = new StandardDigestAlgorithm("blake2b-160", "blake2b-160"); + public static final DigestAlgorithm blake2b256 = new StandardDigestAlgorithm("blake2b-256", "blake2b-256"); + public static final DigestAlgorithm blake2b384 = new StandardDigestAlgorithm("blake2b-384", "blake2b-384"); + public static final DigestAlgorithm sha512_256 = new StandardDigestAlgorithm("sha512/256", "sha-512/256"); + public static final DigestAlgorithm size = new SizeDigestAlgorithm(); + private static final Map REGISTRY = new HashMap<>(Map.of( - DigestAlgorithm.md5.getOcflName(), DigestAlgorithm.md5, - DigestAlgorithm.sha1.getOcflName(), DigestAlgorithm.sha1, - DigestAlgorithm.sha256.getOcflName(), DigestAlgorithm.sha256, - DigestAlgorithm.sha512.getOcflName(), DigestAlgorithm.sha512, - DigestAlgorithm.blake2b512.getOcflName(), DigestAlgorithm.blake2b512, - DigestAlgorithm.blake2b160.getOcflName(), DigestAlgorithm.blake2b160, - DigestAlgorithm.blake2b256.getOcflName(), DigestAlgorithm.blake2b256, - DigestAlgorithm.blake2b384.getOcflName(), DigestAlgorithm.blake2b384, - DigestAlgorithm.sha512_256.getOcflName(), DigestAlgorithm.sha512_256, - DigestAlgorithm.size.getOcflName(), DigestAlgorithm.size)); + md5.getOcflName(), md5, + sha1.getOcflName(), sha1, + sha256.getOcflName(), sha256, + sha512.getOcflName(), sha512, + blake2b512.getOcflName(), blake2b512, + blake2b160.getOcflName(), blake2b160, + blake2b256.getOcflName(), blake2b256, + blake2b384.getOcflName(), blake2b384, + sha512_256.getOcflName(), sha512_256, + size.getOcflName(), size)); private DigestAlgorithmRegistry() {} diff --git a/ocfl-java-api/src/main/java/io/ocfl/api/OcflConstants.java b/ocfl-java-api/src/main/java/io/ocfl/api/OcflConstants.java index 6701ebfd..23fcc60f 100644 --- a/ocfl-java-api/src/main/java/io/ocfl/api/OcflConstants.java +++ b/ocfl-java-api/src/main/java/io/ocfl/api/OcflConstants.java @@ -47,9 +47,9 @@ private OcflConstants() {} public static final String DEFAULT_CONTENT_DIRECTORY = "content"; public static final int DEFAULT_ZERO_PADDING_WIDTH = 0; - public static final DigestAlgorithm DEFAULT_DIGEST_ALGORITHM = DigestAlgorithm.sha512; + public static final DigestAlgorithm DEFAULT_DIGEST_ALGORITHM = DigestAlgorithmRegistry.sha512; public static final Set ALLOWED_DIGEST_ALGORITHMS = - Set.of(DigestAlgorithm.sha512, DigestAlgorithm.sha256); + Set.of(DigestAlgorithmRegistry.sha512, DigestAlgorithmRegistry.sha256); public static final String MUTABLE_HEAD_EXT_NAME = "0005-mutable-head"; public static final String MUTABLE_HEAD_EXT_PATH = EXTENSIONS_DIR + "/" + MUTABLE_HEAD_EXT_NAME; @@ -59,5 +59,5 @@ private OcflConstants() {} public static final String DIGEST_ALGORITHMS_EXT_NAME = "0001-digest-algorithms"; public static final DigestAlgorithm[] VALID_INVENTORY_ALGORITHMS = - new DigestAlgorithm[] {DigestAlgorithm.sha256, DigestAlgorithm.sha512}; + new DigestAlgorithm[] {DigestAlgorithmRegistry.sha256, DigestAlgorithmRegistry.sha512}; } diff --git a/ocfl-java-api/src/main/java/io/ocfl/api/io/FixityCheckChannel.java b/ocfl-java-api/src/main/java/io/ocfl/api/io/FixityCheckChannel.java index 18c6d55f..0407d72b 100644 --- a/ocfl-java-api/src/main/java/io/ocfl/api/io/FixityCheckChannel.java +++ b/ocfl-java-api/src/main/java/io/ocfl/api/io/FixityCheckChannel.java @@ -24,16 +24,13 @@ package io.ocfl.api.io; -import at.favre.lib.bytes.Bytes; import io.ocfl.api.exception.FixityCheckException; -import io.ocfl.api.exception.OcflJavaException; import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.util.Enforce; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; /** * A byte channel wrapper that preforms a fixity check on the bytes that pass through an underlying byte channel. @@ -44,6 +41,7 @@ public class FixityCheckChannel implements ByteChannel { private boolean enabled = true; private final ByteChannel delegate; + private final DigestAlgorithm digestAlgorithm; private final MessageDigest digest; private final String expectedDigestValue; @@ -56,21 +54,8 @@ public class FixityCheckChannel implements ByteChannel { */ public FixityCheckChannel(ByteChannel delegate, DigestAlgorithm digestAlgorithm, String expectedDigestValue) { this.delegate = Enforce.notNull(delegate, "delegate cannot be null"); - this.digest = Enforce.notNull(digestAlgorithm, "digestAlgorithm cannot be null") - .getMessageDigest(); - this.expectedDigestValue = Enforce.notBlank(expectedDigestValue, "expectedDigestValue cannot be blank"); - } - - /** - * Constructs a new FixityCheckChannel - * - * @param delegate the channel to wrap - * @param digestAlgorithm the digest algorithm to use - * @param expectedDigestValue the expected digest value - */ - public FixityCheckChannel(ByteChannel delegate, String digestAlgorithm, String expectedDigestValue) { - this.delegate = Enforce.notNull(delegate, "delegate cannot be null"); - this.digest = messageDigest(Enforce.notBlank(digestAlgorithm, "digestAlgorithm cannot be blank")); + this.digestAlgorithm = Enforce.notNull(digestAlgorithm, "digestAlgorithm cannot be null"); + this.digest = digestAlgorithm.getMessageDigest(); this.expectedDigestValue = Enforce.notBlank(expectedDigestValue, "expectedDigestValue cannot be blank"); } @@ -84,7 +69,7 @@ public FixityCheckChannel(ByteChannel delegate, String digestAlgorithm, String e */ public void checkFixity() { if (enabled) { - var actualDigest = Bytes.wrap(digest.digest()).encodeHex(); + var actualDigest = digestAlgorithm.encode(digest.digest()); if (!expectedDigestValue.equalsIgnoreCase(actualDigest)) { throw new FixityCheckException(String.format( "Expected %s digest: %s; Actual: %s", @@ -139,12 +124,4 @@ public boolean isOpen() { public void close() throws IOException { delegate.close(); } - - private static MessageDigest messageDigest(String digestAlgorithm) { - try { - return MessageDigest.getInstance(digestAlgorithm); - } catch (NoSuchAlgorithmException e) { - throw new OcflJavaException(e); - } - } } diff --git a/ocfl-java-api/src/main/java/io/ocfl/api/io/FixityCheckInputStream.java b/ocfl-java-api/src/main/java/io/ocfl/api/io/FixityCheckInputStream.java index ff3863a5..e4b06883 100644 --- a/ocfl-java-api/src/main/java/io/ocfl/api/io/FixityCheckInputStream.java +++ b/ocfl-java-api/src/main/java/io/ocfl/api/io/FixityCheckInputStream.java @@ -24,15 +24,11 @@ package io.ocfl.api.io; -import at.favre.lib.bytes.Bytes; import io.ocfl.api.exception.FixityCheckException; -import io.ocfl.api.exception.OcflJavaException; import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.util.Enforce; import java.io.InputStream; import java.security.DigestInputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Optional; /** @@ -42,6 +38,7 @@ public class FixityCheckInputStream extends DigestInputStream { private boolean enabled = true; + private final DigestAlgorithm digestAlgorithm; private final String expectedDigestValue; private String actualDigestValue; @@ -53,16 +50,7 @@ public class FixityCheckInputStream extends DigestInputStream { public FixityCheckInputStream( InputStream inputStream, DigestAlgorithm digestAlgorithm, String expectedDigestValue) { super(inputStream, digestAlgorithm.getMessageDigest()); - this.expectedDigestValue = Enforce.notBlank(expectedDigestValue, "expectedDigestValue cannot be blank"); - } - - /** - * @param inputStream the underlying stream - * @param digestAlgorithm the algorithm to use to calculate the digest (eg. sha512) - * @param expectedDigestValue the expected digest value - */ - public FixityCheckInputStream(InputStream inputStream, String digestAlgorithm, String expectedDigestValue) { - super(inputStream, messageDigest(digestAlgorithm)); + this.digestAlgorithm = Enforce.notNull(digestAlgorithm, "digestAlgorithm cannot be null"); this.expectedDigestValue = Enforce.notBlank(expectedDigestValue, "expectedDigestValue cannot be blank"); } @@ -101,7 +89,7 @@ public String getExpectedDigestValue() { */ public Optional getActualDigestValue() { if (enabled && actualDigestValue == null) { - actualDigestValue = Bytes.wrap(digest.digest()).encodeHex(); + actualDigestValue = digestAlgorithm.encode(digest.digest()); } return Optional.of(actualDigestValue); } @@ -127,12 +115,4 @@ public void on(boolean on) { public String toString() { return "[Fixity Check Input Stream] expected: " + expectedDigestValue + "; actual: " + digest.toString(); } - - private static MessageDigest messageDigest(String digestAlgorithm) { - try { - return MessageDigest.getInstance(digestAlgorithm); - } catch (NoSuchAlgorithmException e) { - throw new OcflJavaException(e); - } - } } diff --git a/ocfl-java-api/src/main/java/io/ocfl/api/model/DigestAlgorithm.java b/ocfl-java-api/src/main/java/io/ocfl/api/model/DigestAlgorithm.java index 0c0374f2..e19310ad 100644 --- a/ocfl-java-api/src/main/java/io/ocfl/api/model/DigestAlgorithm.java +++ b/ocfl-java-api/src/main/java/io/ocfl/api/model/DigestAlgorithm.java @@ -27,48 +27,34 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import io.ocfl.api.DigestAlgorithmRegistry; -import io.ocfl.api.exception.OcflInputException; -import io.ocfl.api.exception.OcflJavaException; import io.ocfl.api.util.Enforce; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Objects; -import java.util.function.Supplier; /** * Maps OCFL defined digest algorithms to their Java names. Java does not include built-in implementations for all of the * algorithms, using a 3rd party provider, such as BouncyCastle, is necessary for some, such as blake2b. New algorithms * should be registered in the {@link DigestAlgorithmRegistry}. */ -public class DigestAlgorithm { - - /* - * From spec - */ - public static final DigestAlgorithm md5 = new DigestAlgorithm("md5", "md5"); - public static final DigestAlgorithm sha1 = new DigestAlgorithm("sha1", "sha-1"); - public static final DigestAlgorithm sha256 = new DigestAlgorithm("sha256", "sha-256"); - public static final DigestAlgorithm sha512 = new DigestAlgorithm("sha512", "sha-512"); - public static final DigestAlgorithm blake2b512 = new DigestAlgorithm("blake2b-512", "blake2b-512"); - - /* - * From extensions: https://ocfl.github.io/extensions/0009-digest-algorithms - */ - public static final DigestAlgorithm blake2b160 = new DigestAlgorithm("blake2b-160", "blake2b-160"); - public static final DigestAlgorithm blake2b256 = new DigestAlgorithm("blake2b-256", "blake2b-256"); - public static final DigestAlgorithm blake2b384 = new DigestAlgorithm("blake2b-384", "blake2b-384"); - public static final DigestAlgorithm sha512_256 = new DigestAlgorithm("sha512/256", "sha-512/256"); - public static final DigestAlgorithm size = new DigestAlgorithm("size", new Supplier() { - @Override - public SizeMessageDigest get() { - return new SizeMessageDigest(); - } - }); +public abstract class DigestAlgorithm { private final String ocflName; private final String javaStandardName; - private final Supplier messageDigestSupplier; + /** + * Creates a new {@link MessageDigest} that implements the digest algorithm + * + * @return new {@link MessageDigest} + */ + public abstract MessageDigest getMessageDigest(); + + /** + * Encodes a binary digest value into a string representation. + * + * @param value digest value + * @return the digest value as a string + */ + public abstract String encode(byte[] value); /** * Creates a DigestAlgorithm for the given OCFL name. If the name is not mapped in the {@link DigestAlgorithmRegistry} @@ -82,13 +68,7 @@ public SizeMessageDigest get() { public static DigestAlgorithm fromOcflName(String ocflName) { var algorithm = DigestAlgorithmRegistry.getAlgorithm(ocflName); if (algorithm == null) { - //return a supplier that throws an OcflInputException - algorithm = new DigestAlgorithm(ocflName, new Supplier() { - @Override - public MessageDigest get() { - throw new OcflInputException("specified digest algorithm is not supported"); - } - }); + algorithm = new StandardDigestAlgorithm(ocflName, null); } return algorithm; } @@ -104,24 +84,7 @@ public MessageDigest get() { public static DigestAlgorithm fromOcflName(String ocflName, String javaStandardName) { var algorithm = DigestAlgorithmRegistry.getAlgorithm(ocflName); if (algorithm == null) { - algorithm = new DigestAlgorithm(ocflName, javaStandardName); - } - return algorithm; - } - - /** - * Creates a DigestAlgorithm for the given OCFL name. If the name is not mapped in the {@link DigestAlgorithmRegistry} - * then a new object is created, but not automatically added to the registry. - * - * @param ocflName ocfl name of algorithm - * @param messageDigestSupplier a supplier that provides a new MessageDigest instance - * @return digest algorithm - */ - public static DigestAlgorithm fromOcflName(String ocflName, - Supplier messageDigestSupplier) { - var algorithm = DigestAlgorithmRegistry.getAlgorithm(ocflName); - if (algorithm == null) { - algorithm = new DigestAlgorithm(ocflName, messageDigestSupplier); + algorithm = new StandardDigestAlgorithm(ocflName, javaStandardName); } return algorithm; } @@ -129,22 +92,6 @@ public static DigestAlgorithm fromOcflName(String ocflName, protected DigestAlgorithm(String ocflName, String javaStandardName) { this.ocflName = Enforce.notBlank(ocflName, "ocflName cannot be blank").toLowerCase(); this.javaStandardName = javaStandardName; - this.messageDigestSupplier = new Supplier() { - @Override - public MessageDigest get() { - try { - return MessageDigest.getInstance(javaStandardName); - } catch (NoSuchAlgorithmException e) { - throw new OcflJavaException("Failed to create message digest for: " + this, e); - } - } - }; - } - - protected DigestAlgorithm(String ocflName, Supplier messageDigestSupplier) { - this.ocflName = Enforce.notBlank(ocflName, "ocflName cannot be blank").toLowerCase(); - this.javaStandardName = ""; - this.messageDigestSupplier = messageDigestSupplier; } /** @@ -175,20 +122,9 @@ public boolean hasJavaStandardName() { return javaStandardName != null; } - /** - * Returns a new MessageDigest - * - * @return MessageDigest - */ - public MessageDigest getMessageDigest() { - return messageDigestSupplier.get(); - } - @Override public String toString() { - return "DigestAlgorithm{" + "ocflName='" - + ocflName + '\'' + ", javaStandardName='" - + getJavaStandardName() + '\'' + '}'; + return "DigestAlgorithm{ocflName='" + ocflName + "', javaStandardName='" + getJavaStandardName() + "'}"; } @Override diff --git a/ocfl-java-api/src/main/java/io/ocfl/api/model/SizeDigestAlgorithm.java b/ocfl-java-api/src/main/java/io/ocfl/api/model/SizeDigestAlgorithm.java new file mode 100644 index 00000000..0b8473e3 --- /dev/null +++ b/ocfl-java-api/src/main/java/io/ocfl/api/model/SizeDigestAlgorithm.java @@ -0,0 +1,82 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 OCFL Java Implementers Group + * + * 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. + */ + +package io.ocfl.api.model; + +import at.favre.lib.bytes.Bytes; +import java.nio.ByteBuffer; +import java.security.MessageDigest; + +/** + * Defines a digest algorithms which calculates the size (byte count) for the input + * and returns it as integer represented as string as specified in + * OCFL community extension 9 + */ +public class SizeDigestAlgorithm extends DigestAlgorithm { + + public SizeDigestAlgorithm() { + super("size", "size"); + } + + @Override + public MessageDigest getMessageDigest() { + return new SizeMessageDigest(); + } + + @Override + public String encode(byte[] value) { + return String.valueOf(Bytes.wrap(value).toLong()); + } + + private static class SizeMessageDigest extends MessageDigest { + + public SizeMessageDigest() { + super("size"); + } + + private long size = 0; + + @Override + protected void engineUpdate(byte input) { + size++; + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + size += len; + } + + @Override + protected byte[] engineDigest() { + var buffer = ByteBuffer.allocate(Long.BYTES); + buffer.putLong(size); + return buffer.array(); + } + + @Override + protected void engineReset() { + size = 0; + } + } +} diff --git a/ocfl-java-api/src/main/java/io/ocfl/api/model/SizeMessageDigest.java b/ocfl-java-api/src/main/java/io/ocfl/api/model/SizeMessageDigest.java deleted file mode 100644 index d5b609c6..00000000 --- a/ocfl-java-api/src/main/java/io/ocfl/api/model/SizeMessageDigest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2024 OCFL Java Implementers Group - * - * 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. - */ -package io.ocfl.api.model; - -import java.security.MessageDigest; -import java.util.concurrent.atomic.AtomicLong; - -/** -* Defines a digest algorithms which calculates the size (byte count) for the input -* and returns it as integer represented as string as specified in -* OCFL community extension 9: https://ocfl.github.io/extensions/0009-digest-algorithms -*/ -public class SizeMessageDigest extends MessageDigest { - - public SizeMessageDigest() { - super("size"); - } - - AtomicLong size = new AtomicLong(0); - - @Override - protected void engineUpdate(byte input) { - size.incrementAndGet(); - } - - @Override - protected void engineUpdate(byte[] input, int offset, int len) { - size.addAndGet(len); - } - - @Override - protected byte[] engineDigest() { - return SizeMessageDigest.formatDigestAsByteArray(size.get()); - } - - @Override - protected void engineReset() { - size.set(0); - } - - /** - * convert byte array of message digest into a hex compatible String - * add leading zero to ensure that the length of string is even - * - * @param digest as byte[] - * @return the String containing the decimal value of the size - */ - public static String formatDigest(byte[] digest) { - //Java17: String sizeResult = java.util.HexFormat.of().formatHex(digest); - StringBuilder result = new StringBuilder(); - for (byte aByte : digest) { - result.append(String.format("%02x", aByte)); - } - return result.length() % 2 == 0 ? result.toString() : "0" + result.toString(); - } - - /** - * convert the digest value into a hex compatible String - * add leading zero to ensure that the length of string is even - * - * @param size as long value - * @return the String containing the decimal value of the size - */ - public static String formatDigest(long size) { - String sizeResult = Long.toString(size); - return sizeResult.length() % 2 == 0 ? sizeResult : "0" + sizeResult; - } - - /** - * convert the digest value into a byte array - * - * @param size as long value - * @return the byte array representing the size - * as a string representation of the integer in decimal notation - */ - public static byte[] formatDigestAsByteArray(long size) { - String s = formatDigest(size); - byte[] result = new byte[s.length() / 2]; - for (int i = 0; i < result.length; i++) { - int index = i * 2; - int val = Integer.parseInt(s.substring(index, index + 2), 16); - result[i] = (byte) val; - } - return result; - } - -} diff --git a/ocfl-java-api/src/main/java/io/ocfl/api/model/StandardDigestAlgorithm.java b/ocfl-java-api/src/main/java/io/ocfl/api/model/StandardDigestAlgorithm.java new file mode 100644 index 00000000..c799178d --- /dev/null +++ b/ocfl-java-api/src/main/java/io/ocfl/api/model/StandardDigestAlgorithm.java @@ -0,0 +1,55 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 OCFL Java Implementers Group + * + * 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. + */ + +package io.ocfl.api.model; + +import at.favre.lib.bytes.Bytes; +import io.ocfl.api.exception.OcflJavaException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Implementation of {@link DigestAlgorithm} that uses a standard {@link MessageDigest} and encodes the digest value + * to a lowercase, hex string. + */ +public class StandardDigestAlgorithm extends DigestAlgorithm { + + public StandardDigestAlgorithm(String ocflName, String javaStandardName) { + super(ocflName, javaStandardName); + } + + @Override + public MessageDigest getMessageDigest() { + try { + return MessageDigest.getInstance(getJavaStandardName()); + } catch (NoSuchAlgorithmException e) { + throw new OcflJavaException("Failed to create message digest for: " + this, e); + } + } + + @Override + public String encode(byte[] value) { + return Bytes.wrap(value).encodeHex(); + } +} diff --git a/ocfl-java-api/src/test/java/io/ocfl/api/io/FixityCheckChannelTest.java b/ocfl-java-api/src/test/java/io/ocfl/api/io/FixityCheckChannelTest.java index d2c9f66a..eff7f1ed 100644 --- a/ocfl-java-api/src/test/java/io/ocfl/api/io/FixityCheckChannelTest.java +++ b/ocfl-java-api/src/test/java/io/ocfl/api/io/FixityCheckChannelTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import at.favre.lib.bytes.Bytes; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.exception.OcflIOException; import io.ocfl.api.model.DigestAlgorithm; import java.io.IOException; @@ -23,17 +23,17 @@ public class FixityCheckChannelTest { @Test public void shouldCopyFileAndComputeDigestWhenEnabled() throws IOException { var path = writeFile("test.txt", "This is a test file"); - var expectedDigest = computeDigest(DigestAlgorithm.md5, path); + var expectedDigest = computeDigest(DigestAlgorithmRegistry.md5, path); var outPath = tempDir.resolve("test-copy.txt"); var fileChannel = FileChannel.open(path, StandardOpenOption.READ); - try (var fixityChannel = new FixityCheckChannel(fileChannel, DigestAlgorithm.md5, expectedDigest)) { + try (var fixityChannel = new FixityCheckChannel(fileChannel, DigestAlgorithmRegistry.md5, expectedDigest)) { transfer(fixityChannel, outPath); fixityChannel.checkFixity(); } - assertEquals(expectedDigest, computeDigest(DigestAlgorithm.md5, outPath)); + assertEquals(expectedDigest, computeDigest(DigestAlgorithmRegistry.md5, outPath)); } private void transfer(ReadableByteChannel srcChannel, Path dstPath) throws IOException { @@ -57,7 +57,7 @@ private String computeDigest(DigestAlgorithm algorithm, Path path) { buffer.clear(); } - return Bytes.wrap(digest.digest()).encodeHex(); + return algorithm.encode(digest.digest()); } catch (IOException e) { throw new OcflIOException(e); } diff --git a/ocfl-java-api/src/test/java/io/ocfl/api/model/JacksonTest.java b/ocfl-java-api/src/test/java/io/ocfl/api/model/JacksonTest.java index 6262dab3..b02ec66d 100644 --- a/ocfl-java-api/src/test/java/io/ocfl/api/model/JacksonTest.java +++ b/ocfl-java-api/src/test/java/io/ocfl/api/model/JacksonTest.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.ocfl.api.DigestAlgorithmRegistry; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Map; @@ -24,7 +25,7 @@ public void setup() { public void roundTripObjectDetails() throws JsonProcessingException { var original = new ObjectDetails() .setId("id") - .setDigestAlgorithm(DigestAlgorithm.sha512) + .setDigestAlgorithm(DigestAlgorithmRegistry.sha512) .setHeadVersionNum(VersionNum.V1) .setVersions(Map.of( VersionNum.V1, @@ -40,7 +41,7 @@ public void roundTripObjectDetails() throws JsonProcessingException { new FileDetails() .setPath("file.txt") .setStorageRelativePath("object/file.txt") - .setFixity(Map.of(DigestAlgorithm.sha512, "abc123")))))); + .setFixity(Map.of(DigestAlgorithmRegistry.sha512, "abc123")))))); var json = objectMapper.writeValueAsString(original); diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/DefaultOcflObjectUpdater.java b/ocfl-java-core/src/main/java/io/ocfl/core/DefaultOcflObjectUpdater.java index 2096adc8..45d6cd66 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/DefaultOcflObjectUpdater.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/DefaultOcflObjectUpdater.java @@ -28,6 +28,7 @@ import io.ocfl.api.OcflObjectUpdater; import io.ocfl.api.OcflOption; import io.ocfl.api.exception.FixityCheckException; +import io.ocfl.api.exception.OcflIOException; import io.ocfl.api.exception.OcflInputException; import io.ocfl.api.io.FixityCheckInputStream; import io.ocfl.api.model.DigestAlgorithm; @@ -39,6 +40,8 @@ import io.ocfl.core.util.DigestUtil; import io.ocfl.core.util.FileUtil; import io.ocfl.core.util.UncheckedFiles; + +import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; @@ -294,7 +297,15 @@ public OcflObjectUpdater addFileFixity(String logicalPath, DigestAlgorithm algor } LOG.debug("Computing {} hash of {}", algorithm.getJavaStandardName(), file); - digest = DigestUtil.computeDigestHex(algorithm, file); + if ("size".equals(algorithm.getOcflName())) { + try { + digest = String.valueOf(Files.size(file)); + } catch (IOException e) { + throw OcflIOException.from(e); + } + } else { + digest = DigestUtil.computeDigestHex(algorithm, file); + } } if (!value.equalsIgnoreCase(digest)) { diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/DefaultOcflRepository.java b/ocfl-java-core/src/main/java/io/ocfl/core/DefaultOcflRepository.java index b14f9489..4d11ea6b 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/DefaultOcflRepository.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/DefaultOcflRepository.java @@ -24,7 +24,6 @@ package io.ocfl.core; -import at.favre.lib.bytes.Bytes; import io.ocfl.api.OcflConfig; import io.ocfl.api.OcflConstants; import io.ocfl.api.OcflObjectUpdater; @@ -709,7 +708,9 @@ protected Inventory writeInventory(Inventory inventory, Path stagingDir) { outStream, inventory.getDigestAlgorithm().getMessageDigest()); inventoryMapper.write(digestStream, inventory); - var digest = Bytes.wrap(digestStream.getMessageDigest().digest()).encodeHex(); + var digest = inventory + .getDigestAlgorithm() + .encode(digestStream.getMessageDigest().digest()); SidecarMapper.writeSidecar(inventory, digest, stagingDir); return inventory.buildFrom().inventoryDigest(digest).build(); diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/HashedNTupleIdEncapsulationLayoutExtension.java b/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/HashedNTupleIdEncapsulationLayoutExtension.java index 0a3d1caa..1635fcc4 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/HashedNTupleIdEncapsulationLayoutExtension.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/HashedNTupleIdEncapsulationLayoutExtension.java @@ -108,7 +108,7 @@ public String mapObjectId(String objectId) { throw new OcflExtensionException("This extension must be initialized before it can be used."); } - var digest = DigestUtil.computeDigestHex(config.getDigestAlgorithm(), objectId, false); + var digest = DigestUtil.computeDigestHex(config.getDigestAlgorithm(), objectId); var pathBuilder = new StringBuilder(); diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/HashedNTupleLayoutExtension.java b/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/HashedNTupleLayoutExtension.java index 0e36dc1a..3b9fa019 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/HashedNTupleLayoutExtension.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/HashedNTupleLayoutExtension.java @@ -93,7 +93,7 @@ public String mapObjectId(String objectId) { throw new OcflExtensionException("This extension must be initialized before it can be used."); } - var digest = DigestUtil.computeDigestHex(config.getDigestAlgorithm(), objectId, false); + var digest = DigestUtil.computeDigestHex(config.getDigestAlgorithm(), objectId); if (config.getTupleSize() == 0) { return digest; diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/config/HashedNTupleIdEncapsulationLayoutConfig.java b/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/config/HashedNTupleIdEncapsulationLayoutConfig.java index 954764ea..562719ab 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/config/HashedNTupleIdEncapsulationLayoutConfig.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/config/HashedNTupleIdEncapsulationLayoutConfig.java @@ -25,6 +25,7 @@ package io.ocfl.core.extension.storage.layout.config; import com.fasterxml.jackson.annotation.JsonIgnore; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.exception.OcflExtensionException; import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.util.Enforce; @@ -38,7 +39,7 @@ */ public class HashedNTupleIdEncapsulationLayoutConfig implements OcflExtensionConfig { - private static final DigestAlgorithm DEFAULT_DIGEST_ALGORITHM = DigestAlgorithm.sha256; + private static final DigestAlgorithm DEFAULT_DIGEST_ALGORITHM = DigestAlgorithmRegistry.sha256; private static final int DEFAULT_TUPLE_SIZE = 3; private static final int DEFAULT_NUM_TUPLES = 3; diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/config/HashedNTupleLayoutConfig.java b/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/config/HashedNTupleLayoutConfig.java index 4263cd86..bb3e4132 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/config/HashedNTupleLayoutConfig.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/extension/storage/layout/config/HashedNTupleLayoutConfig.java @@ -25,6 +25,7 @@ package io.ocfl.core.extension.storage.layout.config; import com.fasterxml.jackson.annotation.JsonIgnore; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.exception.OcflExtensionException; import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.util.Enforce; @@ -38,7 +39,7 @@ */ public class HashedNTupleLayoutConfig implements OcflExtensionConfig { - private static final DigestAlgorithm DEFAULT_DIGEST_ALGORITHM = DigestAlgorithm.sha256; + private static final DigestAlgorithm DEFAULT_DIGEST_ALGORITHM = DigestAlgorithmRegistry.sha256; private static final int DEFAULT_TUPLE_SIZE = 3; private static final int DEFAULT_NUM_TUPLES = 3; private static final boolean DEFAULT_SHORT_OBJ_ROOT = false; diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/inventory/AddFileProcessor.java b/ocfl-java-core/src/main/java/io/ocfl/core/inventory/AddFileProcessor.java index 8b7f1c19..6d29c143 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/inventory/AddFileProcessor.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/inventory/AddFileProcessor.java @@ -24,7 +24,6 @@ package io.ocfl.core.inventory; -import at.favre.lib.bytes.Bytes; import io.ocfl.api.OcflOption; import io.ocfl.api.exception.OcflIOException; import io.ocfl.api.model.DigestAlgorithm; @@ -136,7 +135,7 @@ public Map processPath(Path sourcePath, String destinationPath, Oc locks.add(fileLocker.lock(logicalPath)); if (isMove) { - var digest = DigestUtil.computeDigestHex(messageDigest, file); + var digest = DigestUtil.computeDigestHex(messageDigest, digestAlgorithm, file); var result = inventoryUpdater.addFile(digest, logicalPath, options); if (result.isNew()) { @@ -167,7 +166,8 @@ public Map processPath(Path sourcePath, String destinationPath, Oc LOG.debug("Copying file <{}> to <{}>", file, stagingFullPath); Files.copy(file, stream); - digest = Bytes.wrap(stream.getMessageDigest().digest()).encodeHex(); + digest = + digestAlgorithm.encode(stream.getMessageDigest().digest()); result = inventoryUpdater.addFile(digest, logicalPath, options); } catch (IOException e) { throw new OcflIOException(e); diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/inventory/InventoryMapper.java b/ocfl-java-core/src/main/java/io/ocfl/core/inventory/InventoryMapper.java index 4d836783..b6e90341 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/inventory/InventoryMapper.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/inventory/InventoryMapper.java @@ -24,7 +24,6 @@ package io.ocfl.core.inventory; -import at.favre.lib.bytes.Bytes; import com.fasterxml.jackson.databind.InjectableValues; import com.fasterxml.jackson.databind.ObjectMapper; import io.ocfl.api.exception.CorruptObjectException; @@ -196,7 +195,7 @@ private ReadResult readBytes(InputStream stream, DigestAlgorithm digestAlgorithm if (digestAlgorithm != null) { var wrapped = new DigestInputStream(bufferedStream, digestAlgorithm.getMessageDigest()); bytes = wrapped.readAllBytes(); - digest = Bytes.wrap(wrapped.getMessageDigest().digest()).encodeHex(); + digest = digestAlgorithm.encode(wrapped.getMessageDigest().digest()); } else { bytes = bufferedStream.readAllBytes(); } diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/util/DigestUtil.java b/ocfl-java-core/src/main/java/io/ocfl/core/util/DigestUtil.java index 858f801a..21cf8a26 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/util/DigestUtil.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/util/DigestUtil.java @@ -41,19 +41,11 @@ public final class DigestUtil { private DigestUtil() {} public static String computeDigestHex(DigestAlgorithm algorithm, Path path) { - return computeDigestHex(algorithm.getMessageDigest(), path); + return computeDigestHex(algorithm.getMessageDigest(), algorithm, path); } - public static String computeDigestHex(MessageDigest digest, Path path) { - return computeDigestHex(digest, path, false); - } - - public static String computeDigestHex(DigestAlgorithm algorithm, Path path, boolean upperCase) { - return computeDigestHex(algorithm.getMessageDigest(), path, upperCase); - } - - public static String computeDigestHex(MessageDigest digest, Path path, boolean upperCase) { - return Bytes.wrap(computeDigest(digest, path)).encodeHex(upperCase); + public static String computeDigestHex(MessageDigest digest, DigestAlgorithm algorithm, Path path) { + return algorithm.encode(computeDigest(digest, path)); } public static byte[] computeDigest(DigestAlgorithm algorithm, Path path) { diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/util/FileUtil.java b/ocfl-java-core/src/main/java/io/ocfl/core/util/FileUtil.java index 93486576..85581359 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/util/FileUtil.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/util/FileUtil.java @@ -24,10 +24,10 @@ package io.ocfl.core.util; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.OcflOption; import io.ocfl.api.exception.OcflIOException; import io.ocfl.api.exception.OcflNoSuchFileException; -import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.util.Enforce; import java.io.FileNotFoundException; import java.io.IOException; @@ -69,7 +69,7 @@ private FileUtil() {} * @return the path to the new directory */ public static Path createObjectTempDir(Path parent, String objectId) { - var digest = DigestUtil.computeDigestHex(DigestAlgorithm.md5, objectId); + var digest = DigestUtil.computeDigestHex(DigestAlgorithmRegistry.md5, objectId); UncheckedFiles.createDirectories(parent); return UncheckedFiles.createDirectory(parent.resolve(digest + "-" + Integer.toUnsignedString(ThreadLocalRandom.current().nextInt()))); diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/util/MultiDigestInputStream.java b/ocfl-java-core/src/main/java/io/ocfl/core/util/MultiDigestInputStream.java index a7093eb5..6616ad16 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/util/MultiDigestInputStream.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/util/MultiDigestInputStream.java @@ -24,7 +24,6 @@ package io.ocfl.core.util; -import at.favre.lib.bytes.Bytes; import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.util.Enforce; import java.io.FilterInputStream; @@ -78,8 +77,7 @@ public Map getResults() { var results = new HashMap(); streamMap.forEach((algorithm, stream) -> { - results.put( - algorithm, Bytes.wrap(stream.getMessageDigest().digest()).encodeHex()); + results.put(algorithm, algorithm.encode(stream.getMessageDigest().digest())); }); return results; diff --git a/ocfl-java-core/src/main/java/io/ocfl/core/validation/SimpleInventoryValidator.java b/ocfl-java-core/src/main/java/io/ocfl/core/validation/SimpleInventoryValidator.java index dadca22e..aa50707e 100644 --- a/ocfl-java-core/src/main/java/io/ocfl/core/validation/SimpleInventoryValidator.java +++ b/ocfl-java-core/src/main/java/io/ocfl/core/validation/SimpleInventoryValidator.java @@ -32,7 +32,7 @@ import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import static java.time.temporal.ChronoField.YEAR; -import io.ocfl.api.model.DigestAlgorithm; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.model.InventoryType; import io.ocfl.api.model.OcflVersion; import io.ocfl.api.model.ValidationCode; @@ -69,18 +69,18 @@ public class SimpleInventoryValidator { private static final VersionNum VERSION_ZERO = VersionNum.fromInt(0); private static final List ALLOWED_CONTENT_DIGESTS = - List.of(DigestAlgorithm.sha512.getOcflName(), DigestAlgorithm.sha256.getOcflName()); + List.of(DigestAlgorithmRegistry.sha512.getOcflName(), DigestAlgorithmRegistry.sha256.getOcflName()); private static final Map DIGEST_LENGTHS = Map.of( - DigestAlgorithm.md5.getOcflName(), 32, - DigestAlgorithm.sha1.getOcflName(), 40, - DigestAlgorithm.sha256.getOcflName(), 64, - DigestAlgorithm.sha512.getOcflName(), 128, - DigestAlgorithm.blake2b512.getOcflName(), 128, - DigestAlgorithm.blake2b160.getOcflName(), 40, - DigestAlgorithm.blake2b256.getOcflName(), 64, - DigestAlgorithm.blake2b384.getOcflName(), 96, - DigestAlgorithm.sha512_256.getOcflName(), 64); + DigestAlgorithmRegistry.md5.getOcflName(), 32, + DigestAlgorithmRegistry.sha1.getOcflName(), 40, + DigestAlgorithmRegistry.sha256.getOcflName(), 64, + DigestAlgorithmRegistry.sha512.getOcflName(), 128, + DigestAlgorithmRegistry.blake2b512.getOcflName(), 128, + DigestAlgorithmRegistry.blake2b160.getOcflName(), 40, + DigestAlgorithmRegistry.blake2b256.getOcflName(), 64, + DigestAlgorithmRegistry.blake2b384.getOcflName(), 96, + DigestAlgorithmRegistry.sha512_256.getOcflName(), 64); private static final DateTimeFormatter RFC3339_FORMAT = new DateTimeFormatterBuilder() .parseCaseInsensitive() @@ -166,10 +166,10 @@ public ValidationResults validateInventory( inventory.getDigestAlgorithm()); } else { results.addIssue(isTrue( - DigestAlgorithm.sha512.getOcflName().equals(inventory.getDigestAlgorithm()), + DigestAlgorithmRegistry.sha512.getOcflName().equals(inventory.getDigestAlgorithm()), ValidationCode.W004, "Inventory digest algorithm should be %s in %s. Found: %s", - DigestAlgorithm.sha512.getOcflName(), + DigestAlgorithmRegistry.sha512.getOcflName(), inventoryPath, inventory.getDigestAlgorithm())); } diff --git a/ocfl-java-core/src/test/java/io/ocfl/core/extension/storage/layout/HashedNTupleIdEncapsulationLayoutExtensionTest.java b/ocfl-java-core/src/test/java/io/ocfl/core/extension/storage/layout/HashedNTupleIdEncapsulationLayoutExtensionTest.java index 43168e29..5d120767 100644 --- a/ocfl-java-core/src/test/java/io/ocfl/core/extension/storage/layout/HashedNTupleIdEncapsulationLayoutExtensionTest.java +++ b/ocfl-java-core/src/test/java/io/ocfl/core/extension/storage/layout/HashedNTupleIdEncapsulationLayoutExtensionTest.java @@ -27,8 +27,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.exception.OcflExtensionException; -import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.core.extension.storage.layout.config.HashedNTupleIdEncapsulationLayoutConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -70,7 +70,7 @@ public void shouldMapIdWithFlatConfig() { @Test public void shouldMapIdWithDifferentAlgorithm() { - ext.init(new HashedNTupleIdEncapsulationLayoutConfig().setDigestAlgorithm(DigestAlgorithm.md5)); + ext.init(new HashedNTupleIdEncapsulationLayoutConfig().setDigestAlgorithm(DigestAlgorithmRegistry.md5)); assertMapping("609/ea7/968/http%3a%2f%2flibrary%2ewisc%2eedu%2f123"); } diff --git a/ocfl-java-core/src/test/java/io/ocfl/core/extension/storage/layout/HashedNTupleLayoutExtensionTest.java b/ocfl-java-core/src/test/java/io/ocfl/core/extension/storage/layout/HashedNTupleLayoutExtensionTest.java index 35767b55..c47a2677 100644 --- a/ocfl-java-core/src/test/java/io/ocfl/core/extension/storage/layout/HashedNTupleLayoutExtensionTest.java +++ b/ocfl-java-core/src/test/java/io/ocfl/core/extension/storage/layout/HashedNTupleLayoutExtensionTest.java @@ -27,8 +27,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.exception.OcflExtensionException; -import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.core.extension.storage.layout.config.HashedNTupleLayoutConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -79,7 +79,7 @@ public void shouldMapIdWithFlatConfig() { @Test public void shouldMapIdWithDifferentAlgorithm() { - ext.init(new HashedNTupleLayoutConfig().setDigestAlgorithm(DigestAlgorithm.md5)); + ext.init(new HashedNTupleLayoutConfig().setDigestAlgorithm(DigestAlgorithmRegistry.md5)); assertMapping("609/ea7/968/609ea7968f37d4d61aa0e0df7458f6b1"); } diff --git a/ocfl-java-core/src/test/java/io/ocfl/core/inventory/InventoryMapperTest.java b/ocfl-java-core/src/test/java/io/ocfl/core/inventory/InventoryMapperTest.java index 4ef1a6ee..9760880a 100644 --- a/ocfl-java-core/src/test/java/io/ocfl/core/inventory/InventoryMapperTest.java +++ b/ocfl-java-core/src/test/java/io/ocfl/core/inventory/InventoryMapperTest.java @@ -5,7 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import io.ocfl.api.model.DigestAlgorithm; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.core.model.Inventory; import io.ocfl.core.model.RevisionNum; import io.ocfl.core.test.ITestHelper; @@ -33,7 +33,8 @@ public void shouldRoundTripInventory() throws IOException { var objectRoot = "path/to/obj1"; var digest = "bd9b8dff3b9b3debe2f5f88023f85bb501603711861b7e3da093fc970e149a0972797b3e03080e43a75974b51420b269dc87cd7bd64836f676ef06844b2ef345"; - var inventory = mapper.read(objectRoot, DigestAlgorithm.sha512, new ByteArrayInputStream(original.getBytes())); + var inventory = + mapper.read(objectRoot, DigestAlgorithmRegistry.sha512, new ByteArrayInputStream(original.getBytes())); assertFalse(inventory.hasMutableHead()); assertNull(inventory.getRevisionNum()); var output = writeInventoryToString(inventory); @@ -50,7 +51,7 @@ public void shouldRoundTripMutableHeadInventory() throws IOException { "bd9b8dff3b9b3debe2f5f88023f85bb501603711861b7e3da093fc970e149a0972797b3e03080e43a75974b51420b269dc87cd7bd64836f676ef06844b2ef345"; var revision = RevisionNum.fromString("r2"); var inventory = mapper.readMutableHead( - objectRoot, revision, DigestAlgorithm.sha512, new ByteArrayInputStream(original.getBytes())); + objectRoot, revision, DigestAlgorithmRegistry.sha512, new ByteArrayInputStream(original.getBytes())); assertTrue(inventory.hasMutableHead()); assertEquals(revision, inventory.getRevisionNum()); var output = writeInventoryToString(inventory); diff --git a/ocfl-java-core/src/test/java/io/ocfl/core/inventory/InventoryUpdaterTest.java b/ocfl-java-core/src/test/java/io/ocfl/core/inventory/InventoryUpdaterTest.java index e34f1bb3..8a8a7de8 100644 --- a/ocfl-java-core/src/test/java/io/ocfl/core/inventory/InventoryUpdaterTest.java +++ b/ocfl-java-core/src/test/java/io/ocfl/core/inventory/InventoryUpdaterTest.java @@ -7,13 +7,13 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.OcflConfig; import io.ocfl.api.OcflConstants; import io.ocfl.api.OcflOption; import io.ocfl.api.exception.OcflInputException; import io.ocfl.api.exception.OverwriteException; import io.ocfl.api.exception.PathConstraintException; -import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.model.VersionNum; import io.ocfl.core.model.Inventory; import io.ocfl.core.model.Version; @@ -270,46 +270,46 @@ public void shouldRejectInvalidLogicalPaths() { public void shouldAddFixityWhenFileInVersionAndNotDefaultAlgorithm() { var updater = builder.buildCopyState(inventory); - updater.addFixity("file1p", DigestAlgorithm.md5, "md5_1"); + updater.addFixity("file1p", DigestAlgorithmRegistry.md5, "md5_1"); - assertEquals("md5_1", updater.getFixityDigest("file1p", DigestAlgorithm.md5)); + assertEquals("md5_1", updater.getFixityDigest("file1p", DigestAlgorithmRegistry.md5)); } @Test public void shouldReturnNothingWhenWrongAlgorithm() { var updater = builder.buildCopyState(inventory); - updater.addFixity("file1p", DigestAlgorithm.md5, "md5_1"); + updater.addFixity("file1p", DigestAlgorithmRegistry.md5, "md5_1"); - assertNull(updater.getFixityDigest("file1p", DigestAlgorithm.sha1)); + assertNull(updater.getFixityDigest("file1p", DigestAlgorithmRegistry.sha1)); } @Test public void shouldNotAddFixityWhenDefaultAlgorithm() { var updater = builder.buildCopyState(inventory); - updater.addFixity("file1p", DigestAlgorithm.sha512, "sha512_1"); + updater.addFixity("file1p", DigestAlgorithmRegistry.sha512, "sha512_1"); - assertEquals("file1", updater.getFixityDigest("file1p", DigestAlgorithm.sha512)); + assertEquals("file1", updater.getFixityDigest("file1p", DigestAlgorithmRegistry.sha512)); } @Test public void shouldNotAddFixityWhenFileNotInState() { var updater = builder.buildCopyState(inventory); - updater.addFixity("file2p", DigestAlgorithm.md5, "md5_1"); + updater.addFixity("file2p", DigestAlgorithmRegistry.md5, "md5_1"); - assertNull(updater.getFixityDigest("file2p", DigestAlgorithm.md5)); + assertNull(updater.getFixityDigest("file2p", DigestAlgorithmRegistry.md5)); } @Test public void shouldClearFixity() { var updater = builder.buildCopyState(inventory); - updater.addFixity("file1p", DigestAlgorithm.md5, "md5_1"); + updater.addFixity("file1p", DigestAlgorithmRegistry.md5, "md5_1"); updater.clearFixity(); - assertNull(updater.getFixityDigest("file1p", DigestAlgorithm.md5)); + assertNull(updater.getFixityDigest("file1p", DigestAlgorithmRegistry.md5)); } @Test diff --git a/ocfl-java-core/src/test/java/io/ocfl/core/inventory/MutableHeadInventoryCommitterTest.java b/ocfl-java-core/src/test/java/io/ocfl/core/inventory/MutableHeadInventoryCommitterTest.java index b74f4c3b..6ccb9d19 100644 --- a/ocfl-java-core/src/test/java/io/ocfl/core/inventory/MutableHeadInventoryCommitterTest.java +++ b/ocfl-java-core/src/test/java/io/ocfl/core/inventory/MutableHeadInventoryCommitterTest.java @@ -5,9 +5,9 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.OcflConfig; import io.ocfl.api.OcflConstants; -import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.model.OcflVersion; import io.ocfl.api.model.VersionInfo; import io.ocfl.api.model.VersionNum; @@ -36,8 +36,8 @@ public void shouldRewriteInventoryWhenHasMutableContents() { .addFileToManifest("f3", "v2/content/file3") .addFileToManifest("f4", mutableContentPath("r1/file4")) .addFileToManifest("f5", mutableContentPath("r3/file5")) - .addFixityForFile("v1/content/file1", DigestAlgorithm.md5, "md5_1") - .addFixityForFile(mutableContentPath("r1/file4"), DigestAlgorithm.md5, "md5_4") + .addFixityForFile("v1/content/file1", DigestAlgorithmRegistry.md5, "md5_1") + .addFixityForFile(mutableContentPath("r1/file4"), DigestAlgorithmRegistry.md5, "md5_4") .addHeadVersion(Version.builder() .addFile("f1", "file1") .addFile("f2", "file2") @@ -77,7 +77,7 @@ public void shouldRewriteInventoryWhenHasMutableContents() { var fixity = newInventory.getFixityForContentPath("v3/content/r1/file4"); assertEquals(1, fixity.size()); - assertEquals("md5_4", fixity.get(DigestAlgorithm.md5)); + assertEquals("md5_4", fixity.get(DigestAlgorithmRegistry.md5)); var version = newInventory.getHeadVersion(); assertSame(now, version.getCreated()); diff --git a/ocfl-java-core/src/test/java/io/ocfl/core/model/InventoryBuilderTest.java b/ocfl-java-core/src/test/java/io/ocfl/core/model/InventoryBuilderTest.java index 2f0ecae5..806b89ed 100644 --- a/ocfl-java-core/src/test/java/io/ocfl/core/model/InventoryBuilderTest.java +++ b/ocfl-java-core/src/test/java/io/ocfl/core/model/InventoryBuilderTest.java @@ -10,6 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.exception.OcflInputException; import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.model.InventoryType; @@ -31,7 +32,7 @@ public void setup() { this.builder = Inventory.builder() .id("id") .type(InventoryType.OCFL_1_0) - .digestAlgorithm(DigestAlgorithm.sha512) + .digestAlgorithm(DigestAlgorithmRegistry.sha512) .head(VersionNum.fromString("v1")) .objectRootPath("root"); @@ -101,8 +102,8 @@ public void shouldRemoveByPath() { public void shouldAddFixityForFileInManifest() { builder.addFileToManifest("abc", "path"); - builder.addFixityForFile("path", DigestAlgorithm.md5, "md5_123"); - builder.addFixityForFile("path", DigestAlgorithm.sha1, "sha1_123"); + builder.addFixityForFile("path", DigestAlgorithmRegistry.md5, "md5_123"); + builder.addFixityForFile("path", DigestAlgorithmRegistry.sha1, "sha1_123"); var inventory = builder.build(); @@ -110,15 +111,15 @@ public void shouldAddFixityForFileInManifest() { inventory, "path", Map.of( - DigestAlgorithm.md5, "md5_123", - DigestAlgorithm.sha1, "sha1_123")); + DigestAlgorithmRegistry.md5, "md5_123", + DigestAlgorithmRegistry.sha1, "sha1_123")); } @Test public void shouldNotAddFixityWhenFileNotInManifest() { assertThat( assertThrows(OcflInputException.class, () -> { - builder.addFixityForFile("path", DigestAlgorithm.md5, "md5_123"); + builder.addFixityForFile("path", DigestAlgorithmRegistry.md5, "md5_123"); }) .getMessage(), Matchers.containsString("Cannot add fixity information for")); @@ -128,8 +129,8 @@ public void shouldNotAddFixityWhenFileNotInManifest() { public void shouldRemoveFixityWhenFileRemovedFromManifest() { builder.addFileToManifest("abc", "path"); - builder.addFixityForFile("path", DigestAlgorithm.md5, "md5_123"); - builder.addFixityForFile("path", DigestAlgorithm.sha1, "sha1_123"); + builder.addFixityForFile("path", DigestAlgorithmRegistry.md5, "md5_123"); + builder.addFixityForFile("path", DigestAlgorithmRegistry.sha1, "sha1_123"); builder.removeFileId("abc"); @@ -191,16 +192,16 @@ public void shouldAddHeadVersionWhenMutableAndV1R3() { public void shouldClearFixity() { builder.addFileToManifest("1", "path") .addFileToManifest("2", "path2") - .addFixityForFile("path", DigestAlgorithm.md5, "md5_1") - .addFixityForFile("path2", DigestAlgorithm.md5, "md5_2"); + .addFixityForFile("path", DigestAlgorithmRegistry.md5, "md5_1") + .addFixityForFile("path2", DigestAlgorithmRegistry.md5, "md5_2"); - assertEquals("md5_1", builder.getFileFixity("1", DigestAlgorithm.md5)); - assertEquals("md5_2", builder.getFileFixity("2", DigestAlgorithm.md5)); + assertEquals("md5_1", builder.getFileFixity("1", DigestAlgorithmRegistry.md5)); + assertEquals("md5_2", builder.getFileFixity("2", DigestAlgorithmRegistry.md5)); builder.clearFixity(); - assertNull(builder.getFileFixity("1", DigestAlgorithm.md5)); - assertNull(builder.getFileFixity("2", DigestAlgorithm.md5)); + assertNull(builder.getFileFixity("1", DigestAlgorithmRegistry.md5)); + assertNull(builder.getFileFixity("2", DigestAlgorithmRegistry.md5)); } private void assertFixity(Inventory inventory, String contentPath, Map expected) { diff --git a/ocfl-java-core/src/test/java/io/ocfl/core/storage/filesystem/FileSystemOcflStorageTest.java b/ocfl-java-core/src/test/java/io/ocfl/core/storage/filesystem/FileSystemOcflStorageTest.java index 1b230720..9636ad93 100644 --- a/ocfl-java-core/src/test/java/io/ocfl/core/storage/filesystem/FileSystemOcflStorageTest.java +++ b/ocfl-java-core/src/test/java/io/ocfl/core/storage/filesystem/FileSystemOcflStorageTest.java @@ -5,10 +5,10 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.jupiter.api.Assertions.assertThrows; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.OcflConstants; import io.ocfl.api.exception.NotFoundException; import io.ocfl.api.exception.OcflStateException; -import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.model.VersionNum; import io.ocfl.core.extension.ExtensionSupportEvaluator; import io.ocfl.core.extension.OcflExtensionConfig; @@ -112,7 +112,7 @@ public void shouldReturnInventoryBytesWhenExists() { Assertions.assertEquals( "c15f51c96fafe599dd056c1782fce5e8d6a0461017260ec5bc751d12821e2a7c2344048fc32312d57fdbdd67" + "ec32e238a5f68e5127a762dd866e77fcddbaa3ce", - DigestUtil.computeDigestHex(DigestAlgorithm.sha512, bytes)); + DigestUtil.computeDigestHex(DigestAlgorithmRegistry.sha512, bytes)); } @Test @@ -127,7 +127,7 @@ private InventoryBuilder inventoryBuilder() { return Inventory.builder() .id("o1") .type(OcflConstants.DEFAULT_OCFL_VERSION.getInventoryType()) - .digestAlgorithm(DigestAlgorithm.sha512) + .digestAlgorithm(DigestAlgorithmRegistry.sha512) .objectRootPath(FileUtil.pathToStringStandardSeparator(repoDir.resolve("o1"))); } diff --git a/ocfl-java-core/src/test/java/io/ocfl/core/util/ResponseMapperTest.java b/ocfl-java-core/src/test/java/io/ocfl/core/util/ResponseMapperTest.java index e3569715..65342a8a 100644 --- a/ocfl-java-core/src/test/java/io/ocfl/core/util/ResponseMapperTest.java +++ b/ocfl-java-core/src/test/java/io/ocfl/core/util/ResponseMapperTest.java @@ -2,9 +2,9 @@ import static org.assertj.core.api.Assertions.assertThat; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.OcflConfig; import io.ocfl.api.OcflConstants; -import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.model.FileChange; import io.ocfl.api.model.FileChangeType; import io.ocfl.api.model.ObjectVersionId; @@ -60,7 +60,7 @@ public void shouldIncludeSingleChangeWhenFileAddedAndNotUpdated() { .setPath("f1") .setStorageRelativePath("o1/v1/content/f1") .setVersionInfo(new VersionInfo()) - .setFixity(Map.of(DigestAlgorithm.sha512, "i1"))); + .setFixity(Map.of(DigestAlgorithmRegistry.sha512, "i1"))); } @Test @@ -101,7 +101,7 @@ public void shouldIncludeRemoveChangeWhenFileAddedAndRemoved() { .setPath("f1") .setStorageRelativePath("o1/v1/content/f1") .setVersionInfo(new VersionInfo()) - .setFixity(Map.of(DigestAlgorithm.sha512, "i1")), + .setFixity(Map.of(DigestAlgorithmRegistry.sha512, "i1")), new FileChange() .setChangeType(FileChangeType.REMOVE) .setObjectVersionId(ObjectVersionId.version("o1", "v3")) @@ -114,7 +114,7 @@ public void shouldIncludeRemoveChangeWhenFileAddedAndRemoved() { .setPath("f1") .setStorageRelativePath("o1/v1/content/f1") .setVersionInfo(new VersionInfo()) - .setFixity(Map.of(DigestAlgorithm.sha512, "i1"))); + .setFixity(Map.of(DigestAlgorithmRegistry.sha512, "i1"))); } @Test @@ -149,21 +149,21 @@ public void shouldIncludeMultipleUpdatesWhenContentChangs() { .setPath("f1") .setStorageRelativePath("o1/v1/content/f1") .setVersionInfo(new VersionInfo()) - .setFixity(Map.of(DigestAlgorithm.sha512, "i1")), + .setFixity(Map.of(DigestAlgorithmRegistry.sha512, "i1")), new FileChange() .setChangeType(FileChangeType.UPDATE) .setObjectVersionId(ObjectVersionId.version("o1", "v2")) .setPath("f1") .setStorageRelativePath("o1/v2/content/f1") .setVersionInfo(new VersionInfo()) - .setFixity(Map.of(DigestAlgorithm.sha512, "i2")), + .setFixity(Map.of(DigestAlgorithmRegistry.sha512, "i2")), new FileChange() .setChangeType(FileChangeType.UPDATE) .setObjectVersionId(ObjectVersionId.version("o1", "v3")) .setPath("f1") .setStorageRelativePath("o1/v3/content/f1") .setVersionInfo(new VersionInfo()) - .setFixity(Map.of(DigestAlgorithm.sha512, "i3"))); + .setFixity(Map.of(DigestAlgorithmRegistry.sha512, "i3"))); } private void assertFileChanges(List actual, FileChange... expected) { diff --git a/ocfl-java-core/src/test/java/io/ocfl/core/validation/InventoryValidatorTest.java b/ocfl-java-core/src/test/java/io/ocfl/core/validation/InventoryValidatorTest.java index 1d7ed59b..067e9993 100644 --- a/ocfl-java-core/src/test/java/io/ocfl/core/validation/InventoryValidatorTest.java +++ b/ocfl-java-core/src/test/java/io/ocfl/core/validation/InventoryValidatorTest.java @@ -2,10 +2,10 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.OcflConfig; import io.ocfl.api.OcflConstants; import io.ocfl.api.exception.InvalidInventoryException; -import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.api.model.VersionNum; import io.ocfl.core.model.Inventory; import io.ocfl.core.model.InventoryBuilder; @@ -271,7 +271,7 @@ public void failWhenAlgorithmsDifferent() { .build(); var previousInventory = defaultBuilder() - .digestAlgorithm(DigestAlgorithm.sha256) + .digestAlgorithm(DigestAlgorithmRegistry.sha256) .addHeadVersion(Version.builder() .created(OffsetDateTime.now()) .addFile("abc123", "file1") diff --git a/ocfl-java-core/src/test/java/io/ocfl/core/validation/SimpleInventoryParserTest.java b/ocfl-java-core/src/test/java/io/ocfl/core/validation/SimpleInventoryParserTest.java index 2b9bb69d..d5ddcffd 100644 --- a/ocfl-java-core/src/test/java/io/ocfl/core/validation/SimpleInventoryParserTest.java +++ b/ocfl-java-core/src/test/java/io/ocfl/core/validation/SimpleInventoryParserTest.java @@ -7,7 +7,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import io.ocfl.api.model.DigestAlgorithm; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.model.InventoryType; import io.ocfl.api.model.ValidationCode; import java.io.ByteArrayInputStream; @@ -172,7 +172,7 @@ private Map inventoryStub() { inv.put(ID_KEY, "id"); inv.put(TYPE_KEY, InventoryType.OCFL_1_0.getId()); - inv.put(ALGO_KEY, DigestAlgorithm.sha512.getOcflName()); + inv.put(ALGO_KEY, DigestAlgorithmRegistry.sha512.getOcflName()); inv.put(HEAD_KEY, "v1"); inv.put(MANIFEST_KEY, new HashMap()); diff --git a/ocfl-java-itest/src/test/java/io/ocfl/itest/ITestHelper.java b/ocfl-java-itest/src/test/java/io/ocfl/itest/ITestHelper.java index 3f2a23df..3f2ea187 100644 --- a/ocfl-java-itest/src/test/java/io/ocfl/itest/ITestHelper.java +++ b/ocfl-java-itest/src/test/java/io/ocfl/itest/ITestHelper.java @@ -7,8 +7,8 @@ import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.databind.ObjectMapper; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.OcflRepository; -import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.core.DefaultOcflRepository; import io.ocfl.core.inventory.InventoryMapper; import io.ocfl.core.util.DigestUtil; @@ -89,7 +89,7 @@ public static List listAllPaths(Path root) { } public static String computeDigest(Path path) { - return DigestUtil.computeDigestHex(DigestAlgorithm.md5, path); + return DigestUtil.computeDigestHex(DigestAlgorithmRegistry.md5, path); } public static String comparingMessage(Object o1, Object o2) { diff --git a/ocfl-java-itest/src/test/java/io/ocfl/itest/OcflITest.java b/ocfl-java-itest/src/test/java/io/ocfl/itest/OcflITest.java index 3c6bb984..05a1e0de 100644 --- a/ocfl-java-itest/src/test/java/io/ocfl/itest/OcflITest.java +++ b/ocfl-java-itest/src/test/java/io/ocfl/itest/OcflITest.java @@ -24,6 +24,7 @@ import static org.junit.jupiter.api.Assertions.fail; import com.github.benmanes.caffeine.cache.Caffeine; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.OcflConfig; import io.ocfl.api.OcflOption; import io.ocfl.api.OcflRepository; @@ -149,20 +150,20 @@ public void writeInputStreamWithFixityCheck() throws IOException { var file1Sha512 = "6407d5ecc067dad1a2a3c75d088ecdab97d4df5a580a3bbc1b190ad988cea529b92eab11131fd2f5c0b40fa5891eec979e7e5e96b6bed38e6dddde7a20722345"; var inputStream1 = new FixityCheckInputStream( - ITestHelper.streamString(file1Contents), DigestAlgorithm.sha512, file1Sha512); + ITestHelper.streamString(file1Contents), DigestAlgorithmRegistry.sha512, file1Sha512); var file2Contents = "... contents of second file ..."; var file2Sha512 = "d173736e7984e4439cab8d0bd6665e8f9a3aefc4d518a5ed5a3a46e05da40fa5803ac5dc52c9b17d302e12525619a9b6076f33a0c80b558bff051812800e0875"; var inputStream2 = new FixityCheckInputStream( - ITestHelper.streamString(file2Contents), DigestAlgorithm.sha512, file2Sha512); + ITestHelper.streamString(file2Contents), DigestAlgorithmRegistry.sha512, file2Sha512); inputStream2.enableFixityCheck(false); var file3Contents = "... contents of third file ..."; var file3Sha512 = "f280e67f4142469ac514dd7ad366c6ed629e10b30f6f637e6de36b861c44ba5753d8fe8d589b9b23310df9e9d564a20a06d5f4637bd9f8e66ab628c7cce33e72"; var inputStream3 = new DigestInputStream( - ITestHelper.streamString(file3Contents), DigestAlgorithm.sha512.getMessageDigest()); + ITestHelper.streamString(file3Contents), DigestAlgorithmRegistry.sha512.getMessageDigest()); repo.updateObject(ObjectVersionId.head(objectId), new VersionInfo(), updater -> { updater.writeFile(inputStream1, "file1"); @@ -175,17 +176,17 @@ public void writeInputStreamWithFixityCheck() throws IOException { try (var stream = object.getFile("file1").getStream()) { assertEquals(file1Contents, new String(stream.readAllBytes())); } - assertEquals(file1Sha512, object.getFile("file1").getFixity().get(DigestAlgorithm.sha512)); + assertEquals(file1Sha512, object.getFile("file1").getFixity().get(DigestAlgorithmRegistry.sha512)); try (var stream = object.getFile("file2").getStream()) { assertEquals(file2Contents, new String(stream.readAllBytes())); } - assertEquals(file2Sha512, object.getFile("file2").getFixity().get(DigestAlgorithm.sha512)); + assertEquals(file2Sha512, object.getFile("file2").getFixity().get(DigestAlgorithmRegistry.sha512)); try (var stream = object.getFile("file3").getStream()) { assertEquals(file3Contents, new String(stream.readAllBytes())); } - assertEquals(file3Sha512, object.getFile("file3").getFixity().get(DigestAlgorithm.sha512)); + assertEquals(file3Sha512, object.getFile("file3").getFixity().get(DigestAlgorithmRegistry.sha512)); } @Test @@ -391,28 +392,28 @@ public void describeObject() { repo.updateObject(ObjectVersionId.head(objectId), defaultVersionInfo.setMessage("1"), updater -> { updater.addPath(ITestHelper.sourceObjectPath(objectId, "v1")) - .addFileFixity("file1", DigestAlgorithm.md5, "95efdf0764d92207b4698025f2518456") - .addFileFixity("file2", DigestAlgorithm.md5, "55c1824fcae2b1b51cef5037405fc1ad"); + .addFileFixity("file1", DigestAlgorithmRegistry.md5, "95efdf0764d92207b4698025f2518456") + .addFileFixity("file2", DigestAlgorithmRegistry.md5, "55c1824fcae2b1b51cef5037405fc1ad"); }); repo.updateObject(ObjectVersionId.head(objectId), defaultVersionInfo.setMessage("2"), updater -> { updater.clearVersionState() .addPath(ITestHelper.sourceObjectPath(objectId, "v2")) - .addFileFixity("file1", DigestAlgorithm.md5, "a0a8bfbf51b81caf7aa5be00f5e26669") - .addFileFixity("file2", DigestAlgorithm.md5, "55c1824fcae2b1b51cef5037405fc1ad") - .addFileFixity("dir1/file3", DigestAlgorithm.md5, "72b6193fe19ec99c692eba5c798e6bdf"); + .addFileFixity("file1", DigestAlgorithmRegistry.md5, "a0a8bfbf51b81caf7aa5be00f5e26669") + .addFileFixity("file2", DigestAlgorithmRegistry.md5, "55c1824fcae2b1b51cef5037405fc1ad") + .addFileFixity("dir1/file3", DigestAlgorithmRegistry.md5, "72b6193fe19ec99c692eba5c798e6bdf"); }); repo.updateObject(ObjectVersionId.head(objectId), defaultVersionInfo.setMessage("3"), updater -> { updater.clearVersionState() .addPath(ITestHelper.sourceObjectPath(objectId, "v3")) - .addFileFixity("file2", DigestAlgorithm.md5, "55c1824fcae2b1b51cef5037405fc1ad") - .addFileFixity("file4", DigestAlgorithm.md5, "a0a8bfbf51b81caf7aa5be00f5e26669"); + .addFileFixity("file2", DigestAlgorithmRegistry.md5, "55c1824fcae2b1b51cef5037405fc1ad") + .addFileFixity("file4", DigestAlgorithmRegistry.md5, "a0a8bfbf51b81caf7aa5be00f5e26669"); }); var objectDetails = repo.describeObject(objectId); assertEquals(objectId, objectDetails.getId()); assertEquals(VersionNum.fromString("v3"), objectDetails.getHeadVersionNum()); - assertEquals(DigestAlgorithm.sha512, objectDetails.getDigestAlgorithm()); + assertEquals(DigestAlgorithmRegistry.sha512, objectDetails.getDigestAlgorithm()); assertEquals(3, objectDetails.getVersionMap().size()); assertThat( @@ -425,16 +426,16 @@ public void describeObject() { "file1", O1_PATH + "/v1/content/file1", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "96a26e7629b55187f9ba3edc4acc940495d582093b8a88cb1f0303cf3399fe6b1f5283d76dfd561fc401a0cdf878c5aad9f2d6e7e2d9ceee678757bb5d95c39e", - DigestAlgorithm.md5, "95efdf0764d92207b4698025f2518456")), + DigestAlgorithmRegistry.md5, "95efdf0764d92207b4698025f2518456")), fileDetails( "file2", O1_PATH + "/v1/content/file2", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6", - DigestAlgorithm.md5, "55c1824fcae2b1b51cef5037405fc1ad")))); + DigestAlgorithmRegistry.md5, "55c1824fcae2b1b51cef5037405fc1ad")))); assertThat( objectDetails.getVersion(VersionNum.fromString("v2")), @@ -446,23 +447,23 @@ public void describeObject() { "file1", O1_PATH + "/v2/content/file1", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "aff2318b35d3fbc05670b834b9770fd418e4e1b4adc502e6875d598ab3072ca76667121dac04b694c47c71be80f6d259316c7bd0e19d40827cb3f27ee03aa2fc", - DigestAlgorithm.md5, "a0a8bfbf51b81caf7aa5be00f5e26669")), + DigestAlgorithmRegistry.md5, "a0a8bfbf51b81caf7aa5be00f5e26669")), fileDetails( "file2", O1_PATH + "/v1/content/file2", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6", - DigestAlgorithm.md5, "55c1824fcae2b1b51cef5037405fc1ad")), + DigestAlgorithmRegistry.md5, "55c1824fcae2b1b51cef5037405fc1ad")), fileDetails( "dir1/file3", O1_PATH + "/v2/content/dir1/file3", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "cb6f4f7b3d3eef05d3d0327335071d14c120e065fa43364690fea47d456e146dd334d78d35f73926067d0bf46f122ea026508954b71e8e25c351ff75c993c2b2", - DigestAlgorithm.md5, "72b6193fe19ec99c692eba5c798e6bdf")))); + DigestAlgorithmRegistry.md5, "72b6193fe19ec99c692eba5c798e6bdf")))); assertThat( objectDetails.getVersion(VersionNum.fromString("v3")), @@ -474,16 +475,16 @@ public void describeObject() { "file2", O1_PATH + "/v1/content/file2", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6", - DigestAlgorithm.md5, "55c1824fcae2b1b51cef5037405fc1ad")), + DigestAlgorithmRegistry.md5, "55c1824fcae2b1b51cef5037405fc1ad")), fileDetails( "file4", O1_PATH + "/v2/content/file1", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "aff2318b35d3fbc05670b834b9770fd418e4e1b4adc502e6875d598ab3072ca76667121dac04b694c47c71be80f6d259316c7bd0e19d40827cb3f27ee03aa2fc", - DigestAlgorithm.md5, "a0a8bfbf51b81caf7aa5be00f5e26669")))); + DigestAlgorithmRegistry.md5, "a0a8bfbf51b81caf7aa5be00f5e26669")))); assertSame(objectDetails.getHeadVersion(), objectDetails.getVersion(VersionNum.fromString("v3"))); } @@ -499,11 +500,11 @@ public void shouldNotAddAdditionalFixityWhenDefaultAlgorithmSpecified() { updater.addPath(ITestHelper.sourceObjectPath(objectId, "v1")) .addFileFixity( "file1", - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "96a26e7629b55187f9ba3edc4acc940495d582093b8a88cb1f0303cf3399fe6b1f5283d76dfd561fc401a0cdf878c5aad9f2d6e7e2d9ceee678757bb5d95c39e") .addFileFixity( "file2", - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6"); }); repo.updateObject(ObjectVersionId.head(objectId), defaultVersionInfo.setMessage("2"), updater -> { @@ -511,15 +512,15 @@ public void shouldNotAddAdditionalFixityWhenDefaultAlgorithmSpecified() { .addPath(ITestHelper.sourceObjectPath(objectId, "v2")) .addFileFixity( "file1", - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "aff2318b35d3fbc05670b834b9770fd418e4e1b4adc502e6875d598ab3072ca76667121dac04b694c47c71be80f6d259316c7bd0e19d40827cb3f27ee03aa2fc") .addFileFixity( "file2", - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6") .addFileFixity( "dir1/file3", - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "cb6f4f7b3d3eef05d3d0327335071d14c120e065fa43364690fea47d456e146dd334d78d35f73926067d0bf46f122ea026508954b71e8e25c351ff75c993c2b2"); }); repo.updateObject(ObjectVersionId.head(objectId), defaultVersionInfo.setMessage("3"), updater -> { @@ -527,11 +528,11 @@ public void shouldNotAddAdditionalFixityWhenDefaultAlgorithmSpecified() { .addPath(ITestHelper.sourceObjectPath(objectId, "v3")) .addFileFixity( "file2", - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6") .addFileFixity( "file4", - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "aff2318b35d3fbc05670b834b9770fd418e4e1b4adc502e6875d598ab3072ca76667121dac04b694c47c71be80f6d259316c7bd0e19d40827cb3f27ee03aa2fc"); }); @@ -551,13 +552,13 @@ public void shouldNotAddAdditionalFixityWhenDefaultAlgorithmSpecified() { "file1", O1_PATH + "/v1/content/file1", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "96a26e7629b55187f9ba3edc4acc940495d582093b8a88cb1f0303cf3399fe6b1f5283d76dfd561fc401a0cdf878c5aad9f2d6e7e2d9ceee678757bb5d95c39e")), fileDetails( "file2", O1_PATH + "/v1/content/file2", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6")))); assertThat( @@ -570,19 +571,19 @@ public void shouldNotAddAdditionalFixityWhenDefaultAlgorithmSpecified() { "file1", O1_PATH + "/v2/content/file1", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "aff2318b35d3fbc05670b834b9770fd418e4e1b4adc502e6875d598ab3072ca76667121dac04b694c47c71be80f6d259316c7bd0e19d40827cb3f27ee03aa2fc")), fileDetails( "file2", O1_PATH + "/v1/content/file2", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6")), fileDetails( "dir1/file3", O1_PATH + "/v2/content/dir1/file3", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "cb6f4f7b3d3eef05d3d0327335071d14c120e065fa43364690fea47d456e146dd334d78d35f73926067d0bf46f122ea026508954b71e8e25c351ff75c993c2b2")))); assertThat( @@ -595,13 +596,13 @@ public void shouldNotAddAdditionalFixityWhenDefaultAlgorithmSpecified() { "file2", O1_PATH + "/v1/content/file2", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6")), fileDetails( "file4", O1_PATH + "/v2/content/file1", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "aff2318b35d3fbc05670b834b9770fd418e4e1b4adc502e6875d598ab3072ca76667121dac04b694c47c71be80f6d259316c7bd0e19d40827cb3f27ee03aa2fc")))); assertSame(objectDetails.getHeadVersion(), objectDetails.getVersion(VersionNum.fromString("v3"))); @@ -617,7 +618,7 @@ public void shouldFailWhenFixityDoesNotMatch() { OcflAsserts.assertThrowsWithMessage(FixityCheckException.class, "Expected md5 digest of", () -> { repo.updateObject(ObjectVersionId.head(objectId), defaultVersionInfo.setMessage("1"), updater -> { updater.addPath(ITestHelper.sourceObjectPath(objectId, "v1")) - .addFileFixity("file1", DigestAlgorithm.md5, "bogus"); + .addFileFixity("file1", DigestAlgorithmRegistry.md5, "bogus"); }); }); } @@ -630,7 +631,9 @@ public void shouldFailFixityWhenUnknownAlgorithm() { var objectId = "o1"; OcflAsserts.assertThrowsWithMessage( - OcflInputException.class, "specified digest algorithm is not supported", () -> { + OcflInputException.class, + "The specified digest algorithm is not mapped to a Java name: DigestAlgorithm{ocflName='bogus', javaStandardName='null'}", + () -> { repo.updateObject(ObjectVersionId.head(objectId), defaultVersionInfo.setMessage("1"), updater -> { updater.addPath(ITestHelper.sourceObjectPath(objectId, "v1")) .addFileFixity("file1", DigestAlgorithm.fromOcflName("bogus"), "bogus"); @@ -653,7 +656,7 @@ public void shouldFailFixityWhenFileNotAddedInBlockAndDoesNotHaveExistingFixity( repo.updateObject(ObjectVersionId.head(objectId), defaultVersionInfo.setMessage("2"), updater -> { updater.clearVersionState() .addPath(ITestHelper.sourceObjectPath(objectId, "v2")) - .addFileFixity("file2", DigestAlgorithm.md5, "55c1824fcae2b1b51cef5037405fc1ad"); + .addFileFixity("file2", DigestAlgorithmRegistry.md5, "55c1824fcae2b1b51cef5037405fc1ad"); }); }); } @@ -679,14 +682,14 @@ public void readObjectFiles() { O2_PATH + "/v1/content/dir1/dir2/file2", "Test file 2", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6")), versionFile( "dir1/file3", O2_PATH + "/v3/content/dir1/file3", "This is a different file 3", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "6e027f3dc89e0bfd97e4c2ec6919a8fb793bdc7b5c513bea618f174beec32a66d2fc0ce19439751e2f01ae49f78c56dcfc7b49c167a751c823d09da8419a4331")))); ocflObject = repo.getObject(ObjectVersionId.version(objectId, "v1")); @@ -702,14 +705,14 @@ public void readObjectFiles() { O2_PATH + "/v1/content/dir1/dir2/file2", "Test file 2", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6")), versionFile( "file1", O2_PATH + "/v1/content/file1", "Test file 1", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "96a26e7629b55187f9ba3edc4acc940495d582093b8a88cb1f0303cf3399fe6b1f5283d76dfd561fc401a0cdf878c5aad9f2d6e7e2d9ceee678757bb5d95c39e")))); } @@ -747,7 +750,7 @@ public void changeHistory() { O1_PATH + "/v1/content/f1", versionInfo(defaultVersionInfo.getUser(), "1"), Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4dff4ea340f0a823f15d3f4f01ab62eae0e5da579ccb851f8db9dfe84c58b2b37b89903a740e1ee172da793a6e79d560e5f7f9bd058a12a280433ed6fa46510a")), fileChange( FileChangeType.REMOVE, @@ -763,7 +766,7 @@ public void changeHistory() { O1_PATH + "/v1/content/f1", versionInfo(defaultVersionInfo.getUser(), "3"), Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4dff4ea340f0a823f15d3f4f01ab62eae0e5da579ccb851f8db9dfe84c58b2b37b89903a740e1ee172da793a6e79d560e5f7f9bd058a12a280433ed6fa46510a")))); assertThat( @@ -776,7 +779,7 @@ public void changeHistory() { O1_PATH + "/v1/content/f2", versionInfo(defaultVersionInfo.getUser(), "1"), Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "40b244112641dd78dd4f93b6c9190dd46e0099194d5a44257b7efad6ef9ff4683da1eda0244448cb343aa688f5d3efd7314dafe580ac0bcbf115aeca9e8dc114")), fileChange( FileChangeType.UPDATE, @@ -785,7 +788,7 @@ public void changeHistory() { O1_PATH + "/v3/content/f2", versionInfo(defaultVersionInfo.getUser(), "3"), Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "7db70149dac5561e411a202629d06832b06b7e8dfef61086ff9e0922459fbe14a69d565cf838fd43681fdb29a698bfe377861b966d12416298997843820bfdb7")))); assertThat( @@ -798,7 +801,7 @@ public void changeHistory() { O1_PATH + "/v2/content/f3", versionInfo(defaultVersionInfo.getUser(), "2"), Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "3bafbf08882a2d10133093a1b8433f50563b93c14acd05b79028eb1d12799027241450980651994501423a66c276ae26c43b739bc65c4e16b10c3af6c202aebb")))); } @@ -849,14 +852,14 @@ public void getObjectFilesFromLazyLoadGetObject() { O2_PATH + "/v1/content/dir1/dir2/file2", "Test file 2", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6")), versionFile( "dir1/file3", O2_PATH + "/v3/content/dir1/file3", "This is a different file 3", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "6e027f3dc89e0bfd97e4c2ec6919a8fb793bdc7b5c513bea618f174beec32a66d2fc0ce19439751e2f01ae49f78c56dcfc7b49c167a751c823d09da8419a4331")))); objectVersion = repo.getObject(ObjectVersionId.version(objectId, "v1")); @@ -872,14 +875,14 @@ public void getObjectFilesFromLazyLoadGetObject() { O2_PATH + "/v1/content/dir1/dir2/file2", "Test file 2", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "4cf0ff5673ec65d9900df95502ed92b2605fc602ca20b6901652c7561b302668026095813af6adb0e663bdcdbe1f276d18bf0de254992a78573ad6574e7ae1f6")), versionFile( "file1", O2_PATH + "/v1/content/file1", "Test file 1", Map.of( - DigestAlgorithm.sha512, + DigestAlgorithmRegistry.sha512, "96a26e7629b55187f9ba3edc4acc940495d582093b8a88cb1f0303cf3399fe6b1f5283d76dfd561fc401a0cdf878c5aad9f2d6e7e2d9ceee678757bb5d95c39e")))); } @@ -899,7 +902,7 @@ public void acceptEmptyPutObjectRequests() throws IOException { } @Test - public void removeAllOfTheFilesFromAnObject() throws IOException { + public void removeAllOfTheFilesFromAnObject() { var repoName = "repo4"; var repo = defaultRepo(repoName); @@ -922,7 +925,7 @@ public void removeAllOfTheFilesFromAnObject() throws IOException { } @Test - public void rejectObjectNotFoundWhenObjectDoesNotExists() throws IOException { + public void rejectObjectNotFoundWhenObjectDoesNotExists() { var repoName = "repo4"; var repoDir = ITestHelper.expectedRepoPath(repoName); var repo = existingRepo(repoName, repoDir); @@ -933,7 +936,7 @@ public void rejectObjectNotFoundWhenObjectDoesNotExists() throws IOException { } @Test - public void rejectObjectNotFoundWhenObjectExistsButVersionDoesNot() throws IOException { + public void rejectObjectNotFoundWhenObjectExistsButVersionDoesNot() { var repoName = "repo4"; var repoDir = ITestHelper.expectedRepoPath(repoName); var repo = existingRepo(repoName, repoDir); @@ -1536,7 +1539,7 @@ public void writeInputStreamToObjectWhenHasFixityCheckAndValid() { updater.writeFile( new FixityCheckInputStream( inputStream(sourcePath.resolve("file1")), - DigestAlgorithm.md5, + DigestAlgorithmRegistry.md5, "95efdf0764d92207b4698025f2518456"), "file1"); }); @@ -1557,7 +1560,7 @@ public void failInputStreamToObjectWhenHasFixityCheckAndNotValid() { repo.updateObject(ObjectVersionId.head(objectId), defaultVersionInfo, updater -> { updater.writeFile( new FixityCheckInputStream( - inputStream(sourcePath.resolve("file1")), DigestAlgorithm.md5, "bogus"), + inputStream(sourcePath.resolve("file1")), DigestAlgorithmRegistry.md5, "bogus"), "file1"); }); }); @@ -2408,7 +2411,7 @@ public void addFileWithPrecomputedDigest() throws IOException { } @Test - public void corruptObjectWhenInvalidDigestProvided() throws IOException { + public void corruptObjectWhenInvalidDigestProvided() { var repoName = "unsafe"; var repo = defaultRepo(repoName); @@ -2436,7 +2439,7 @@ public void corruptObjectWhenInvalidDigestProvided() throws IOException { } @Test - public void failCommitWhenMissingFile() throws IOException { + public void failCommitWhenMissingFile() { var repoName = "unsafe"; var repo = defaultRepo(repoName); @@ -2474,7 +2477,7 @@ public void failCommitWhenMissingFile() throws IOException { } @Test - public void failCommitWhenHasExtraFile() throws IOException { + public void failCommitWhenHasExtraFile() { var repoName = "unsafe"; var repo = defaultRepo(repoName); @@ -2768,6 +2771,67 @@ public void upgradeExistingObjectOnWrite() { assertEquals(0, result.getErrors().size()); } + @Test + public void sizeBasedFixityShouldValidateWhenCorrect() throws IOException { + var repoName = "size-fixity-valid"; + var repo = defaultRepo(repoName); + + var objectId = "obj1"; + + var file = writeFile("asdf".repeat(99999)); + var fileSize = Files.size(file); + + repo.updateObject(ObjectVersionId.head(objectId), null, updater -> { + updater.writeFile(ITestHelper.streamString("file1"), "file1"); + updater.addFileFixity("file1", DigestAlgorithmRegistry.size, "5"); + updater.addPath(file, "file2"); + updater.addFileFixity("file2", DigestAlgorithmRegistry.size, String.valueOf(fileSize)); + }); + + assertEquals( + "5", + repo.getObject(ObjectVersionId.head(objectId)) + .getFile("file1") + .getFixity() + .get(DigestAlgorithmRegistry.size)); + assertEquals( + String.valueOf(fileSize), + repo.getObject(ObjectVersionId.head(objectId)) + .getFile("file2") + .getFixity() + .get(DigestAlgorithmRegistry.size)); + + var results = repo.validateObject(objectId, false); + + assertEquals(0, results.getErrors().size(), () -> results.getErrors().toString()); + } + + @Test + public void sizeBasedFixityShouldFailValidationWhenInvalid() { + var repoName = "size-fixity"; + var repo = defaultRepo(repoName); + + var objectId = "obj1"; + + var file = writeFile("asdf".repeat(99999)); + + OcflAsserts.assertThrowsWithMessage( + FixityCheckException.class, "Expected size digest of file1 to be 100, but was 5.", () -> { + repo.updateObject(ObjectVersionId.head(objectId), null, updater -> { + updater.writeFile(ITestHelper.streamString("file1"), "file1"); + updater.addFileFixity("file1", DigestAlgorithmRegistry.size, "100"); + }); + }); + + OcflAsserts.assertThrowsWithMessage( + FixityCheckException.class, "Expected size digest of file2 to be 100, but was 399996.", () -> { + repo.updateObject(ObjectVersionId.head(objectId), null, updater -> { + updater.addPath(file, "file2"); + updater.addFileFixity("file2", DigestAlgorithmRegistry.size, "100"); + }); + }); + } + private Path writeFile(String content) { try { return Files.writeString( diff --git a/ocfl-java-itest/src/test/java/io/ocfl/itest/StorageTest.java b/ocfl-java-itest/src/test/java/io/ocfl/itest/StorageTest.java index 168d64fb..097aa670 100644 --- a/ocfl-java-itest/src/test/java/io/ocfl/itest/StorageTest.java +++ b/ocfl-java-itest/src/test/java/io/ocfl/itest/StorageTest.java @@ -7,9 +7,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.api.exception.OcflFileAlreadyExistsException; import io.ocfl.api.exception.OcflNoSuchFileException; -import io.ocfl.api.model.DigestAlgorithm; import io.ocfl.core.storage.common.Listing; import io.ocfl.core.storage.common.Storage; import io.ocfl.core.util.FileUtil; @@ -175,7 +175,7 @@ public void readFileLazyContentWhenExists() throws IOException { var content = "something3"; file("f1.txt", content); - var retriever = storage.readLazy("f1.txt", DigestAlgorithm.md5, "f57c22367d47ee55c920465e8f17dc70"); + var retriever = storage.readLazy("f1.txt", DigestAlgorithmRegistry.md5, "f57c22367d47ee55c920465e8f17dc70"); try (var stream = retriever.retrieveFile()) { assertEquals(content, toString(stream)); @@ -186,7 +186,7 @@ public void readFileLazyContentWhenExists() throws IOException { public void doNotFailReadFileLazyContentWhenNotExists() { var content = "something3"; file("f1.txt", content); - storage.readLazy("f2.txt", DigestAlgorithm.md5, "f57c22367d47ee55c920465e8f17dc70"); + storage.readLazy("f2.txt", DigestAlgorithmRegistry.md5, "f57c22367d47ee55c920465e8f17dc70"); } @Test diff --git a/ocfl-java-itest/src/test/java/io/ocfl/itest/s3/S3ITestHelper.java b/ocfl-java-itest/src/test/java/io/ocfl/itest/s3/S3ITestHelper.java index 9a2ee11d..a526fd63 100644 --- a/ocfl-java-itest/src/test/java/io/ocfl/itest/s3/S3ITestHelper.java +++ b/ocfl-java-itest/src/test/java/io/ocfl/itest/s3/S3ITestHelper.java @@ -4,7 +4,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static software.amazon.awssdk.http.SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES; -import io.ocfl.api.model.DigestAlgorithm; +import io.ocfl.api.DigestAlgorithmRegistry; import io.ocfl.core.util.DigestUtil; import io.ocfl.core.util.FileUtil; import io.ocfl.itest.ITestHelper; @@ -136,7 +136,7 @@ private byte[] getObjectContent(String bucket, String prefix, String key) { } private String computeS3Digest(String bucket, String prefix, String key) { - return DigestUtil.computeDigestHex(DigestAlgorithm.md5, getObjectContent(bucket, prefix, key)); + return DigestUtil.computeDigestHex(DigestAlgorithmRegistry.md5, getObjectContent(bucket, prefix, key)); } public List listAllObjects(String bucket, String prefix) {