Class CollectionArchiveService

java.lang.Object
io.goobi.viewer.model.export.bagit.CollectionArchiveService

public class CollectionArchiveService extends Object
Builds and serves the per-collection BagIt archives described by <collectionArchives> in config_viewer.xml.

Responsibilities:

  • build the collection-selector query with the anonymous access / static / blacklist suffixes (record-level pre-filter), with all indexed values Solr-escaped to prevent query injection;
  • enforce page-level access when assembling the payload: per-file for images/ALTO/plaintext (only anonymously-accessible files are packed), and a whole-record "fully open" gate for the METS/LIDO source and TEI;
  • decide whether a collection's archive needs (re)generation by comparing the current record count and maximum SolrConstants.DATEINDEXED against the values encoded in the existing archive's file name;
  • assemble the enabled content types into a bag's data/ directory, write it via BagItWriter, and atomically move it into the storage folder;
  • locate and describe existing archives for the download endpoint / collection page, and prune orphaned archives.

Archive files are named <slug>__n<recordCount>__u<maxIndexedMillis>.zip; this file name is the sole persisted state (no sidecar manifest, no database table).

  • Constructor Details

    • CollectionArchiveService

      public CollectionArchiveService()
      Creates a service using the global DataManager configuration.
    • CollectionArchiveService

      public CollectionArchiveService(Configuration config)
      Parameters:
      config - the configuration to read archive settings and data-folder names from
  • Method Details

    • getStorageBaseFolder

      public Path getStorageBaseFolder()
      Returns:
      the base storage folder for all collection archives (<viewerHome>/<collectionArchivesFolder>)
    • getFieldFolder

      public Path getFieldFolder(String field)
      Parameters:
      field - the collection Solr field (validated by the caller against the configured allow-list)
      Returns:
      the storage folder holding archives for the given field
    • slugify

      public static String slugify(String name)
      Builds a filesystem-safe slug from a collection (or field) name. All characters outside [A-Za-z0-9.-] are replaced with an underscore, so the slug can never contain a path separator or traversal sequence.
      Parameters:
      name - the raw name
      Returns:
      a slug safe for use as a single path segment
    • findArchive

      public Optional<CollectionArchiveService.ArchiveInfo> findArchive(String field, String collectionName) throws IOException
      Finds the current archive file for a collection, if one exists.
      Parameters:
      field - the collection Solr field
      collectionName - the collection name
      Returns:
      the archive description, or empty if none exists
      Throws:
      IOException - on directory read error
    • buildCollectionQuery

      public static String buildCollectionQuery(String field, String collectionName)
      Builds the full Solr query selecting the freely-accessible top-level records of a collection (and its sub-collections). The collection name is escaped both as a phrase and as a wildcard prefix to prevent query injection.

      Access, static-query and collection-blacklist filtering are delegated to SearchHelper.getAllSuffixes(HttpServletRequest, boolean, boolean) with a null request, which evaluates the access filter for an anonymous principal (failing closed to OPENACCESS-only), appends the configured <staticQuerySuffix>, and excludes blacklisted DC collections. This is a coarse record-level pre-filter; page-level access is enforced per file when the payload is assembled (see populateAccessFilteredFolders(io.goobi.viewer.model.export.bagit.ArchiveContentType, java.nio.file.Path, java.util.List<org.apache.solr.common.SolrDocument>, java.lang.String, java.lang.String, java.lang.String, io.goobi.viewer.model.export.bagit.CollectionArchiveService.PopulationStats) and hasRestrictedFulltextPage(java.lang.String)).

      Parameters:
      field - the collection Solr field
      collectionName - the collection name
      Returns:
      the combined Solr query string
    • generateIfChanged

      Generates the archive for a single collection if (and only if) it does not yet exist or its content has changed since the last generation, as determined by comparing the current record count and maximum DATEINDEXED against the state encoded in the existing archive's file name.
      Parameters:
      field - the collection Solr field
      collectionName - the collection name
      archiveConfig - the resolved set of content types to include
      Returns:
      a CollectionArchiveService.GenerationResult indicating whether an archive was (re)generated and a human-readable detail (content summary when generated, or the reason when skipped)
      Throws:
      PresentationException - on Solr query errors
      IndexUnreachableException - if the Solr index is unreachable
      DAOException - if license types cannot be loaded
      IOException - on filesystem errors
      ViewerConfigurationException - on export configuration errors
    • deleteExistingArchives

      public void deleteExistingArchives(String field, String collectionName) throws IOException
      Deletes all archive files for a collection.
      Parameters:
      field - the collection Solr field
      collectionName - the collection name
      Throws:
      IOException - on filesystem error
    • pruneOrphans

      public void pruneOrphans(String field, Set<String> liveSlugs) throws IOException
      Removes archive files under a field's folder whose collection slug is not present in liveSlugs (i.e. the collection no longer exists in the index or is no longer configured for archiving).
      Parameters:
      field - the collection Solr field
      liveSlugs - the set of slugs for collections that should retain their archives
      Throws:
      IOException - on filesystem error