Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
39efa1c
refactor(export): modularize ExportService by introducing ExporterReg…
poikilotherm Aug 7, 2026
5842090
refactor(export): make ExportService a @Stateless EJB bean
poikilotherm Aug 7, 2026
0e6d247
feat(export): introduce new ExportCache subsystem with cache key, inv…
poikilotherm Aug 7, 2026
0f92a34
refactor(export): extract file embargo expiry logic from ExportServic…
poikilotherm Aug 7, 2026
955298b
refactor(export): move caching logic from ExportService to new Storag…
poikilotherm Aug 7, 2026
b63f2f7
refactor(export): remove legacy unversioned cache logic from StorageI…
poikilotherm Aug 7, 2026
0f29e8f
feat(export): enhance ExportCacheKey with validation and convenience …
poikilotherm Aug 7, 2026
e5b23a4
refactor(export): relocate service and provider classes to `export.se…
poikilotherm Aug 7, 2026
d88b243
docs(export): add Javadoc to private helpers in StorageIOCache #11405
poikilotherm Aug 18, 2026
a3c0c42
refactor(export): move invalidators list from ExportCacheInvalidator …
poikilotherm Aug 18, 2026
0113219
style(export): rename `exporterRegistry` field to `registry` in Expor…
poikilotherm Aug 18, 2026
ee20cef
feat(export): make ExportCache instance available in service #11405
poikilotherm Aug 18, 2026
5a43762
refactor(export): make cache clearing version-aware #11405
poikilotherm Aug 18, 2026
3ba9d63
style(export): move export trigger service methods next to each other…
poikilotherm Aug 18, 2026
16c9cd7
feat(export): add prerequisite dependency verification to ExporterReg…
poikilotherm Aug 19, 2026
a0e9d8b
feat(export): cache formatRequiredBy map in ExporterRegistryBean #11405
poikilotherm Aug 19, 2026
f4d38f9
feat(export): add topological comparator to ExporterRegistryBean #11405
poikilotherm Aug 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
570 changes: 0 additions & 570 deletions src/main/java/edu/harvard/iq/dataverse/export/ExportService.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package edu.harvard.iq.dataverse.export.service;

import edu.harvard.iq.dataverse.Dataset;
import io.gdcc.spi.export.ExportException;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Optional;

/**
* Storage abstraction for cached metadata exports. Implementations own all
* knowledge about where and under which names cached exports live; the export
* pipeline only ever deals in {@link ExportCacheKey}s and streams.
*/
public sealed interface ExportCache permits StorageIOCache {

/**
* Looks up a cached export.
* @return the cached export stream, or empty if none is cached. Note: the caller is responsible for closing the stream.
* @throws IOException on actual storage failures (not on a cache miss)
*/
Optional<InputStream> read(ExportCacheKey key) throws IOException;

/**
* Produces and stores an export. The {@code writer} callback receives the output stream to write to.
* Any implementations guarantee that a partially written export is never made visible under the cache key
* (i.e., a failed write leaves either the previous entry or no entry).
*/
void write(ExportCacheKey key, ExportStreamWriter writer) throws ExportException, IOException;

/** Removes a cached export. Absence of the entry is not an error. */
void evict(ExportCacheKey key) throws IOException;

/**
* Removes all cached exports for a dataset, across all versions and formats, including legacy (pre-versioning) entries.
* Intended for publish/deaccession hooks and the admin "reexport" API.
*/
void evictAll(Dataset dataset) throws IOException;

/** Callback that renders an export into the store-provided stream. */
@FunctionalInterface
interface ExportStreamWriter {
void writeTo(OutputStream out) throws ExportException, IOException;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package edu.harvard.iq.dataverse.export.service;

/**
* Represents an abstraction for determining whether a cached export needs to be invalidated and regenerated.
* <p>
* This sealed interface is intended to enforce a controlled hierarchy of classes that implement the cache
* invalidation logic, ensuring behavior consistency across different implementations. If necessary, the contract
* may be altered to allow more dynamic discovery of invalidators.
* <p>
* If at a later point we want to enable export plugins to provide their own invalidation logic,
* this interface shall be unsealed and moved into the Exporter SPI codebase.
*/
public sealed interface ExportCacheInvalidator permits FileEmbargoExpiryInvalidator {
/** Should a cached export for this key be discarded and regenerated? */
boolean isStale(ExportCacheKey key);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package edu.harvard.iq.dataverse.export.service;

import edu.harvard.iq.dataverse.Dataset;
import edu.harvard.iq.dataverse.DatasetVersion;

import java.util.Objects;

/**
* This record encapsulates information related to the dataset, the version of the dataset,
* and the format name used for the export, enabling precise identification
* of cache entries for export operations.
*/
public record ExportCacheKey(Dataset dataset, DatasetVersion version, String formatName) {

/**
* Constructs an ExportCacheKey instance with the specified dataset, dataset version, and format name.
* @param dataset the dataset associated with this cache key; must not be null
* @param version the dataset version associated with this cache key; must not be null
* @param formatName the format name used for export operations; must not be null or blank
* @throws NullPointerException if the dataset, version, or formatName is null
* @throws IllegalArgumentException if the formatName is blank or empty
*/
public ExportCacheKey(Dataset dataset, DatasetVersion version, String formatName) {
this.dataset = Objects.requireNonNull(dataset);
this.version = Objects.requireNonNull(version);
if (Objects.requireNonNull(formatName).isBlank()) {
throw new IllegalArgumentException("formatName must not be blank or empty");
}
this.formatName = formatName;
}

/**
* Convenience wrapper to create a cache key fro ma version and format alone.
* Note: the entity object must have a reference to the dataset present!
* @param version the dataset version
* @param formatName the target format
* @throws NullPointerException if either version, the dataset in the version or the format are null
* @throws IllegalArgumentException if the format name is blank or empty
*/
public ExportCacheKey(DatasetVersion version, String formatName) {
this(Objects.requireNonNull(version).getDataset(), version, formatName);
}

/** The one canonical, version-qualified aux tag. */
public String auxTag() {
return "export_" + formatName + "_" + version.getFriendlyVersionNumber() + ".cached";
}
}
Loading