Public API¶
Not yet on Maven Central
dedup4j 0.1.0 has not been published. These coordinates are the
intended release coordinates and cannot be resolved from Maven Central
today. The Maven Central namespace is confirmed in release gate G0 and
this file is the single place it is edited.
The types dedup4j expects you to use. Everything here is in
com.edem.dedup4j.*.
Records, not beans
Every model type is an immutable record with validation in its compact
constructor. There are no setters, and an invalid instance cannot be
constructed.
Entry points¶
BlobStore¶
The upload facade. Auto-configured by the starter.
public interface BlobStore {
BlobReference store(MultipartFile file);
BlobReference store(Path path);
BlobReference store(byte[] content, String filename, String contentType);
BlobReference store(InputStream content, long sizeBytes, String filename,
String contentType, Map<String, String> metadata);
BatchStoreResult storeAll(MultipartFile[] files);
}
See Uploading & deduplication.
BlobDeduplicationService¶
Retrieval and reference counting. Auto-configured.
public interface BlobDeduplicationService {
BlobReference store(StoreBlobCommand command);
void retain(UUID assetContentId);
void release(UUID assetContentId);
BlobResource get(UUID assetContentId);
BlobLocation location(UUID assetContentId);
}
Its store takes a StoreBlobCommand directly — the lower-level entry point
BlobStore delegates to. See
Retrieval, retain & release.
Models¶
BlobReference¶
Returned by every store.
public record BlobReference(
UUID assetContentId,
ContentHash contentHash,
String contentType,
String storageProvider,
String bucketOrContainer,
String objectKey,
boolean duplicate
) {
BlobLocation location();
}
assetContentId is the handle to persist. duplicate reports whether the
bytes already existed.
ContentHash¶
Content identity is the whole triple. The algorithm is SHA-256.
BlobResource¶
Returned by get. Holds an open stream and implements AutoCloseable.
public record BlobResource(
String objectKey,
InputStream content,
long sizeBytes,
String contentType,
Map<String, String> metadata
) implements AutoCloseable {}
BlobLocation¶
Note the component is provider, while BlobReference calls the same concept
storageProvider.
Everything needed to build a presigned URL with your provider's own SDK — see why dedup4j does not issue them.
StoreBlobCommand¶
public record StoreBlobCommand(
InputStream content,
String filename,
String contentType,
long sizeBytes,
Map<String, String> metadata
) {}
Batch results¶
public sealed interface BatchStoreOutcome
permits BlobStoreSuccess, BlobStoreFailure {
int index();
String filename();
}
public record BlobStoreSuccess(int index, String filename, BlobReference reference)
implements BatchStoreOutcome {}
public record BlobStoreFailure(int index, String filename, RuntimeException failure)
implements BatchStoreOutcome {}
public record BatchStoreResult(List<BatchStoreOutcome> outcomes) {
List<BlobStoreSuccess> successes();
List<BlobStoreFailure> failures();
boolean allSucceeded();
}
Sealed, so a switch over outcomes is exhaustively checked. index() maps
back to the position in the array you submitted.
Extension points¶
Each is an auto-configured bean declared @ConditionalOnMissingBean — declare
your own and the default steps aside.
BlobStorage¶
The storage SPI. Implement it to support a provider dedup4j does not ship.
public interface BlobStorage {
StoredBlob put(PutBlobRequest request);
BlobResource get(String objectKey);
void delete(String objectKey);
boolean exists(String objectKey);
}
delete must be idempotent — an already-missing object counts as deleted.
public record PutBlobRequest(
String objectKey, InputStream content, long sizeBytes,
String contentType, String originalFilename, Map<String, String> metadata
) {}
public record StoredBlob(
String objectKey, String provider, String bucketOrContainer,
long sizeBytes, String contentType, String checksum, Instant createdAt
) {}
See custom providers.
ObjectKeyStrategy¶
Decides where content lands in the store.
The key is derived from content identity alone — the same bytes always produce the same key, which is what makes deduplication work. A strategy that introduces randomness breaks that.
ContentHasher¶
Changing this orphans existing content
Identity is the hash. Existing rows keep hashes from the old algorithm, so previously stored content will never match again and will be stored a second time.
Reconciliation¶
Not auto-configured — construct it yourself.
public interface LogicalReferenceCountSource {
Map<UUID, Long> countLogicalReferences();
}
public record ReconciliationMismatch(
UUID assetContentId,
long expectedReferenceCount,
long actualReferenceCount
) {}
public record ReconciliationReport(
long checkedContentCount,
List<ReconciliationMismatch> mismatches
) {}
LogicalReferenceCountSource is a functional interface: you tell dedup4j what
the counts should be, because it cannot enumerate your records.
Exceptions¶
All unchecked. See Troubleshooting.