Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
e37526d
refactor(export): modularize ExportService by introducing ExporterReg…
poikilotherm Aug 7, 2026
83a8477
refactor(export): make ExportService a @Stateless EJB bean #12686
poikilotherm Aug 7, 2026
d466317
feat(export): introduce new ExportCache subsystem with cache key, inv…
poikilotherm Aug 7, 2026
8a7a5e4
refactor(export): extract file embargo expiry logic from ExportServic…
poikilotherm Aug 7, 2026
be3aeea
refactor(export): move caching logic from ExportService to new Storag…
poikilotherm Aug 7, 2026
8dfdda4
refactor(export): remove legacy unversioned cache logic from StorageI…
poikilotherm Aug 7, 2026
883e438
feat(export): enhance ExportCacheKey with validation and convenience …
poikilotherm Aug 7, 2026
105ba09
refactor(export): relocate service and provider classes to `export.se…
poikilotherm Aug 7, 2026
206c07e
docs(export): add Javadoc to private helpers in StorageIOCache #12686
poikilotherm Aug 18, 2026
7d48a93
refactor(export): move invalidators list from ExportCacheInvalidator …
poikilotherm Aug 18, 2026
60d953f
style(export): rename `exporterRegistry` field to `registry` in Expor…
poikilotherm Aug 18, 2026
9bbec86
feat(export): make ExportCache instance available in service #12686
poikilotherm Aug 18, 2026
38f3243
refactor(export): make cache clearing version-aware #12686
poikilotherm Aug 18, 2026
beefa7b
style(export): move export trigger service methods next to each other…
poikilotherm Aug 18, 2026
c747540
feat(export): add prerequisite dependency verification to ExporterReg…
poikilotherm Aug 19, 2026
642685b
feat(export): cache formatRequiredBy map in ExporterRegistryBean #12686
poikilotherm Aug 19, 2026
357528c
feat(export): add topological comparator to ExporterRegistryBean #12686
poikilotherm Aug 19, 2026
a8f422d
feat(util,export): enforce owner-only permissions on export temp file…
poikilotherm Aug 19, 2026
f3d21d4
fix(export): decouple ExportCacheKey from JPA entities #12686
poikilotherm Aug 20, 2026
aa5c787
refactor(export): replace depth-based comparator with transitive depe…
poikilotherm Aug 20, 2026
34aecb4
refactor(export): replace Labels record with sealed Details interface…
poikilotherm Aug 21, 2026
b59f4af
style(export): convert field comments to block comment style for read…
poikilotherm Aug 21, 2026
1a7c079
refactor(export): store formatName and friendlyVersion again as Expor…
poikilotherm Aug 21, 2026
709198a
refactor(export): remove obsolete exporter lookup methods from Export…
poikilotherm Aug 21, 2026
cfc60d9
fix(export): guard against null formatName in ExporterRegistryBean#ge…
poikilotherm Aug 21, 2026
0399135
feat(util): add FailureEscalation for threshold-based log level escal…
poikilotherm Aug 21, 2026
a4446ec
docs(export): correct legacy cache name behavior in StorageIOCache Ja…
poikilotherm Aug 21, 2026
e1d6642
feat(export): apply FailureEscalation to StorageIOCache log levels #1…
poikilotherm Aug 21, 2026
57e0f0b
feat(export): add ExportPipelineBean as central export orchestration …
poikilotherm Aug 21, 2026
8a49f74
refactor(export): introduce ExportPipelineBean to ExportServiceBean #…
poikilotherm Aug 21, 2026
c7900cb
feat(export): add isCacheable helper centralizing version cache polic…
poikilotherm Aug 21, 2026
4424bc1
refactor(export): rework exportFormats to use pipeline and topologica…
poikilotherm Aug 21, 2026
6ecdba8
refactor(export): delegate ExportService.getExport to ExportPipeline …
poikilotherm Aug 21, 2026
23b31b8
docs(export): note limitations of naive staleness invalidation #12686
poikilotherm Aug 21, 2026
d01ac8b
refactor(commands): replace ExportService.getInstance() with CommandC…
poikilotherm Aug 21, 2026
721c348
refactor(oai): introduce new export service beans #12686
poikilotherm Aug 21, 2026
74fdcb9
refactor(api): introduce export beans at API base infrastructure and …
poikilotherm Aug 21, 2026
4e73e16
refactor(signposting): use export service and registry beans #12686
poikilotherm Aug 21, 2026
75b616c
refactor(ui): use export service and registry beans in FilePage #12686
poikilotherm Aug 21, 2026
cebe87b
refactor(dataset): ask export service directly for dataset aux storag…
poikilotherm Aug 26, 2026
81f72c3
refactor(export): simplify getLatestPublishedAsString with new pipeli…
poikilotherm Aug 26, 2026
d94c10e
refactor(ui): use export service bean in DatasetPage #12686
poikilotherm Aug 26, 2026
13f92b2
docs(export): clarify stream-handling caveats in cached export path #…
poikilotherm Aug 26, 2026
8a76549
refactor(api): use new export service in Datasets API #12686
poikilotherm Aug 26, 2026
cea4534
refactor(export): add createProvider factory for test usage #12686
poikilotherm Sep 2, 2026
f69b8ce
refactor(export): add datasetId context scope to ExportCacheKey #12686
poikilotherm Sep 2, 2026
c7134a1
test(export): add unit tests for ExportCacheKey #12686
poikilotherm Sep 2, 2026
ef51d16
style(export): use lambda for fine-level cache export #12686
poikilotherm Sep 4, 2026
45a5f59
test(export): add unit tests for `StorageIOCache` functionality #12686
poikilotherm Sep 4, 2026
ad87fa4
style: fix leftover invalid imports from git merge
poikilotherm Sep 9, 2026
cbfffc3
refactor(export): increase testability of invalidator handling in Exp…
poikilotherm Sep 9, 2026
a8bb8b8
refactor(export): update `ExportPipelineBean` visibility to public as…
poikilotherm Sep 9, 2026
564d52b
test(export): add unit tests for `ExportPipelineBean` functionality #…
poikilotherm Sep 9, 2026
542506a
refactor(export): unseal `ExportCacheInvalidator` to allow extension …
poikilotherm Sep 9, 2026
421dd44
refactor(export): improve testability of `ExporterRegistryBean` with …
poikilotherm Sep 9, 2026
d4c33e8
test(export): add unit tests for `ExporterRegistryBean` functionality…
poikilotherm Sep 9, 2026
1d952fe
refactor(export): enhance cache eviction logic and improve format han…
poikilotherm Sep 10, 2026
d5b5891
refactor(export): move transitive dependent format expansion logic to…
poikilotherm Sep 10, 2026
b79bcf4
refactor(export): enrich eviction failure exception message with form…
poikilotherm Sep 10, 2026
0e95922
docs(export): fix Javadocs on clearCachedFormats #12686
poikilotherm Sep 10, 2026
751725b
refactor(export): improve `clearCachedFormats` null-check logic and a…
poikilotherm Sep 10, 2026
e6db1a5
fix(export): unseal `ExportCache` to enable EJB proxy classes
poikilotherm Sep 10, 2026
a0863ca
fix(export): improve logging for external exporter JAR access excepti…
poikilotherm Sep 10, 2026
d49c069
refactor(export): migrate `ExportPipelineBean` from EJB to CDI with c…
poikilotherm Sep 10, 2026
feb3f42
doc(export): clarify Javadocs for export methods they cache but don't…
poikilotherm Sep 10, 2026
529702c
Merge branch 'develop' into 12686-enhance-export-subsys
poikilotherm Sep 11, 2026
c2bff17
feat(export): add `ExportSystemException` hierarchy and mappers for i…
poikilotherm Sep 14, 2026
e5ce25e
refactor(export): replace `ExportException` with `ExportSystemExcepti…
poikilotherm Sep 14, 2026
cb0470b
refactor(export): update ExportRegistryBean with new exceptions #12686
poikilotherm Sep 14, 2026
9bebc57
refactor(export): update ExportPipelineBean with new exceptions #12686
poikilotherm Sep 14, 2026
8b0b5f6
refactor(export): hand over IOExceptions from ExportPipelineBean to E…
poikilotherm Sep 14, 2026
c6e5cbd
refactor(export): update ExportServiceBean to use new exceptions #12686
poikilotherm Sep 14, 2026
921e9da
refactor(export): close exception escape hatches at service border in…
poikilotherm Sep 14, 2026
bc2fe8d
refactor(export): validate dataset input in ExportServiceBean.usedCac…
poikilotherm Sep 14, 2026
b96cd57
refactor(export): track failed formats in ExportServiceBean during ex…
poikilotherm Sep 14, 2026
259b981
test(export): add InMemoryExportCache for unit testing ExportPipeline…
poikilotherm Sep 14, 2026
122257a
test(api): improve assertions for dataset export error messages #12686
poikilotherm Sep 14, 2026
3eb2f19
refactor(oai): align sDataverseXoaiItemRepository with new export sys…
poikilotherm Sep 14, 2026
8807192
refactor(dataset): log problems in usedCacheStorage calculation #12686
poikilotherm Sep 14, 2026
65686c6
fix(export): handle ExportSystemException in DatasetPage, add logging…
poikilotherm Sep 14, 2026
5513deb
style,ci: make checkstyle happy
poikilotherm Sep 14, 2026
e5509d0
fix(export): only update last export time if all formats are exported…
poikilotherm Sep 17, 2026
e04fe57
doc(export): explain why internal error exception is thrown for depen…
poikilotherm Sep 17, 2026
6d35f29
test(export): add unit tests for ExportServiceBean, covering export o…
poikilotherm Sep 17, 2026
5508afa
doc(util): add details on log level escalation and example usage of F…
poikilotherm Sep 17, 2026
cf8b0f3
doc(util): document SecureTempFiles usage for temporary file security…
poikilotherm Sep 17, 2026
807bb1c
fix(export): restrict FileEmbargoExpiryInvalidator cache checks to DD…
poikilotherm Sep 17, 2026
2f39a8c
test(export): add unit tests for FileEmbargoExpiryInvalidator edge ca…
poikilotherm Sep 17, 2026
d25615a
doc(export): add release notes snippet for #12686
poikilotherm Sep 17, 2026
4ec9683
Merge branch 'develop' into 12686-enhance-export-subsys
poikilotherm Sep 17, 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
2 changes: 1 addition & 1 deletion .github/workflows/reviewdog_checkstyle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v7
- name: Run check style
uses: nikitasavinov/checkstyle-action@master
uses: nikitasavinov/checkstyle-action@1.0.0
with:
fail_on_error: true
reporter: github-pr-review
Expand Down
28 changes: 28 additions & 0 deletions doc/release-notes/12686-enhance-export-subsys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
### More robust metadata export

The metadata export subsystem was overhauled: exports are generated more reliably, failures are reported more clearly, and cached exports are kept fresh more consistently.

### Better error reporting for failed exports

- When a dataset is (re-)exported in multiple formats, a failure of one format no longer aborts the others.
The resulting error message now lists exactly which formats failed (and the log records more context, including the dataset and format involved).
- Requesting an export in a format that Dataverse does not recognize (for example through a typo in the `exporter=` query parameter)
now returns a clear HTTP 400 (Bad Request) response with a readable error message, instead of an uninformative server error (HTTP 500) or forbidden (HTTP 403) response.
- Generating an export for an unreleased (draft) version no longer holds the entire export in memory; it is streamed through a temporary file instead.
Temporary export files are now created with owner-only permissions rather than the system default.

### Updates for system administrators

- Export failures now log clearer warnings including the dataset and the format(s) affected.
Repeatedly failing I/O operations (such as reading and deleting cached exports) escalate their log level and report recovery, making intermittent storage problems easier to spot.
- **Note:** Cached metadata exports generated with this or a previous release are stored under their legacy auxiliary object name (for example `export_ddi.cached`).
The new code manages the newer, version-qualified names instead (for example `export_ddi_1.0.cached`) and does not delete the legacy ones yet.
They may linger in a dataset's auxiliary storage until manual cleanup or a full re-export removes them. Purging of legacy cache entries is tracked as a follow-up task.

### Updates for developers

The previous monolithic `ExportService` was replaced by a small set of well-separated beans (`ExportServiceBean`, `ExportPipelineBean`, `ExporterRegistryBean`).
They are built on a new `ExportCache` abstraction, with a `StorageIO`-based implementation as default.
A new `ExportSystemException` hierarchy (subclasses `InternalFailure` and `InvalidRequest`) is mapped to appropriate HTTP statuses by JAX-RS exception mappers.
Export cache entries are now keyed by the dataset's version, and the code comes with extensive new unit tests.
The old `io.gdcc.spi.export.ExportException` is no longer used on or across export subsystem boundaries.
14 changes: 14 additions & 0 deletions doc/sphinx-guides/source/developers/coding-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ Generally speaking you should use `fine` for everything that you don't want to s

When adding logging, do not simply add `System.out.println()` lines because the logging level cannot be controlled.

#### Log Level Escalation

When ignoring or handling errors, often a debug-level error message is created.
Low occurences of such errors may not warrant a sysadmin's attention.
On the other hand, when such errors are more frequently encountered, log level escalation might be required.

The `edu.harvard.iq.dataverse.util.logging.FailureEscalation` utility class helps you achieve such escalation patterns.
An example on how to use the class is provided in its Javadocs.

### Avoid Hard-Coding Strings (Use Constants)

Special strings should be defined as public constants. For example, `DatasetFieldConstant.java` contains a field for "title" and it's used in many places in the code (try "Find Usages" in Netbeans). This is better than writing the string "title" in all those places.
Expand All @@ -119,6 +128,11 @@ If you just downloaded Netbeans and are using the out-of-the-box settings, you s

If you know of a way to easily share Netbeans configuration across a team, please get in touch.

### Temporary Files Security

To avoid exposing potentially sensitive information in temporary files stored on the file system, use the
`edu.harvard.iq.dataverse.util.SecureTempFiles` utility instead of Java mechanics as `Files.createTempFile` directly.

## Bash

Generally, Google's Shell Style Guide at <https://google.github.io/styleguide/shell.xml> seems to have good advice.
Expand Down
65 changes: 30 additions & 35 deletions src/main/java/edu/harvard/iq/dataverse/DatasetPage.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.harvard.iq.dataverse;

import edu.harvard.iq.dataverse.export.service.ExportSystemException;
import edu.harvard.iq.dataverse.provenance.ProvPopupFragmentBean;
import edu.harvard.iq.dataverse.api.AbstractApiBean;
import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean;
Expand Down Expand Up @@ -38,12 +39,11 @@
import edu.harvard.iq.dataverse.engine.command.impl.PublishDatasetCommand;
import edu.harvard.iq.dataverse.engine.command.impl.PublishDataverseCommand;
import edu.harvard.iq.dataverse.engine.command.impl.UpdateDatasetVersionCommand;
import edu.harvard.iq.dataverse.export.ExportService;
import edu.harvard.iq.dataverse.settings.FeatureFlags;
import edu.harvard.iq.dataverse.export.service.ExportServiceBean;
import edu.harvard.iq.dataverse.export.service.ExporterRegistryBean;
import edu.harvard.iq.dataverse.util.cache.CacheFactoryBean;
import edu.harvard.iq.dataverse.util.json.JsonUtil;
import io.gdcc.spi.export.ExportException;
import io.gdcc.spi.export.Exporter;
import edu.harvard.iq.dataverse.ingest.IngestRequest;
import edu.harvard.iq.dataverse.ingest.IngestServiceBean;
import edu.harvard.iq.dataverse.license.LicenseServiceBean;
Expand Down Expand Up @@ -105,7 +105,6 @@
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.persistence.OptimisticLockException;
Expand Down Expand Up @@ -255,6 +254,10 @@ public enum DisplayMode {
DvObjectServiceBean dvObjectService;
@EJB
CacheFactoryBean cacheFactory;
@EJB
ExportServiceBean exportService;
@EJB
ExporterRegistryBean exporterRegistryService;
@Inject
DataverseRequestServiceBean dvRequestService;
@Inject
Expand Down Expand Up @@ -4898,31 +4901,18 @@ public String getTabularDataFileURL(Long fileid) {
}

public List< String[]> getExporters(){
List<String[]> retList = new ArrayList<>();
String myHostURL = getDataverseSiteUrl();
for (String [] provider : ExportService.getInstance().getExportersLabels() ){
String formatName = provider[1];
String formatDisplayName = provider[0];

Exporter exporter = null;
try {
exporter = ExportService.getInstance().getExporter(formatName);
} catch (ExportException ex) {
logger.warning("Failed to get : " + formatName);
logger.warning(ex.getLocalizedMessage());
exporter = null;
}
if (exporter != null && exporter.isAvailableToUsers()) {
// Not all metadata exports should be presented to the web users!
// Some are only for harvesting clients.

String[] temp = new String[2];
temp[0] = formatDisplayName;
temp[1] = myHostURL + "/api/datasets/export?exporter=" + formatName + "&persistentId=" + dataset.getGlobalId().asString();
retList.add(temp);
}
}
return retList;
String urlTemplate = getDataverseSiteUrl() + "/api/datasets/export?exporter=%s&persistentId=%s";

return exporterRegistryService.getDetails().stream()
.filter(ExporterRegistryBean.Details::isAvailableToUsers)
.map(details -> new String[]{
details.localizedDisplayName(),
urlTemplate.formatted(
details.formatName(),
dataset.getGlobalId().asString()
)
})
.toList();
}


Expand Down Expand Up @@ -6136,9 +6126,15 @@ public String getCroissant() {
// The full version is available from the "Export Metadata" dropdown.
// Both versions are available via API.
final String CROISSANT_SCHEMA_NAME = "croissantSlim";
ExportService instance = ExportService.getInstance();
String croissant = instance.getLatestPublishedAsString(dataset, CROISSANT_SCHEMA_NAME);
if (FeatureFlags.CROISSANT_WITH_LOCAL_REVIEWS.enabled()) {
String croissant = null;
try {
croissant = exportService.getLatestPublishedAsString(dataset, CROISSANT_SCHEMA_NAME);
} catch (ExportSystemException e) {
logger.log(Level.WARNING, "Unable to get latest published croissant schema as String", e);
return null;
}

if (croissant != null && FeatureFlags.CROISSANT_WITH_LOCAL_REVIEWS.enabled()) {
// Rewrite the export on the fly and insert local reviews until we have a solution for https://github.com/gdcc/dataverse-spi/issues/5
JsonObjectBuilder reviewsJsonObj = null;
try {
Expand Down Expand Up @@ -6169,8 +6165,7 @@ public List<License> getAvailableLicenses(){

public String getJsonLd() {
if (isThisLatestReleasedVersion()) {
ExportService instance = ExportService.getInstance();
String jsonLd = instance.getLatestPublishedAsString(dataset, SchemaDotOrgExporter.NAME);
String jsonLd = exportService.getLatestPublishedAsString(dataset, SchemaDotOrgExporter.NAME);
if (jsonLd != null) {
logger.fine("Returning cached schema.org JSON-LD.");
return jsonLd;
Expand Down Expand Up @@ -7019,7 +7014,7 @@ public String getSignpostingLinkHeader() {
return null;
}
if (signpostingLinkHeader == null) {
SignpostingResources sr = new SignpostingResources(systemConfig, workingVersion,
SignpostingResources sr = new SignpostingResources(systemConfig, exporterRegistryService, workingVersion,
JvmSettings.SIGNPOSTING_LEVEL1_AUTHOR_LIMIT.lookupOptional().orElse(""),
JvmSettings.SIGNPOSTING_LEVEL1_ITEM_LIMIT.lookupOptional().orElse(""));
signpostingLinkHeader = sr.getLinks();
Expand Down
22 changes: 10 additions & 12 deletions src/main/java/edu/harvard/iq/dataverse/DatasetServiceBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import edu.harvard.iq.dataverse.authorization.Permission;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.authorization.users.User;
import edu.harvard.iq.dataverse.dataaccess.DataAccess;

Check warning on line 8 in src/main/java/edu/harvard/iq/dataverse/DatasetServiceBean.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused import 'edu.harvard.iq.dataverse.dataaccess.DataAccess'.

See more on https://sonarcloud.io/project/issues?id=IQSS_dataverse&issues=AaCLqI0l069oWOY1zgis&open=AaCLqI0l069oWOY1zgis&pullRequest=12688
import edu.harvard.iq.dataverse.dataaccess.ImageThumbConverter;
import edu.harvard.iq.dataverse.dataaccess.StorageIO;
import edu.harvard.iq.dataverse.dataset.DatasetUtil;
Expand All @@ -16,7 +16,8 @@
import edu.harvard.iq.dataverse.engine.command.impl.DestroyDatasetCommand;
import edu.harvard.iq.dataverse.engine.command.impl.FinalizeDatasetPublicationCommand;
import edu.harvard.iq.dataverse.engine.command.impl.GetDatasetStorageSizeCommand;
import edu.harvard.iq.dataverse.export.ExportService;
import edu.harvard.iq.dataverse.export.service.ExportServiceBean;
import edu.harvard.iq.dataverse.export.service.ExportSystemException;
import edu.harvard.iq.dataverse.globus.GlobusServiceBean;
import edu.harvard.iq.dataverse.harvest.server.OAIRecordServiceBean;
import edu.harvard.iq.dataverse.pidproviders.FailedPIDResolutionLoggingServiceBean;
Expand Down Expand Up @@ -103,6 +104,9 @@

@EJB
UserNotificationServiceBean userNotificationService;

@EJB
ExportServiceBean exportService;

private static final SimpleDateFormat logFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss");

Expand Down Expand Up @@ -1079,17 +1083,11 @@

// and finally,
if (countCachedExtras) {
// count the sizes of the files cached for the dataset itself
// (i.e., the metadata exports):
StorageIO<Dataset> datasetSIO = DataAccess.getStorageIO(dataset);

for (String[] exportProvider : ExportService.getInstance().getExportersLabels()) {
String exportLabel = "export_" + exportProvider[1] + ".cached";
try {
total += datasetSIO.getAuxObjectSize(exportLabel);
} catch (IOException ioex) {
// safe to ignore; object not cached
}
try {
// Count the sizes of the files cached for the dataset itself (i.e., the metadata exports):
total += exportService.usedCacheStorage(dataset);
} catch (IOException | ExportSystemException ex) {
logger.log(Level.INFO, ex, () -> "Failed to count cached metadata exports size for the dataset: " + dataset.getId() + " (ignoring)");
}
}

Expand Down
20 changes: 19 additions & 1 deletion src/main/java/edu/harvard/iq/dataverse/EjbDataverseEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean;
import edu.harvard.iq.dataverse.authorization.providers.builtin.BuiltinUserServiceBean;
import edu.harvard.iq.dataverse.dataverse.featured.DataverseFeaturedItemServiceBean;
import edu.harvard.iq.dataverse.export.service.ExportServiceBean;
import edu.harvard.iq.dataverse.export.service.ExporterRegistryBean;
import edu.harvard.iq.dataverse.license.LicenseServiceBean;
import edu.harvard.iq.dataverse.util.cache.CacheFactoryBean;
import edu.harvard.iq.dataverse.engine.DataverseEngine;
Expand Down Expand Up @@ -209,6 +211,12 @@ public class EjbDataverseEngine {
@EJB
CacheFactoryBean cacheFactory;

@EJB
ExportServiceBean exportService;

@EJB
ExporterRegistryBean exporterRegistry;

@Resource
EJBContext ejbCtxt;

Expand Down Expand Up @@ -664,7 +672,17 @@ public MetadataBlockServiceBean metadataBlocks() {
public DatasetTypeServiceBean datasetTypes() {
return datasetTypeService;
}


@Override
public ExportServiceBean exportService() {
return exportService;
}

@Override
public ExporterRegistryBean exporterRegistry() {
return exporterRegistry;
}

@Override
public void beginCommandSequence() {
this.commandsCalled = new Stack();
Expand Down
58 changes: 24 additions & 34 deletions src/main/java/edu/harvard/iq/dataverse/FilePage.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
import edu.harvard.iq.dataverse.engine.command.impl.RestrictFileCommand;
import edu.harvard.iq.dataverse.engine.command.impl.UningestFileCommand;
import edu.harvard.iq.dataverse.engine.command.impl.UpdateDatasetVersionCommand;
import edu.harvard.iq.dataverse.export.ExportService;
import io.gdcc.spi.export.ExportException;
import io.gdcc.spi.export.Exporter;
import edu.harvard.iq.dataverse.export.service.ExportServiceBean;
import edu.harvard.iq.dataverse.export.service.ExportSystemException;
import edu.harvard.iq.dataverse.export.service.ExporterRegistryBean;
import edu.harvard.iq.dataverse.export.service.ExporterRegistryBean.Details;
import edu.harvard.iq.dataverse.externaltools.ExternalTool;
import edu.harvard.iq.dataverse.externaltools.ExternalToolHandler;
import edu.harvard.iq.dataverse.externaltools.ExternalToolServiceBean;
Expand All @@ -36,7 +37,6 @@
import edu.harvard.iq.dataverse.makedatacount.MakeDataCountLoggingServiceBean;
import edu.harvard.iq.dataverse.makedatacount.MakeDataCountLoggingServiceBean.MakeDataCountEntry;
import edu.harvard.iq.dataverse.privateurl.PrivateUrlServiceBean;
import edu.harvard.iq.dataverse.settings.FeatureFlags;
import edu.harvard.iq.dataverse.settings.JvmSettings;
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
import edu.harvard.iq.dataverse.util.BundleUtil;
Expand Down Expand Up @@ -64,7 +64,6 @@
import jakarta.faces.application.FacesMessage;
import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.validator.ValidatorException;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
Expand Down Expand Up @@ -129,6 +128,10 @@ public class FilePage implements java.io.Serializable {
IngestServiceBean ingestService;
@EJB
SystemConfig systemConfig;
@EJB
ExportServiceBean exportService;
@EJB
ExporterRegistryBean exporterRegistryService;


@Inject
Expand Down Expand Up @@ -466,30 +469,19 @@ public void setVersion(String version) {
this.version = version;
}

public List< String[]> getExporters(){
List<String[]> retList = new ArrayList<>();
String myHostURL = systemConfig.getDataverseSiteUrl();
for (String [] provider : ExportService.getInstance().getExportersLabels() ){
String formatName = provider[1];
String formatDisplayName = provider[0];

Exporter exporter = null;
try {
exporter = ExportService.getInstance().getExporter(formatName);
} catch (ExportException ex) {
exporter = null;
}
if (exporter != null && exporter.isAvailableToUsers()) {
// Not all metadata exports should be presented to the web users!
// Some are only for harvesting clients.

String[] temp = new String[2];
temp[0] = formatDisplayName;
temp[1] = myHostURL + "/api/datasets/export?exporter=" + formatName + "&persistentId=" + fileMetadata.getDatasetVersion().getDataset().getGlobalId().asString();
retList.add(temp);
}
}
return retList;
public List<String[]> getExporters(){
String urlTemplate = systemConfig.getDataverseSiteUrl() + "/api/datasets/export?exporter=%s&persistentId=%s";

return exporterRegistryService.getDetails().stream()
.filter(Details::isAvailableToUsers)
.map(details -> new String[]{
details.localizedDisplayName(),
urlTemplate.formatted(
details.formatName(),
fileMetadata.getDatasetVersion().getDataset().getGlobalId().asString()
)
})
.toList();
}

public String saveProvFreeform(String freeformTextInput, DataFile dataFileFromPopup) throws CommandException {
Expand Down Expand Up @@ -640,15 +632,13 @@ public String uningestFile() throws CommandException {
editDataset = file.getOwner();
if (editDataset.isReleased()) {
try {
ExportService instance = ExportService.getInstance();
instance.exportAllFormats(editDataset);

} catch (ExportException ex) {
exportService.exportAllFormats(editDataset);
} catch (ExportSystemException ex) {
// Something went wrong!
// Just like with indexing, a failure to export is not a fatal
// condition. We'll just log the error as a warning and keep
// going:
logger.log(Level.WARNING, "Uningest: Exception while exporting:{0}", ex.getMessage());
logger.log(Level.WARNING, ex, () -> "Uningest: Exception while exporting " + editDataset.getId());
}
}
datafileService.save(file);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import edu.harvard.iq.dataverse.engine.command.impl.GetLatestAccessibleDatasetVersionCommand;
import edu.harvard.iq.dataverse.engine.command.impl.GetLatestPublishedDatasetVersionCommand;
import edu.harvard.iq.dataverse.engine.command.impl.GetSpecificPublishedDatasetVersionCommand;
import edu.harvard.iq.dataverse.export.service.ExportServiceBean;
import edu.harvard.iq.dataverse.export.service.ExporterRegistryBean;
import edu.harvard.iq.dataverse.externaltools.ExternalToolServiceBean;
import edu.harvard.iq.dataverse.license.LicenseServiceBean;
import edu.harvard.iq.dataverse.makedatacount.DatasetMetricsServiceBean;
Expand Down Expand Up @@ -246,6 +248,12 @@ String getWrappedMessageWhenJson() {

@EJB
TemplateServiceBean templateSvc;

@EJB
ExportServiceBean exportSvc;

@EJB
ExporterRegistryBean exporterRegistrySvc;

@Inject
FailedPIDResolutionLoggingServiceBean fprLogService;
Expand Down
Loading
Loading