From 25bc361ac37f55f27a95e5031e09cedfbae4ce23 Mon Sep 17 00:00:00 2001 From: Torsten Krause Date: Thu, 29 Jan 2026 13:33:04 +0100 Subject: [PATCH 1/4] MCR-3600 turn MCRXMLMetadataManager into an interface --- .../common/MCRDefaultXMLMetadataManager.java | 665 ++++++++++++++++++ .../MCRDefaultXMLMetadataManagerAdapter.java | 613 +--------------- .../common/MCRXMLMetadataManager.java | 279 ++++---- .../common/MCRXMLMetadataManagerAdapter.java | 180 +---- .../main/resources/config/mycore.properties | 2 +- .../common/MCRXMLMetadataManagerTest.java | 2 +- .../mycore/ocfl/commands/MCROCFLCommands.java | 12 +- .../ocfl/commands/MCROCFLRegexCommands.java | 10 +- .../MCRGZIPOCFLXMLMetadataManager.java | 63 ++ .../MCRGZIPOCFLXMLMetadataManagerAdapter.java | 46 +- .../metadata/MCROCFLXMLMetadataManager.java | 590 ++++++++++++++++ .../MCROCFLXMLMetadataManagerAdapter.java | 568 +-------------- .../metadata/migration/MCROCFLMigration.java | 8 +- .../src/test/resources/mycore.properties | 2 +- 14 files changed, 1506 insertions(+), 1534 deletions(-) create mode 100644 mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultXMLMetadataManager.java create mode 100644 mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCRGZIPOCFLXMLMetadataManager.java create mode 100644 mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCROCFLXMLMetadataManager.java diff --git a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultXMLMetadataManager.java b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultXMLMetadataManager.java new file mode 100644 index 0000000000..4a238a8fdc --- /dev/null +++ b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultXMLMetadataManager.java @@ -0,0 +1,665 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.datamodel.common; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jdom2.Document; +import org.jdom2.JDOMException; +import org.mycore.common.MCRCache; +import org.mycore.common.MCRPersistenceException; +import org.mycore.common.config.MCRConfiguration2; +import org.mycore.common.config.MCRConfigurationBase; +import org.mycore.common.config.MCRConfigurationException; +import org.mycore.common.content.MCRContent; +import org.mycore.common.content.MCRJDOMContent; +import org.mycore.datamodel.ifs2.MCRMetadataStore; +import org.mycore.datamodel.ifs2.MCRMetadataVersion; +import org.mycore.datamodel.ifs2.MCRObjectIDFileSystemDate; +import org.mycore.datamodel.ifs2.MCRStore; +import org.mycore.datamodel.ifs2.MCRStoreCenter; +import org.mycore.datamodel.ifs2.MCRStoreManager; +import org.mycore.datamodel.ifs2.MCRStoredMetadata; +import org.mycore.datamodel.ifs2.MCRVersionedMetadata; +import org.mycore.datamodel.ifs2.MCRVersioningMetadataStore; +import org.mycore.datamodel.metadata.MCRDerivate; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.datamodel.metadata.MCRObjectID; +import org.mycore.datamodel.metadata.history.MCRMetadataHistoryManager; + +/** + * Manages persistence of MCRObject and MCRDerivate xml metadata. + * Provides methods to create, retrieve, update and delete object metadata + * using IFS2 MCRMetadataStore instances. + *

+ * For configuration, at least the following properties must be set: + *

+ * MCR.Metadata.Store.BaseDir=/path/to/metadata/dir + * MCR.Metadata.Store.SVNBase=file:///path/to/local/svndir/ + *

+ * Both directories will be created if they do not exist yet. + * For each project and type, a subdirectory will be created, + * for example %MCR.Metadata.Store.BaseDir%/DocPortal/document/. + *

+ * The default IFS2 store is MCRVersioningMetadataStore, which + * versions metadata using SVN in local repositories below SVNBase. + * If you do not want versioning and would like to have better + * performance, change the following property to + *

+ * MCR.Metadata.Store.DefaultClass=org.mycore.datamodel.ifs2.MCRMetadataStore + *

+ * It is also possible to change individual properties per project and object type + * and overwrite the defaults, for example + *

+ * MCR.IFS2.Store.Class=org.mycore.datamodel.ifs2.MCRVersioningMetadataStore + * MCR.IFS2.Store.SVNRepositoryURL=file:///use/other/location/for/document/versions/ + * MCR.IFS2.Store.SlotLayout=2-2-2-2 + *

+ * See documentation of MCRStore and MCRMetadataStore for details. + * + * @author Frank Lützenkirchen + * @author Jens Kupferschmidt + * @author Thomas Scheffler (yagee) + */ +public class MCRDefaultXMLMetadataManager implements MCRXMLMetadataManager { + + private static final Logger LOGGER = LogManager.getLogger(); + + private static final String DEFAULT_SVN_DIRECTORY_NAME = "versions-metadata"; + + private final Set createdStores; + + /** + * The default IFS2 Metadata store class to use, set by MCR.Metadata.Store.DefaultClass + */ + @SuppressWarnings("rawtypes") + private Class defaultClass; + + /** + * The default subdirectory slot layout for IFS2 metadata store, is 4-2-2 for 8-digit IDs, + * that means DocPortal_document_0000001 will be stored in the file + * DocPortal/document/0000/00/DocPortal_document_00000001.xml + */ + private String defaultLayout; + + /** + * The base directory for all IFS2 metadata stores used, set by MCR.Metadata.Store.BaseDir + */ + private Path basePath; + + /** + * The local base directory for IFS2 versioned metadata using SVN, set URI by MCR.Metadata.Store.SVNBase + */ + private Path svnPath; + + /** + * The local file:// uri of all SVN versioned metadata, set URI by MCR.Metadata.Store.SVNBase + */ + private URI svnBase; + + public MCRDefaultXMLMetadataManager() { + this.createdStores = new HashSet<>(); + reload(); + } + + @Override + public synchronized void reload() { + String pattern = MCRConfiguration2.getStringOrThrow("MCR.Metadata.ObjectID.NumberPattern"); + defaultLayout = pattern.length() - 4 + "-2-2"; + + String base = MCRConfiguration2.getStringOrThrow("MCR.Metadata.Store.BaseDir"); + basePath = Paths.get(base); + checkPath(basePath, "base"); + + defaultClass = MCRConfiguration2.getClass("MCR.Metadata.Store.DefaultClass") + .orElse(MCRVersioningMetadataStore.class); + if (MCRVersioningMetadataStore.class.isAssignableFrom(defaultClass)) { + Optional svnBaseOpt = MCRConfiguration2.getString("MCR.Metadata.Store.SVNBase"); + if (svnBaseOpt.isEmpty()) { + svnPath = Paths.get(MCRConfiguration2.getStringOrThrow("MCR.datadir")) + .resolve(DEFAULT_SVN_DIRECTORY_NAME); + checkPath(svnPath, "svn"); + svnBase = svnPath.toUri(); + } else { + try { + String svnBaseValue = svnBaseOpt.get(); + if (!svnBaseValue.endsWith("/")) { + svnBaseValue += '/'; + } + svnBase = new URI(svnBaseValue); + LOGGER.info("SVN Base: {}", svnBase); + if (svnBase.getScheme() == null) { + String workingDirectory = (new File(".")).getAbsolutePath(); + URI root = new File(MCRConfiguration2.getString("MCR.datadir").orElse(workingDirectory)) + .toURI(); + URI resolved = root.resolve(svnBase); + LOGGER.warn("Resolved {} to {}", svnBase, resolved); + svnBase = resolved; + } + } catch (URISyntaxException ex) { + String msg = "Syntax error in MCR.Metadata.Store.SVNBase property: " + svnBase; + throw new MCRConfigurationException(msg, ex); + } + if (svnBase.getScheme().equals("file")) { + svnPath = Paths.get(svnBase); + checkPath(svnPath, "svn"); + } + } + } + closeCreatedStores(); + } + + private synchronized void closeCreatedStores() { + for (String storeId : createdStores) { + MCRStoreCenter.getInstance().removeStore(storeId); + } + createdStores.clear(); + } + + /** + * Checks the directory configured exists and is readable and writable, or creates it + * if it does not exist yet. + * + * @param path the path to check + * @param type metadata store type + */ + @SuppressWarnings("PMD.AvoidDuplicateLiterals") + private void checkPath(Path path, String type) { + if (!Files.exists(path)) { + try { + if (!Files.exists(Files.createDirectories(path))) { + throw new MCRConfigurationException( + "The metadata store " + type + " directory " + path.toAbsolutePath() + " does not exist."); + } + } catch (Exception ex) { + String msg = "Exception while creating metadata store " + type + " directory " + path.toAbsolutePath(); + throw new MCRConfigurationException(msg, ex); + } + } else { + if (!Files.isDirectory(path)) { + throw new MCRConfigurationException( + "Metadata store " + type + " " + path.toAbsolutePath() + " is a file, not a directory"); + } + if (!Files.isReadable(path)) { + throw new MCRConfigurationException( + "Metadata store " + type + " directory " + path.toAbsolutePath() + " is not readable"); + } + if (!Files.isWritable(path)) { + throw new MCRConfigurationException( + "Metadata store " + type + " directory " + path.toAbsolutePath() + " is not writeable"); + } + } + } + + /** + * Returns IFS2 MCRMetadataStore for the given MCRObjectID base, which is {project}_{type} + * + * @param base the MCRObjectID base, e.g. DocPortal_document + */ + private MCRMetadataStore getStore(String base) { + String[] split = base.split("_"); + return getStore(split[0], split[1], false); + } + + /** + * Returns IFS2 MCRMetadataStore for the given MCRObjectID base, which is {project}_{type} + * + * @param base the MCRObjectID base, e.g. DocPortal_document + * @param readOnly If readOnly, the store will not be created if it does not exist yet. Instead an exception + * is thrown. + * @return the metadata store + */ + private MCRMetadataStore getStore(String base, boolean readOnly) { + String[] split = base.split("_"); + return getStore(split[0], split[1], readOnly); + } + + /** + * Returns IFS2 MCRMetadataStore used to store metadata of the given MCRObjectID + * + * @param mcrid the mycore object identifier + * @param readOnly If readOnly, the store will not be created if it does not exist yet. Instead an exception + * is thrown. + * @return the metadata store + */ + private MCRMetadataStore getStore(MCRObjectID mcrid, boolean readOnly) { + return getStore(mcrid.getProjectId(), mcrid.getTypeId(), readOnly); + } + + /** + * Returns IFS2 MCRMetadataStore for the given project and object type + * + * @param project the project, e.g. DocPortal + * @param type the object type, e.g. document + * @param readOnly if readOnly, this method will throw an exception if the store does not exist's yet + * @return the metadata store + */ + private MCRMetadataStore getStore(String project, String type, boolean readOnly) { + String projectType = getStoryKey(project, type); + String prefix = "MCR.IFS2.Store." + projectType + "."; + String forceXML = MCRConfiguration2.getString(prefix + "ForceXML").orElse(null); + if (forceXML == null) { + synchronized (this) { + forceXML = MCRConfiguration2.getString(prefix + "ForceXML").orElse(null); + if (forceXML == null) { + try { + setupStore(project, type, prefix, readOnly); + } catch (ReflectiveOperationException e) { + throw new MCRPersistenceException( + new MessageFormat("Could not instantiate store for project {0} and object type {1}.", + Locale.ROOT).format(new Object[] { project, type }), + e); + } + } + } + } + MCRMetadataStore store = MCRStoreManager.getStore(projectType); + if (store == null) { + throw new MCRPersistenceException( + new MessageFormat("Metadata store for project {0} and object type {1} is unconfigured.", Locale.ROOT) + .format(new Object[] { project, type })); + } + return store; + } + + @Override + public void verifyStore(String base) { + MCRMetadataStore store = getStore(base); + if (store instanceof MCRVersioningMetadataStore versioningMetadataStore) { + LOGGER.info("Verifying SVN history of {}.", base); + versioningMetadataStore.verify(); + } else { + LOGGER.warn("Cannot verify unversioned store {}!", base); + } + } + + @SuppressWarnings("unchecked") + private void setupStore(String project, String objectType, String configPrefix, boolean readOnly) + throws ReflectiveOperationException { + String baseID = getStoryKey(project, objectType); + Class clazz = MCRConfiguration2.getClass(configPrefix + "Class") + .orElseGet(() -> { + MCRConfiguration2.set(configPrefix + "Class", defaultClass.getName()); + return defaultClass; + }); + if (MCRVersioningMetadataStore.class.isAssignableFrom(clazz)) { + String property = configPrefix + "SVNRepositoryURL"; + String svnURL = MCRConfiguration2.getString(property).orElse(null); + if (svnURL == null) { + String relativeURI = new MessageFormat("{0}/{1}/", Locale.ROOT) + .format(new Object[] { project, objectType }); + URI repURI = svnBase.resolve(relativeURI); + LOGGER.info("Resolved {} to {} for {}", () -> relativeURI, repURI::toASCIIString, () -> property); + MCRConfiguration2.set(property, repURI.toASCIIString()); + checkAndCreateDirectory(svnPath.resolve(project), project, objectType, configPrefix, readOnly); + } + } + + Path typePath = basePath.resolve(project).resolve(objectType); + checkAndCreateDirectory(typePath, project, objectType, configPrefix, readOnly); + + String slotLayout = MCRConfiguration2.getString(configPrefix + "SlotLayout").orElse(null); + if (slotLayout == null) { + MCRConfiguration2.set(configPrefix + "SlotLayout", defaultLayout); + } + MCRConfiguration2.set(configPrefix + "BaseDir", typePath.toAbsolutePath().toString()); + MCRConfiguration2.set(configPrefix + "ForceXML", String.valueOf(true)); + String value = Objects.equals(objectType, MCRDerivate.OBJECT_TYPE) ? + MCRDerivate.ROOT_NAME : MCRObject.ROOT_NAME; + MCRConfiguration2.set(configPrefix + "ForceDocType", value); + createdStores.add(baseID); + MCRStoreManager.createStore(baseID, clazz); + } + + private void checkAndCreateDirectory(Path path, String project, String objectType, String configPrefix, + boolean readOnly) { + if (Files.exists(path)) { + return; + } + if (readOnly) { + throw new MCRPersistenceException(String.format(Locale.ENGLISH, + "Path does not exists ''%s'' to set up store for project ''%s'' and objectType ''%s'' " + + "and config prefix ''%s''. We are not willing to create it for an read only operation.", + path.toAbsolutePath(), project, objectType, configPrefix)); + } + try { + if (!Files.exists(Files.createDirectories(path))) { + throw new FileNotFoundException(path.toAbsolutePath() + " does not exists."); + } + } catch (Exception e) { + throw new MCRPersistenceException(String.format(Locale.ENGLISH, + "Couldn'e create directory ''%s'' to set up store for project ''%s'' and objectType ''%s'' " + + "and config prefix ''%s''", + path.toAbsolutePath(), project, objectType, configPrefix), e); + } + } + + private String getStoryKey(String project, String objectType) { + return project + "_" + objectType; + } + + @Override + public void create(MCRObjectID mcrid, MCRContent xml, Date lastModified) + throws MCRPersistenceException { + try { + MCRStoredMetadata sm = getStore(mcrid, false).create(xml, mcrid.getNumberAsInteger()); + sm.setLastModified(lastModified); + MCRConfigurationBase.systemModified(); + } catch (Exception exc) { + throw new MCRPersistenceException("Error while storing object: " + mcrid, exc); + } + } + + @Override + public void delete(MCRObjectID mcrid) throws MCRPersistenceException { + try { + getStore(mcrid, true).delete(mcrid.getNumberAsInteger()); + MCRConfigurationBase.systemModified(); + } catch (Exception exc) { + throw new MCRPersistenceException("Error while deleting object: " + mcrid, exc); + } + } + + @Override + public void update(MCRObjectID mcrid, MCRContent xml, Date lastModified) + throws MCRPersistenceException { + if (!exists(mcrid)) { + throw new MCRPersistenceException("Object to update does not exist: " + mcrid); + } + try { + MCRStoredMetadata sm = getStore(mcrid, false).retrieve(mcrid.getNumberAsInteger()); + sm.update(xml); + sm.setLastModified(lastModified); + MCRConfigurationBase.systemModified(); + } catch (Exception exc) { + throw new MCRPersistenceException("Unable to update object " + mcrid, exc); + } + } + + @Override + public MCRContent retrieveContent(MCRObjectID mcrid) throws IOException { + MCRContent metadata; + MCRStoredMetadata storedMetadata = retrieveStoredMetadata(mcrid); + if (storedMetadata == null || storedMetadata.isDeleted()) { + return null; + } + metadata = storedMetadata.getMetadata(); + return metadata; + } + + @Override + public MCRContent retrieveContent(MCRObjectID mcrid, String revision) throws IOException { + LOGGER.info("Getting object {} in revision {}", mcrid, revision); + MCRMetadataVersion version = getMetadataVersion(mcrid, Long.parseLong(revision)); + if (version != null) { + MCRContent content = version.retrieve(); + try { + Document doc = content.asXML(); + doc.getRootElement().setAttribute("rev", version.getRevision()); + return new MCRJDOMContent(doc); + } catch (JDOMException e) { + throw new MCRPersistenceException("Could not parse XML from default store", e); + } + } + return null; + } + + /** + * Returns the {@link MCRMetadataVersion} of the given id and revision. + * + * @param mcrId + * the id of the object to be retrieved + * @param rev + * the revision to be returned, specify -1 if you want to + * retrieve the latest revision (includes deleted objects also) + * @return a {@link MCRMetadataVersion} representing the {@link MCRObject} of the + * given revision or null if there is no such object + * with the given revision + * @throws IOException version metadata couldn't be retrieved due an i/o error + */ + private MCRMetadataVersion getMetadataVersion(MCRObjectID mcrId, long rev) throws IOException { + MCRVersionedMetadata versionedMetaData = getVersionedMetaData(mcrId); + if (versionedMetaData == null) { + return null; + } + return versionedMetaData.getRevision(rev); + } + + @Override + public List listRevisions(MCRObjectID id) throws IOException { + MCRVersionedMetadata vm = getVersionedMetaData(id); + if (vm == null) { + return null; + } + return vm.listVersions(); + } + + private MCRVersionedMetadata getVersionedMetaData(MCRObjectID id) throws IOException { + if (id == null) { + return null; + } + MCRMetadataStore metadataStore = getStore(id, true); + if (!(metadataStore instanceof MCRVersioningMetadataStore verStore)) { + return null; + } + return verStore.retrieve(id.getNumberAsInteger()); + } + + /** + * Retrieves stored metadata xml as IFS2 metadata object. + * + * @param mcrid the MCRObjectID + */ + private MCRStoredMetadata retrieveStoredMetadata(MCRObjectID mcrid) throws IOException { + return getStore(mcrid, true).retrieve(mcrid.getNumberAsInteger()); + } + + @Override + public int getHighestStoredID(String project, String type) { + MCRMetadataStore store; + try { + store = getStore(project, type, true); + } catch (MCRPersistenceException persistenceException) { + // store does not exists -> return 0 + return 0; + } + int highestStoredID = store.getHighestStoredID(); + //fixes MCR-1534 (IDs once deleted should never be used again) + return Math.max(highestStoredID, MCRMetadataHistoryManager.getHighestStoredID(project, type) + .map(MCRObjectID::getNumberAsInteger) + .orElse(0)); + } + + @Override + public boolean exists(MCRObjectID mcrid) throws MCRPersistenceException { + try { + if (mcrid == null) { + return false; + } + MCRMetadataStore store; + try { + store = getStore(mcrid, true); + } catch (MCRPersistenceException persistenceException) { + // the store couldn't be retrieved, the object does not exists + return false; + } + return store.exists(mcrid.getNumberAsInteger()); + } catch (Exception exc) { + throw new MCRPersistenceException("Unable to check if object exists " + mcrid, exc); + } + } + + @Override + public List listIDsForBase(String base) { + MCRMetadataStore store; + try { + store = getStore(base, true); + } catch (MCRPersistenceException e) { + LOGGER.warn("Store for '{}' does not exist.", base); + return Collections.emptyList(); + } + + List list = new ArrayList<>(); + Iterator it = store.listIDs(MCRStore.ASCENDING); + String[] idParts = MCRObjectID.getIDParts(base); + while (it.hasNext()) { + list.add(MCRObjectID.formatID(idParts[0], idParts[1], it.next())); + } + return list; + } + + @Override + public List listIDsOfType(String type) { + try (Stream streamBasePath = list(basePath)) { + return streamBasePath.flatMap(projectPath -> { + final String project = projectPath.getFileName().toString(); + return list(projectPath).flatMap(typePath -> { + if (type.equals(typePath.getFileName().toString())) { + final String base = getStoryKey(project, type); + return listIDsForBase(base).stream(); + } + return Stream.empty(); + }); + }).collect(Collectors.toList()); + } + } + + @Override + public List listIDs() { + try (Stream streamBasePath = list(basePath)) { + return streamBasePath.flatMap(projectPath -> { + final String project = projectPath.getFileName().toString(); + return list(projectPath).flatMap(typePath -> { + final String type = typePath.getFileName().toString(); + final String base = getStoryKey(project, type); + return listIDsForBase(base).stream(); + }); + }).collect(Collectors.toList()); + } + } + + @Override + public Collection getObjectTypes() { + try (Stream streamBasePath = list(basePath)) { + return streamBasePath.flatMap(this::list) + .map(Path::getFileName) + .map(Path::toString) + .filter(MCRObjectID::isValidType) + .distinct() + .collect(Collectors.toSet()); + } + } + + @Override + public Collection getObjectBaseIds() { + try (Stream streamBasePath = list(basePath)) { + return streamBasePath.flatMap(this::list) + .filter(p -> MCRObjectID.isValidType(p.getFileName().toString())) + .map(p -> p.getParent().getFileName() + "_" + p.getFileName()) + .collect(Collectors.toSet()); + } + } + + /** + * Returns the entries of the given path. Throws a MCRException if an I/O-Exceptions occur. + * + * @return stream of project directories + */ + private Stream list(Path path) { + try { + return Files.list(path); + } catch (IOException ioException) { + throw new MCRPersistenceException( + "unable to list files of IFS2 metadata directory " + path.toAbsolutePath(), ioException); + } + } + + @Override + public List retrieveObjectDates(List ids) throws IOException { + List objidlist = new ArrayList<>(ids.size()); + for (String id : ids) { + MCRStoredMetadata sm = this.retrieveStoredMetadata(MCRObjectID.getInstance(id)); + objidlist.add(new MCRObjectIDFileSystemDate(sm, id)); + } + return objidlist; + } + + @Override + public long getLastModified(MCRObjectID id) throws IOException { + MCRMetadataStore store = getStore(id, true); + MCRStoredMetadata metadata = store.retrieve(id.getNumberAsInteger()); + if (metadata != null) { + return metadata.getLastModified().getTime(); + } + return -1; + } + + @Override + public MCRCache.ModifiedHandle getLastModifiedHandle(final MCRObjectID id, final long expire, TimeUnit unit) { + return new StoreModifiedHandle(this, id, expire, unit); + } + + private static final class StoreModifiedHandle implements MCRCache.ModifiedHandle { + private final MCRDefaultXMLMetadataManager mm; + + private final long expire; + + private final MCRObjectID id; + + private StoreModifiedHandle(MCRDefaultXMLMetadataManager mm, MCRObjectID id, long time, TimeUnit unit) { + this.mm = mm; + this.expire = unit.toMillis(time); + this.id = id; + } + + @Override + public long getCheckPeriod() { + return expire; + } + + @Override + public long getLastModified() throws IOException { + return mm.getLastModified(id); + } + } +} diff --git a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultXMLMetadataManagerAdapter.java b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultXMLMetadataManagerAdapter.java index 5580abb80a..3f75fbd486 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultXMLMetadataManagerAdapter.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultXMLMetadataManagerAdapter.java @@ -18,55 +18,6 @@ package org.mycore.datamodel.common; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jdom2.Document; -import org.jdom2.JDOMException; -import org.mycore.common.MCRCache; -import org.mycore.common.MCRPersistenceException; -import org.mycore.common.config.MCRConfiguration2; -import org.mycore.common.config.MCRConfigurationBase; -import org.mycore.common.config.MCRConfigurationException; -import org.mycore.common.content.MCRContent; -import org.mycore.common.content.MCRJDOMContent; -import org.mycore.datamodel.ifs2.MCRMetadataStore; -import org.mycore.datamodel.ifs2.MCRMetadataVersion; -import org.mycore.datamodel.ifs2.MCRObjectIDFileSystemDate; -import org.mycore.datamodel.ifs2.MCRStore; -import org.mycore.datamodel.ifs2.MCRStoreCenter; -import org.mycore.datamodel.ifs2.MCRStoreManager; -import org.mycore.datamodel.ifs2.MCRStoredMetadata; -import org.mycore.datamodel.ifs2.MCRVersionedMetadata; -import org.mycore.datamodel.ifs2.MCRVersioningMetadataStore; -import org.mycore.datamodel.metadata.MCRDerivate; -import org.mycore.datamodel.metadata.MCRObject; -import org.mycore.datamodel.metadata.MCRObjectID; -import org.mycore.datamodel.metadata.history.MCRMetadataHistoryManager; - /** * Manages persistence of MCRObject and MCRDerivate xml metadata. * Provides methods to create, retrieve, update and delete object metadata @@ -100,566 +51,10 @@ * @author Frank Lützenkirchen * @author Jens Kupferschmidt * @author Thomas Scheffler (yagee) + * + * @deprecated Use {@link MCRDefaultXMLMetadataManager} instead. */ -public class MCRDefaultXMLMetadataManagerAdapter implements MCRXMLMetadataManagerAdapter { - - private static final Logger LOGGER = LogManager.getLogger(); - - private static final String DEFAULT_SVN_DIRECTORY_NAME = "versions-metadata"; - - private final Set createdStores; - - /** - * The default IFS2 Metadata store class to use, set by MCR.Metadata.Store.DefaultClass - */ - @SuppressWarnings("rawtypes") - private Class defaultClass; - - /** - * The default subdirectory slot layout for IFS2 metadata store, is 4-2-2 for 8-digit IDs, - * that means DocPortal_document_0000001 will be stored in the file - * DocPortal/document/0000/00/DocPortal_document_00000001.xml - */ - private String defaultLayout; - - /** - * The base directory for all IFS2 metadata stores used, set by MCR.Metadata.Store.BaseDir - */ - private Path basePath; - - /** - * The local base directory for IFS2 versioned metadata using SVN, set URI by MCR.Metadata.Store.SVNBase - */ - private Path svnPath; - - /** - * The local file:// uri of all SVN versioned metadata, set URI by MCR.Metadata.Store.SVNBase - */ - private URI svnBase; - - public MCRDefaultXMLMetadataManagerAdapter() { - this.createdStores = new HashSet<>(); - reload(); - } - - @Override - public synchronized void reload() { - String pattern = MCRConfiguration2.getStringOrThrow("MCR.Metadata.ObjectID.NumberPattern"); - defaultLayout = pattern.length() - 4 + "-2-2"; - - String base = MCRConfiguration2.getStringOrThrow("MCR.Metadata.Store.BaseDir"); - basePath = Paths.get(base); - checkPath(basePath, "base"); - - defaultClass = MCRConfiguration2.getClass("MCR.Metadata.Store.DefaultClass") - .orElse(MCRVersioningMetadataStore.class); - if (MCRVersioningMetadataStore.class.isAssignableFrom(defaultClass)) { - Optional svnBaseOpt = MCRConfiguration2.getString("MCR.Metadata.Store.SVNBase"); - if (svnBaseOpt.isEmpty()) { - svnPath = Paths.get(MCRConfiguration2.getStringOrThrow("MCR.datadir")) - .resolve(DEFAULT_SVN_DIRECTORY_NAME); - checkPath(svnPath, "svn"); - svnBase = svnPath.toUri(); - } else { - try { - String svnBaseValue = svnBaseOpt.get(); - if (!svnBaseValue.endsWith("/")) { - svnBaseValue += '/'; - } - svnBase = new URI(svnBaseValue); - LOGGER.info("SVN Base: {}", svnBase); - if (svnBase.getScheme() == null) { - String workingDirectory = (new File(".")).getAbsolutePath(); - URI root = new File(MCRConfiguration2.getString("MCR.datadir").orElse(workingDirectory)) - .toURI(); - URI resolved = root.resolve(svnBase); - LOGGER.warn("Resolved {} to {}", svnBase, resolved); - svnBase = resolved; - } - } catch (URISyntaxException ex) { - String msg = "Syntax error in MCR.Metadata.Store.SVNBase property: " + svnBase; - throw new MCRConfigurationException(msg, ex); - } - if (svnBase.getScheme().equals("file")) { - svnPath = Paths.get(svnBase); - checkPath(svnPath, "svn"); - } - } - } - closeCreatedStores(); - } - - private synchronized void closeCreatedStores() { - for (String storeId : createdStores) { - MCRStoreCenter.getInstance().removeStore(storeId); - } - createdStores.clear(); - } - - /** - * Checks the directory configured exists and is readable and writable, or creates it - * if it does not exist yet. - * - * @param path the path to check - * @param type metadata store type - */ - @SuppressWarnings("PMD.AvoidDuplicateLiterals") - private void checkPath(Path path, String type) { - if (!Files.exists(path)) { - try { - if (!Files.exists(Files.createDirectories(path))) { - throw new MCRConfigurationException( - "The metadata store " + type + " directory " + path.toAbsolutePath() + " does not exist."); - } - } catch (Exception ex) { - String msg = "Exception while creating metadata store " + type + " directory " + path.toAbsolutePath(); - throw new MCRConfigurationException(msg, ex); - } - } else { - if (!Files.isDirectory(path)) { - throw new MCRConfigurationException( - "Metadata store " + type + " " + path.toAbsolutePath() + " is a file, not a directory"); - } - if (!Files.isReadable(path)) { - throw new MCRConfigurationException( - "Metadata store " + type + " directory " + path.toAbsolutePath() + " is not readable"); - } - if (!Files.isWritable(path)) { - throw new MCRConfigurationException( - "Metadata store " + type + " directory " + path.toAbsolutePath() + " is not writeable"); - } - } - } - - /** - * Returns IFS2 MCRMetadataStore for the given MCRObjectID base, which is {project}_{type} - * - * @param base the MCRObjectID base, e.g. DocPortal_document - */ - private MCRMetadataStore getStore(String base) { - String[] split = base.split("_"); - return getStore(split[0], split[1], false); - } - - /** - * Returns IFS2 MCRMetadataStore for the given MCRObjectID base, which is {project}_{type} - * - * @param base the MCRObjectID base, e.g. DocPortal_document - * @param readOnly If readOnly, the store will not be created if it does not exist yet. Instead an exception - * is thrown. - * @return the metadata store - */ - private MCRMetadataStore getStore(String base, boolean readOnly) { - String[] split = base.split("_"); - return getStore(split[0], split[1], readOnly); - } - - /** - * Returns IFS2 MCRMetadataStore used to store metadata of the given MCRObjectID - * - * @param mcrid the mycore object identifier - * @param readOnly If readOnly, the store will not be created if it does not exist yet. Instead an exception - * is thrown. - * @return the metadata store - */ - private MCRMetadataStore getStore(MCRObjectID mcrid, boolean readOnly) { - return getStore(mcrid.getProjectId(), mcrid.getTypeId(), readOnly); - } - - /** - * Returns IFS2 MCRMetadataStore for the given project and object type - * - * @param project the project, e.g. DocPortal - * @param type the object type, e.g. document - * @param readOnly if readOnly, this method will throw an exception if the store does not exist's yet - * @return the metadata store - */ - private MCRMetadataStore getStore(String project, String type, boolean readOnly) { - String projectType = getStoryKey(project, type); - String prefix = "MCR.IFS2.Store." + projectType + "."; - String forceXML = MCRConfiguration2.getString(prefix + "ForceXML").orElse(null); - if (forceXML == null) { - synchronized (this) { - forceXML = MCRConfiguration2.getString(prefix + "ForceXML").orElse(null); - if (forceXML == null) { - try { - setupStore(project, type, prefix, readOnly); - } catch (ReflectiveOperationException e) { - throw new MCRPersistenceException( - new MessageFormat("Could not instantiate store for project {0} and object type {1}.", - Locale.ROOT).format(new Object[] { project, type }), - e); - } - } - } - } - MCRMetadataStore store = MCRStoreManager.getStore(projectType); - if (store == null) { - throw new MCRPersistenceException( - new MessageFormat("Metadata store for project {0} and object type {1} is unconfigured.", Locale.ROOT) - .format(new Object[] { project, type })); - } - return store; - } - - @Override - public void verifyStore(String base) { - MCRMetadataStore store = getStore(base); - if (store instanceof MCRVersioningMetadataStore versioningMetadataStore) { - LOGGER.info("Verifying SVN history of {}.", base); - versioningMetadataStore.verify(); - } else { - LOGGER.warn("Cannot verify unversioned store {}!", base); - } - } - - @SuppressWarnings("unchecked") - private void setupStore(String project, String objectType, String configPrefix, boolean readOnly) - throws ReflectiveOperationException { - String baseID = getStoryKey(project, objectType); - Class clazz = MCRConfiguration2.getClass(configPrefix + "Class") - .orElseGet(() -> { - MCRConfiguration2.set(configPrefix + "Class", defaultClass.getName()); - return defaultClass; - }); - if (MCRVersioningMetadataStore.class.isAssignableFrom(clazz)) { - String property = configPrefix + "SVNRepositoryURL"; - String svnURL = MCRConfiguration2.getString(property).orElse(null); - if (svnURL == null) { - String relativeURI = new MessageFormat("{0}/{1}/", Locale.ROOT) - .format(new Object[] { project, objectType }); - URI repURI = svnBase.resolve(relativeURI); - LOGGER.info("Resolved {} to {} for {}", () -> relativeURI, repURI::toASCIIString, () -> property); - MCRConfiguration2.set(property, repURI.toASCIIString()); - checkAndCreateDirectory(svnPath.resolve(project), project, objectType, configPrefix, readOnly); - } - } - - Path typePath = basePath.resolve(project).resolve(objectType); - checkAndCreateDirectory(typePath, project, objectType, configPrefix, readOnly); - - String slotLayout = MCRConfiguration2.getString(configPrefix + "SlotLayout").orElse(null); - if (slotLayout == null) { - MCRConfiguration2.set(configPrefix + "SlotLayout", defaultLayout); - } - MCRConfiguration2.set(configPrefix + "BaseDir", typePath.toAbsolutePath().toString()); - MCRConfiguration2.set(configPrefix + "ForceXML", String.valueOf(true)); - String value = Objects.equals(objectType, MCRDerivate.OBJECT_TYPE) ? - MCRDerivate.ROOT_NAME : MCRObject.ROOT_NAME; - MCRConfiguration2.set(configPrefix + "ForceDocType", value); - createdStores.add(baseID); - MCRStoreManager.createStore(baseID, clazz); - } - - private void checkAndCreateDirectory(Path path, String project, String objectType, String configPrefix, - boolean readOnly) { - if (Files.exists(path)) { - return; - } - if (readOnly) { - throw new MCRPersistenceException(String.format(Locale.ENGLISH, - "Path does not exists ''%s'' to set up store for project ''%s'' and objectType ''%s'' " - + "and config prefix ''%s''. We are not willing to create it for an read only operation.", - path.toAbsolutePath(), project, objectType, configPrefix)); - } - try { - if (!Files.exists(Files.createDirectories(path))) { - throw new FileNotFoundException(path.toAbsolutePath() + " does not exists."); - } - } catch (Exception e) { - throw new MCRPersistenceException(String.format(Locale.ENGLISH, - "Couldn'e create directory ''%s'' to set up store for project ''%s'' and objectType ''%s'' " - + "and config prefix ''%s''", - path.toAbsolutePath(), project, objectType, configPrefix), e); - } - } - - private String getStoryKey(String project, String objectType) { - return project + "_" + objectType; - } - - @Override - public void create(MCRObjectID mcrid, MCRContent xml, Date lastModified) - throws MCRPersistenceException { - try { - MCRStoredMetadata sm = getStore(mcrid, false).create(xml, mcrid.getNumberAsInteger()); - sm.setLastModified(lastModified); - MCRConfigurationBase.systemModified(); - } catch (Exception exc) { - throw new MCRPersistenceException("Error while storing object: " + mcrid, exc); - } - } - - @Override - public void delete(MCRObjectID mcrid) throws MCRPersistenceException { - try { - getStore(mcrid, true).delete(mcrid.getNumberAsInteger()); - MCRConfigurationBase.systemModified(); - } catch (Exception exc) { - throw new MCRPersistenceException("Error while deleting object: " + mcrid, exc); - } - } - - @Override - public void update(MCRObjectID mcrid, MCRContent xml, Date lastModified) - throws MCRPersistenceException { - if (!exists(mcrid)) { - throw new MCRPersistenceException("Object to update does not exist: " + mcrid); - } - try { - MCRStoredMetadata sm = getStore(mcrid, false).retrieve(mcrid.getNumberAsInteger()); - sm.update(xml); - sm.setLastModified(lastModified); - MCRConfigurationBase.systemModified(); - } catch (Exception exc) { - throw new MCRPersistenceException("Unable to update object " + mcrid, exc); - } - } - - @Override - public MCRContent retrieveContent(MCRObjectID mcrid) throws IOException { - MCRContent metadata; - MCRStoredMetadata storedMetadata = retrieveStoredMetadata(mcrid); - if (storedMetadata == null || storedMetadata.isDeleted()) { - return null; - } - metadata = storedMetadata.getMetadata(); - return metadata; - } - - @Override - public MCRContent retrieveContent(MCRObjectID mcrid, String revision) throws IOException { - LOGGER.info("Getting object {} in revision {}", mcrid, revision); - MCRMetadataVersion version = getMetadataVersion(mcrid, Long.parseLong(revision)); - if (version != null) { - MCRContent content = version.retrieve(); - try { - Document doc = content.asXML(); - doc.getRootElement().setAttribute("rev", version.getRevision()); - return new MCRJDOMContent(doc); - } catch (JDOMException e) { - throw new MCRPersistenceException("Could not parse XML from default store", e); - } - } - return null; - } - - /** - * Returns the {@link MCRMetadataVersion} of the given id and revision. - * - * @param mcrId - * the id of the object to be retrieved - * @param rev - * the revision to be returned, specify -1 if you want to - * retrieve the latest revision (includes deleted objects also) - * @return a {@link MCRMetadataVersion} representing the {@link MCRObject} of the - * given revision or null if there is no such object - * with the given revision - * @throws IOException version metadata couldn't be retrieved due an i/o error - */ - private MCRMetadataVersion getMetadataVersion(MCRObjectID mcrId, long rev) throws IOException { - MCRVersionedMetadata versionedMetaData = getVersionedMetaData(mcrId); - if (versionedMetaData == null) { - return null; - } - return versionedMetaData.getRevision(rev); - } - - @Override - public List listRevisions(MCRObjectID id) throws IOException { - MCRVersionedMetadata vm = getVersionedMetaData(id); - if (vm == null) { - return null; - } - return vm.listVersions(); - } - - private MCRVersionedMetadata getVersionedMetaData(MCRObjectID id) throws IOException { - if (id == null) { - return null; - } - MCRMetadataStore metadataStore = getStore(id, true); - if (!(metadataStore instanceof MCRVersioningMetadataStore verStore)) { - return null; - } - return verStore.retrieve(id.getNumberAsInteger()); - } - - /** - * Retrieves stored metadata xml as IFS2 metadata object. - * - * @param mcrid the MCRObjectID - */ - private MCRStoredMetadata retrieveStoredMetadata(MCRObjectID mcrid) throws IOException { - return getStore(mcrid, true).retrieve(mcrid.getNumberAsInteger()); - } - - @Override - public int getHighestStoredID(String project, String type) { - MCRMetadataStore store; - try { - store = getStore(project, type, true); - } catch (MCRPersistenceException persistenceException) { - // store does not exists -> return 0 - return 0; - } - int highestStoredID = store.getHighestStoredID(); - //fixes MCR-1534 (IDs once deleted should never be used again) - return Math.max(highestStoredID, MCRMetadataHistoryManager.getHighestStoredID(project, type) - .map(MCRObjectID::getNumberAsInteger) - .orElse(0)); - } - - @Override - public boolean exists(MCRObjectID mcrid) throws MCRPersistenceException { - try { - if (mcrid == null) { - return false; - } - MCRMetadataStore store; - try { - store = getStore(mcrid, true); - } catch (MCRPersistenceException persistenceException) { - // the store couldn't be retrieved, the object does not exists - return false; - } - return store.exists(mcrid.getNumberAsInteger()); - } catch (Exception exc) { - throw new MCRPersistenceException("Unable to check if object exists " + mcrid, exc); - } - } - - @Override - public List listIDsForBase(String base) { - MCRMetadataStore store; - try { - store = getStore(base, true); - } catch (MCRPersistenceException e) { - LOGGER.warn("Store for '{}' does not exist.", base); - return Collections.emptyList(); - } - - List list = new ArrayList<>(); - Iterator it = store.listIDs(MCRStore.ASCENDING); - String[] idParts = MCRObjectID.getIDParts(base); - while (it.hasNext()) { - list.add(MCRObjectID.formatID(idParts[0], idParts[1], it.next())); - } - return list; - } - - @Override - public List listIDsOfType(String type) { - try (Stream streamBasePath = list(basePath)) { - return streamBasePath.flatMap(projectPath -> { - final String project = projectPath.getFileName().toString(); - return list(projectPath).flatMap(typePath -> { - if (type.equals(typePath.getFileName().toString())) { - final String base = getStoryKey(project, type); - return listIDsForBase(base).stream(); - } - return Stream.empty(); - }); - }).collect(Collectors.toList()); - } - } - - @Override - public List listIDs() { - try (Stream streamBasePath = list(basePath)) { - return streamBasePath.flatMap(projectPath -> { - final String project = projectPath.getFileName().toString(); - return list(projectPath).flatMap(typePath -> { - final String type = typePath.getFileName().toString(); - final String base = getStoryKey(project, type); - return listIDsForBase(base).stream(); - }); - }).collect(Collectors.toList()); - } - } - - @Override - public Collection getObjectTypes() { - try (Stream streamBasePath = list(basePath)) { - return streamBasePath.flatMap(this::list) - .map(Path::getFileName) - .map(Path::toString) - .filter(MCRObjectID::isValidType) - .distinct() - .collect(Collectors.toSet()); - } - } - - @Override - public Collection getObjectBaseIds() { - try (Stream streamBasePath = list(basePath)) { - return streamBasePath.flatMap(this::list) - .filter(p -> MCRObjectID.isValidType(p.getFileName().toString())) - .map(p -> p.getParent().getFileName() + "_" + p.getFileName()) - .collect(Collectors.toSet()); - } - } - - /** - * Returns the entries of the given path. Throws a MCRException if an I/O-Exceptions occur. - * - * @return stream of project directories - */ - private Stream list(Path path) { - try { - return Files.list(path); - } catch (IOException ioException) { - throw new MCRPersistenceException( - "unable to list files of IFS2 metadata directory " + path.toAbsolutePath(), ioException); - } - } - - @Override - public List retrieveObjectDates(List ids) throws IOException { - List objidlist = new ArrayList<>(ids.size()); - for (String id : ids) { - MCRStoredMetadata sm = this.retrieveStoredMetadata(MCRObjectID.getInstance(id)); - objidlist.add(new MCRObjectIDFileSystemDate(sm, id)); - } - return objidlist; - } - - @Override - public long getLastModified(MCRObjectID id) throws IOException { - MCRMetadataStore store = getStore(id, true); - MCRStoredMetadata metadata = store.retrieve(id.getNumberAsInteger()); - if (metadata != null) { - return metadata.getLastModified().getTime(); - } - return -1; - } - - @Override - public MCRCache.ModifiedHandle getLastModifiedHandle(final MCRObjectID id, final long expire, TimeUnit unit) { - return new StoreModifiedHandle(this, id, expire, unit); - } - - private static final class StoreModifiedHandle implements MCRCache.ModifiedHandle { - private final MCRDefaultXMLMetadataManagerAdapter mm; - - private final long expire; - - private final MCRObjectID id; - - private StoreModifiedHandle(MCRDefaultXMLMetadataManagerAdapter mm, MCRObjectID id, long time, TimeUnit unit) { - this.mm = mm; - this.expire = unit.toMillis(time); - this.id = id; - } - - @Override - public long getCheckPeriod() { - return expire; - } +@Deprecated(forRemoval = true) +public class MCRDefaultXMLMetadataManagerAdapter extends MCRDefaultXMLMetadataManager { - @Override - public long getLastModified() throws IOException { - return mm.getLastModified(id); - } - } } diff --git a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManager.java b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManager.java index 987b1167a6..b1246f65cc 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManager.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManager.java @@ -33,20 +33,51 @@ import org.mycore.common.content.MCRByteContent; import org.mycore.common.content.MCRContent; import org.mycore.common.content.MCRJDOMContent; +import org.mycore.datamodel.ifs2.MCRMetadataVersion; +import org.mycore.datamodel.metadata.MCRObject; import org.mycore.datamodel.metadata.MCRObjectID; /** - * Provides an adapter to communicate with the configured {@link MCRXMLMetadataManagerAdapter} implementation. + * Provides an interface for persistence managers of MCRObject and MCRDerivate xml + * metadata to extend, with methods to perform CRUD operations on object metadata. + *

+ * The default xml metadata manager is {@link MCRDefaultXMLMetadataManager}. If you wish to use + * another manager implementation instead, change the following property accordingly: + *

+ * MCR.Metadata.Manager.Class=org.mycore.datamodel.common.MCRDefaultXMLMetadataManager + *

+ * Xml metadata managers have a default class they will instantiate for every store. + * If you wish to use a different default class, change the following property + * accordingly. For example, when using the MCRDefaultXMLMetadataManager: + *

+ * MCR.Metadata.Store.DefaultClass=org.mycore.datamodel.ifs2.MCRVersioningMetadataStore + *

+ * The following directory will be used by xml metadata managers to keep up-to-date + * store contents in. This directory will be created if it does not exist yet. + *

+ * MCR.Metadata.Store.BaseDir=/path/to/metadata/dir + *

+ * For each project and type, subdirectories will be created below this path, + * for example %MCR.Metadata.Store.BaseDir%/DocPortal/document/. + *

+ * If an SVN-based store is configured, then the following property will be used to + * store and manage local SVN repositories: + *

+ * MCR.Metadata.Store.SVNBase=file:///path/to/local/svndir/ + *

+ * It is also possible to change individual properties per project and object type + * and overwrite the defaults, for example + *

+ * MCR.IFS2.Store.Class=org.mycore.datamodel.ifs2.MCRVersioningMetadataStore + * MCR.IFS2.Store.SVNRepositoryURL=file:///use/other/location/for/document/versions/ + * MCR.IFS2.Store.SlotLayout=2-2-2-2 + *

+ * See documentation of MCRStore, MCRMetadataStore and the MCRXMLMetadataManager + * extensions (e.g. MCRDefaultXMLMetadataManager) for details. * * @author Christoph Neidahl (OPNA2608) */ -public final class MCRXMLMetadataManager { - - private final MCRXMLMetadataManagerAdapter adapter = MCRConfiguration2.getInstanceOfOrThrow( - MCRXMLMetadataManagerAdapter.class, "MCR.Metadata.Manager.Class"); - - private MCRXMLMetadataManager() { - } +public interface MCRXMLMetadataManager { /** * Returns the singleton instance of {@link MCRXMLMetadataManager}. Reads the property @@ -55,183 +86,176 @@ private MCRXMLMetadataManager() { * * @return the XML metadata manager */ - public static synchronized MCRXMLMetadataManager getInstance() { + static MCRXMLMetadataManager getInstance() { return LazyInstanceHolder.SINGLETON_INSTANCE; } - /* - * Delegations to IMPLEMENTATION - */ - /** - * Delegation, see linked method for relevant documentation. - * - * @see MCRXMLMetadataManagerAdapter#reload() + * Reads configuration properties, checks and creates base directories and builds the singleton. */ - public void reload() { - adapter.reload(); - } + void reload(); /** - * Delegation, see linked method for relevant documentation. + * Try to validate a store. * - * @see MCRXMLMetadataManagerAdapter#verifyStore(String) + * @param base The base ID of a to-be-validated store */ - public void verifyStore(String base) { - adapter.verifyStore(base); - } + void verifyStore(String base); /** - * Delegation, see linked method for relevant documentation. + * Stores metadata of a new MCRObject in the persistent store. * - * @see MCRXMLMetadataManagerAdapter#create(MCRObjectID, MCRContent, Date) + * @param mcrid the MCRObjectID + * @param xml the xml metadata of the MCRObject + * @param lastModified the date of last modification to set + * @throws MCRPersistenceException the object couldn't be created due persistence problems */ - public void create(MCRObjectID mcrid, MCRContent xml, Date lastModified) - throws MCRPersistenceException { - adapter.create(mcrid, xml, lastModified); - } + void create(MCRObjectID mcrid, MCRContent xml, Date lastModified) + throws MCRPersistenceException; /** - * Delegation, see linked method for relevant documentation. + * Delete metadata in store. * - * @see MCRXMLMetadataManagerAdapter#delete(MCRObjectID) + * @param mcrid the MCRObjectID + * @throws MCRPersistenceException if an error occurs during the deletion */ - public void delete(MCRObjectID mcrid) throws MCRPersistenceException { - adapter.delete(mcrid); - } + void delete(MCRObjectID mcrid) throws MCRPersistenceException; /** - * Delegation, see linked method for relevant documentation. + * Updates metadata of existing MCRObject in the persistent store. * - * @see MCRXMLMetadataManagerAdapter#update(MCRObjectID, MCRContent, Date) + * @param mcrid the MCRObjectID + * @param xml the xml metadata of the MCRObject + * @param lastModified the date of last modification to set */ - public void update(MCRObjectID mcrid, MCRContent xml, Date lastModified) - throws MCRPersistenceException { - adapter.update(mcrid, xml, lastModified); - } + void update(MCRObjectID mcrid, MCRContent xml, Date lastModified) + throws MCRPersistenceException; /** - * Delegation, see linked method for relevant documentation. + * Retrieves the (latest) content of a metadata object. * - * @see MCRXMLMetadataManagerAdapter#retrieveContent(MCRObjectID) + * @param mcrid + * the id of the object to be retrieved + * @return a {@link MCRContent} representing the {@link MCRObject} or + * null if there is no such object */ - public MCRContent retrieveContent(MCRObjectID mcrid) throws IOException { - return adapter.retrieveContent(mcrid); - } + MCRContent retrieveContent(MCRObjectID mcrid) throws IOException; /** - * Delegation, see linked method for relevant documentation. + * Retrieves the content of a specific revision of a metadata object. * - * @see MCRXMLMetadataManagerAdapter#retrieveContent(MCRObjectID, String) + * @param mcrid + * the id of the object to be retrieved + * @param revision + * the revision to be returned, specify -1 if you want to + * retrieve the latest revision (includes deleted objects also) + * @return a {@link MCRContent} representing the {@link MCRObject} of the + * given revision or null if there is no such object + * with the given revision */ - public MCRContent retrieveContent(MCRObjectID mcrid, String revision) throws IOException { - return adapter.retrieveContent(mcrid, revision); - } + MCRContent retrieveContent(MCRObjectID mcrid, String revision) throws IOException; /** - * Delegation, see linked method for relevant documentation. + * Lists all versions of this metadata object available in the + * subversion repository. * - * @see MCRXMLMetadataManagerAdapter#listRevisions(MCRObjectID) + * @param id + * the id of the object to be retrieved + * @return {@link List} with all {@link MCRMetadataVersion} of + * the given object or null if the id is null or the metadata + * store doesn't support versioning */ - public List> listRevisions(MCRObjectID id) throws IOException { - return adapter.listRevisions(id); - } + List> listRevisions(MCRObjectID id) throws IOException; /** - * Delegation, see linked method for relevant documentation. + * This method returns the highest stored ID number for a given MCRObjectID + * base, or 0 if no object is stored for this type and project. * - * @see MCRXMLMetadataManagerAdapter#getHighestStoredID(String, String) + * @param project + * the project ID part of the MCRObjectID base + * @param type + * the type ID part of the MCRObjectID base + * @exception MCRPersistenceException + * if a persistence problem is occurred + * @return the highest stored ID number as a String */ - public int getHighestStoredID(String project, String type) { - return adapter.getHighestStoredID(project, type); - } + int getHighestStoredID(String project, String type); - public int getHighestStoredID(String base) { - return adapter.getHighestStoredID( - base.substring(0, base.indexOf('_')), - base.substring(base.indexOf('_') + 1)); + default int getHighestStoredID(String base) { + return getHighestStoredID( + base.substring(0, base.indexOf('_')), + base.substring(base.indexOf('_') + 1)); } - + /** - * Delegation, see linked method for relevant documentation. + * Checks if an object with the given MCRObjectID exists in the store. * - * @see MCRXMLMetadataManagerAdapter#exists(MCRObjectID) + * @param mcrid + * the MCRObjectID to check + * @return true if the ID exists, or false if it doesn't + * @throws MCRPersistenceException if an error occurred in the store */ - public boolean exists(MCRObjectID mcrid) throws MCRPersistenceException { - return adapter.exists(mcrid); - } + boolean exists(MCRObjectID mcrid) throws MCRPersistenceException; /** - * Delegation, see linked method for relevant documentation. + * Lists all MCRObjectIDs stored for the given base, which is {project}_{type} * - * @see MCRXMLMetadataManagerAdapter#listIDsForBase(String) + * @param base + * the MCRObjectID base, e.g. DocPortal_document + * @return List of Strings with all MyCoRe identifiers found in the metadata stores for the given base */ - public List listIDsForBase(String base) { - return adapter.listIDsForBase(base); - } + List listIDsForBase(String base); /** - * Delegation, see linked method for relevant documentation. + * Lists all MCRObjectIDs stored for the given object type, for all projects * - * @see MCRXMLMetadataManagerAdapter#listIDsOfType(String) + * @param type + * the MCRObject type, e.g. document + * @return List of Strings with all MyCoRe identifiers found in the metadata store for the given type */ - public List listIDsOfType(String type) { - return adapter.listIDsOfType(type); - } + List listIDsOfType(String type); /** - * Delegation, see linked method for relevant documentation. + * Lists all MCRObjectIDs of all types and projects stored in any metadata store * - * @see MCRXMLMetadataManagerAdapter#listIDs() + * @return List of Strings with all MyCoRe identifiers found in the metadata store */ - public List listIDs() { - return adapter.listIDs(); - } + List listIDs(); /** - * Delegation, see linked method for relevant documentation. + * Returns all stored object types of MCRObjects/MCRDerivates. * - * @see MCRXMLMetadataManagerAdapter#getObjectTypes() + * @return Collection of Strings with all object types + * @see MCRObjectID#getTypeId() */ - public Collection getObjectTypes() { - return adapter.getObjectTypes(); - } + Collection getObjectTypes(); /** - * Delegation, see linked method for relevant documentation. + * Returns all used base ids of MCRObjects/MCRDerivates. * - * @see MCRXMLMetadataManagerAdapter#getObjectBaseIds() + * @return Collection of Strings with all object base identifier + * @see MCRObjectID#getBase() */ - public Collection getObjectBaseIds() { - return adapter.getObjectBaseIds(); - } + Collection getObjectBaseIds(); /** - * Delegation, see linked method for relevant documentation. + * Returns an enhanced list of object ids and their last modified date * - * @see MCRXMLMetadataManagerAdapter#retrieveObjectDates(List) + * @param ids MCRObject ids */ - public List retrieveObjectDates(List ids) throws IOException { - return adapter.retrieveObjectDates(ids); - } + List retrieveObjectDates(List ids) throws IOException; /** - * Delegation, see linked method for relevant documentation. + * Returns the time when the xml data of a MCRObject was last modified. * - * @see MCRXMLMetadataManagerAdapter#getLastModified(MCRObjectID) + * @param id + * the MCRObjectID of an object + * @return the last modification data of the object + * @throws IOException thrown while retrieving the object from the store */ - public long getLastModified(MCRObjectID id) throws IOException { - return adapter.getLastModified(id); - } + long getLastModified(MCRObjectID id) throws IOException; - /** - * Delegation, see linked method for relevant documentation. - * - * @see MCRXMLMetadataManagerAdapter#getLastModifiedHandle(MCRObjectID, long, TimeUnit) - */ - public MCRCache.ModifiedHandle getLastModifiedHandle(MCRObjectID id, long expire, TimeUnit unit) { - return adapter.getLastModifiedHandle(id, expire, unit); - } + MCRCache.ModifiedHandle getLastModifiedHandle(MCRObjectID id, long expire, TimeUnit unit); /* * Redirections/wrappers to delegations @@ -245,7 +269,7 @@ public MCRCache.ModifiedHandle getLastModifiedHandle(MCRObjectID id, long expire * @param lastModified the date of last modification to set * @throws MCRPersistenceException the object couldn't be created due persistence problems */ - public void create(MCRObjectID mcrid, Document xml, Date lastModified) + default void create(MCRObjectID mcrid, Document xml, Date lastModified) throws MCRPersistenceException { create(mcrid, new MCRJDOMContent(xml), lastModified); } @@ -258,11 +282,11 @@ public void create(MCRObjectID mcrid, Document xml, Date lastModified) * @param lastModified the date of last modification to set * @throws MCRPersistenceException the object couldn't be created due persistence problems */ - public void create(MCRObjectID mcrid, byte[] xml, Date lastModified) throws MCRPersistenceException { + default void create(MCRObjectID mcrid, byte[] xml, Date lastModified) throws MCRPersistenceException { create(mcrid, new MCRByteContent(xml, lastModified.getTime()), lastModified); } - public void delete(String mcrid) throws MCRPersistenceException { + default void delete(String mcrid) throws MCRPersistenceException { delete(MCRObjectID.getInstance(mcrid)); } @@ -274,7 +298,7 @@ public void delete(String mcrid) throws MCRPersistenceException { * @param lastModified the date of last modification to set * @throws MCRPersistenceException the object couldn't be updated due persistence problems */ - public void update(MCRObjectID mcrid, Document xml, Date lastModified) + default void update(MCRObjectID mcrid, Document xml, Date lastModified) throws MCRPersistenceException { update(mcrid, new MCRJDOMContent(xml), lastModified); } @@ -287,7 +311,7 @@ public void update(MCRObjectID mcrid, Document xml, Date lastModified) * @param lastModified the date of last modification to set * @throws MCRPersistenceException the object couldn't be created or updated due persistence problems */ - public void createOrUpdate(MCRObjectID mcrid, Document xml, Date lastModified) + default void createOrUpdate(MCRObjectID mcrid, Document xml, Date lastModified) throws MCRPersistenceException { if (exists(mcrid)) { update(mcrid, xml, lastModified); @@ -304,7 +328,7 @@ public void createOrUpdate(MCRObjectID mcrid, Document xml, Date lastModified) * @param lastModified the date of last modification to set * @throws MCRPersistenceException the object couldn't be updated due persistence problems */ - public void update(MCRObjectID mcrid, byte[] xml, Date lastModified) throws MCRPersistenceException { + default void update(MCRObjectID mcrid, byte[] xml, Date lastModified) throws MCRPersistenceException { update(mcrid, new MCRByteContent(xml, lastModified.getTime()), lastModified); } @@ -314,7 +338,7 @@ public void update(MCRObjectID mcrid, byte[] xml, Date lastModified) throws MCRP * @param mcrid the MCRObjectID * @return null if metadata is not present */ - public Document retrieveXML(MCRObjectID mcrid) throws IOException, JDOMException { + default Document retrieveXML(MCRObjectID mcrid) throws IOException, JDOMException { MCRContent metadata = retrieveContent(mcrid); return metadata == null ? null : metadata.asXML(); } @@ -325,7 +349,7 @@ public Document retrieveXML(MCRObjectID mcrid) throws IOException, JDOMException * @param mcrid the MCRObjectID * @return null if metadata is not present */ - public byte[] retrieveBLOB(MCRObjectID mcrid) throws IOException { + default byte[] retrieveBLOB(MCRObjectID mcrid) throws IOException { MCRContent metadata = retrieveContent(mcrid); return metadata == null ? null : metadata.asByteArray(); } @@ -335,7 +359,7 @@ public byte[] retrieveBLOB(MCRObjectID mcrid) throws IOException { * * @return List of {@link MCRObjectIDDate} */ - public List listObjectDates() throws IOException { + default List listObjectDates() throws IOException { return retrieveObjectDates(this.listIDs()); } @@ -344,7 +368,7 @@ public List listObjectDates() throws IOException { * * @param type type of object */ - public List listObjectDates(String type) throws IOException { + default List listObjectDates(String type) throws IOException { return retrieveObjectDates(this.listIDsOfType(type)); } @@ -353,12 +377,13 @@ public List listObjectDates(String type) throws IOException { * * @return Last modification date of the MyCoRe system */ - public long getLastModified() { + default long getLastModified() { return MCRConfigurationBase.getSystemLastModified(); } - private static final class LazyInstanceHolder { - public static final MCRXMLMetadataManager SINGLETON_INSTANCE = new MCRXMLMetadataManager(); + final class LazyInstanceHolder { + public static final MCRXMLMetadataManager SINGLETON_INSTANCE = MCRConfiguration2.getInstanceOfOrThrow( + MCRXMLMetadataManager.class, "MCR.Metadata.Manager.Class"); } } diff --git a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManagerAdapter.java b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManagerAdapter.java index bd0f092322..7108e71178 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManagerAdapter.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManagerAdapter.java @@ -18,24 +18,11 @@ package org.mycore.datamodel.common; -import java.io.IOException; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.mycore.common.MCRCache; -import org.mycore.common.MCRPersistenceException; -import org.mycore.common.content.MCRContent; -import org.mycore.datamodel.ifs2.MCRMetadataVersion; -import org.mycore.datamodel.metadata.MCRObject; -import org.mycore.datamodel.metadata.MCRObjectID; - /** * Provides an abstract class for persistence managers of MCRObject and MCRDerivate xml * metadata to extend, with methods to perform CRUD operations on object metadata. *

- * The default xml metadata manager is {@link MCRDefaultXMLMetadataManagerAdapter}. If you wish to use + * The default xml metadata manager is {@link MCRDefaultXMLMetadataManager}. If you wish to use * another manager implementation instead, change the following property accordingly: *

* MCR.Metadata.Manager.Class=org.mycore.datamodel.common.MCRDefaultXMLMetadataManager @@ -70,167 +57,10 @@ * extensions (e.g. MCRDefaultXMLMetadataManager) for details. * * @author Christoph Neidahl (OPNA2608) + * + * @deprecated Use {@link MCRXMLMetadataManager instead}. */ -public interface MCRXMLMetadataManagerAdapter { - - /** - * Reads configuration properties, checks and creates base directories and builds the singleton. - */ - void reload(); - - /** - * Try to validate a store. - * - * @param base The base ID of a to-be-validated store - */ - void verifyStore(String base); - - /** - * Stores metadata of a new MCRObject in the persistent store. - * - * @param mcrid the MCRObjectID - * @param xml the xml metadata of the MCRObject - * @param lastModified the date of last modification to set - * @throws MCRPersistenceException the object couldn't be created due persistence problems - */ - void create(MCRObjectID mcrid, MCRContent xml, Date lastModified) - throws MCRPersistenceException; - - /** - * Delete metadata in store. - * - * @param mcrid the MCRObjectID - * @throws MCRPersistenceException if an error occurs during the deletion - */ - void delete(MCRObjectID mcrid) throws MCRPersistenceException; - - /** - * Updates metadata of existing MCRObject in the persistent store. - * - * @param mcrid the MCRObjectID - * @param xml the xml metadata of the MCRObject - * @param lastModified the date of last modification to set - */ - void update(MCRObjectID mcrid, MCRContent xml, Date lastModified) - throws MCRPersistenceException; - - /** - * Retrieves the (latest) content of a metadata object. - * - * @param mcrid - * the id of the object to be retrieved - * @return a {@link MCRContent} representing the {@link MCRObject} or - * null if there is no such object - */ - MCRContent retrieveContent(MCRObjectID mcrid) throws IOException; - - /** - * Retrieves the content of a specific revision of a metadata object. - * - * @param mcrid - * the id of the object to be retrieved - * @param revision - * the revision to be returned, specify -1 if you want to - * retrieve the latest revision (includes deleted objects also) - * @return a {@link MCRContent} representing the {@link MCRObject} of the - * given revision or null if there is no such object - * with the given revision - */ - MCRContent retrieveContent(MCRObjectID mcrid, String revision) throws IOException; - - /** - * Lists all versions of this metadata object available in the - * subversion repository. - * - * @param id - * the id of the object to be retrieved - * @return {@link List} with all {@link MCRMetadataVersion} of - * the given object or null if the id is null or the metadata - * store doesn't support versioning - */ - List> listRevisions(MCRObjectID id) throws IOException; - - /** - * This method returns the highest stored ID number for a given MCRObjectID - * base, or 0 if no object is stored for this type and project. - * - * @param project - * the project ID part of the MCRObjectID base - * @param type - * the type ID part of the MCRObjectID base - * @exception MCRPersistenceException - * if a persistence problem is occurred - * @return the highest stored ID number as a String - */ - int getHighestStoredID(String project, String type); - - /** - * Checks if an object with the given MCRObjectID exists in the store. - * - * @param mcrid - * the MCRObjectID to check - * @return true if the ID exists, or false if it doesn't - * @throws MCRPersistenceException if an error occurred in the store - */ - boolean exists(MCRObjectID mcrid) throws MCRPersistenceException; - - /** - * Lists all MCRObjectIDs stored for the given base, which is {project}_{type} - * - * @param base - * the MCRObjectID base, e.g. DocPortal_document - * @return List of Strings with all MyCoRe identifiers found in the metadata stores for the given base - */ - List listIDsForBase(String base); - - /** - * Lists all MCRObjectIDs stored for the given object type, for all projects - * - * @param type - * the MCRObject type, e.g. document - * @return List of Strings with all MyCoRe identifiers found in the metadata store for the given type - */ - List listIDsOfType(String type); - - /** - * Lists all MCRObjectIDs of all types and projects stored in any metadata store - * - * @return List of Strings with all MyCoRe identifiers found in the metadata store - */ - List listIDs(); - - /** - * Returns all stored object types of MCRObjects/MCRDerivates. - * - * @return Collection of Strings with all object types - * @see MCRObjectID#getTypeId() - */ - Collection getObjectTypes(); - - /** - * Returns all used base ids of MCRObjects/MCRDerivates. - * - * @return Collection of Strings with all object base identifier - * @see MCRObjectID#getBase() - */ - Collection getObjectBaseIds(); - - /** - * Returns an enhanced list of object ids and their last modified date - * - * @param ids MCRObject ids - */ - List retrieveObjectDates(List ids) throws IOException; - - /** - * Returns the time when the xml data of a MCRObject was last modified. - * - * @param id - * the MCRObjectID of an object - * @return the last modification data of the object - * @throws IOException thrown while retrieving the object from the store - */ - long getLastModified(MCRObjectID id) throws IOException; +@Deprecated(forRemoval = true) +public interface MCRXMLMetadataManagerAdapter extends MCRXMLMetadataManager { - MCRCache.ModifiedHandle getLastModifiedHandle(MCRObjectID id, long expire, TimeUnit unit); } diff --git a/mycore-base/src/main/resources/config/mycore.properties b/mycore-base/src/main/resources/config/mycore.properties index fd97718947..b1c193c98b 100644 --- a/mycore-base/src/main/resources/config/mycore.properties +++ b/mycore-base/src/main/resources/config/mycore.properties @@ -288,7 +288,7 @@ MCR.Access.Facts.Condition.category=org.mycore.access.facts.condition.fact.MCRCa MCR.IFS2.SyncLastModifiedOnSVNCommit=true # Which metadata manager to use (dictates the available stores) - MCR.Metadata.Manager.Class=org.mycore.datamodel.common.MCRDefaultXMLMetadataManagerAdapter + MCR.Metadata.Manager.Class=org.mycore.datamodel.common.MCRDefaultXMLMetadataManager # Metadata store for derivate XML MCR.IFS2.Store.derivate.Class=org.mycore.datamodel.ifs2.MCRVersioningMetadataStore diff --git a/mycore-base/src/test/java/org/mycore/datamodel/common/MCRXMLMetadataManagerTest.java b/mycore-base/src/test/java/org/mycore/datamodel/common/MCRXMLMetadataManagerTest.java index 1bd330c517..bab790ed55 100644 --- a/mycore-base/src/test/java/org/mycore/datamodel/common/MCRXMLMetadataManagerTest.java +++ b/mycore-base/src/test/java/org/mycore/datamodel/common/MCRXMLMetadataManagerTest.java @@ -61,7 +61,7 @@ @MyCoReTest @ExtendWith(MCRMetadataExtension.class) @MCRTestConfiguration(properties = { - @MCRTestProperty(key = "MCR.Metadata.Manager.Class", classNameOf = MCRDefaultXMLMetadataManagerAdapter.class), + @MCRTestProperty(key = "MCR.Metadata.Manager.Class", classNameOf = MCRDefaultXMLMetadataManager.class), @MCRTestProperty(key = "MCR.Metadata.Type.document", string = "true"), @MCRTestProperty(key = "MCR.Metadata.ObjectID.NumberPattern", string = "00000000") }) diff --git a/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLCommands.java b/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLCommands.java index 8ac54f96eb..9854220a9a 100644 --- a/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLCommands.java +++ b/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLCommands.java @@ -72,7 +72,7 @@ import org.mycore.frontend.cli.annotation.MCRCommandGroup; import org.mycore.ocfl.classification.MCROCFLClassificationTransaction; import org.mycore.ocfl.classification.MCROCFLXMLClassificationManager; -import org.mycore.ocfl.metadata.MCROCFLXMLMetadataManagerAdapter; +import org.mycore.ocfl.metadata.MCROCFLXMLMetadataManager; import org.mycore.ocfl.metadata.migration.MCROCFLMigration; import org.mycore.ocfl.metadata.migration.MCROCFLRevisionPruner; import org.mycore.ocfl.niofs.MCROCFLFileSystemProvider; @@ -133,8 +133,8 @@ protected static void migrateWithPrunersAndRepositoryKeyOrMetadataManager(String MCROCFLMigration migration; if (metadataManagerConfigKey != null && !metadataManagerConfigKey.isEmpty()) { - MCROCFLXMLMetadataManagerAdapter metadataManager = - MCRConfiguration2.getInstanceOf(MCROCFLXMLMetadataManagerAdapter.class, metadataManagerConfigKey) + MCROCFLXMLMetadataManager metadataManager = + MCRConfiguration2.getInstanceOf(MCROCFLXMLMetadataManager.class, metadataManagerConfigKey) .orElseThrow(() -> MCRConfiguration2.createConfigurationException(metadataManagerConfigKey)); migration = new MCROCFLMigration(null, prunerList, metadataManager); } else if (repository != null && !repository.isEmpty()) { @@ -326,7 +326,7 @@ public static void writeClassToDb(String classId) help = "restore mcrobject {0} with version {1} to current store from ocfl history") public static void restoreObjFromOCFLVersioned(String mcridString, String revision) throws IOException { MCRObjectID mcrid = MCRObjectID.getInstance(mcridString); - MCROCFLXMLMetadataManagerAdapter manager = new MCROCFLXMLMetadataManagerAdapter(); + MCROCFLXMLMetadataManager manager = new MCROCFLXMLMetadataManager(); manager.setRepositoryKey(MCRConfiguration2.getStringOrThrow("MCR.Metadata.Manager.Repository")); MCRContent content = manager.retrieveContent(mcrid, revision); try { @@ -361,7 +361,7 @@ public static void restoreObjFromOCFL(String mcridString) throws IOException { help = "Permanently delete object {0} and its history from ocfl") public static void purgeObject(String mcridString) { MCRObjectID mcrid = MCRObjectID.getInstance(mcridString); - MCROCFLXMLMetadataManagerAdapter manager = new MCROCFLXMLMetadataManagerAdapter(); + MCROCFLXMLMetadataManager manager = new MCROCFLXMLMetadataManager(); manager.setRepositoryKey(MCRConfiguration2.getStringOrThrow("MCR.Metadata.Manager.Repository")); manager.purge(mcrid, new Date(), MCRUserManager.getCurrentUser().getUserName()); } @@ -410,7 +410,7 @@ public static void purgeMarkedObjects() { return; } String repositoryKey = MCRConfiguration2.getStringOrThrow("MCR.Metadata.Manager.Repository"); - MCROCFLXMLMetadataManagerAdapter manager = new MCROCFLXMLMetadataManagerAdapter(); + MCROCFLXMLMetadataManager manager = new MCROCFLXMLMetadataManager(); manager.setRepositoryKey(repositoryKey); OcflRepository repository = manager.getRepository(); repository.listObjectIds() diff --git a/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLRegexCommands.java b/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLRegexCommands.java index 5466395956..1a4e33cd50 100644 --- a/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLRegexCommands.java +++ b/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLRegexCommands.java @@ -35,7 +35,7 @@ import org.mycore.datamodel.metadata.MCRObjectID; import org.mycore.frontend.cli.annotation.MCRCommand; import org.mycore.frontend.cli.annotation.MCRCommandGroup; -import org.mycore.ocfl.metadata.MCROCFLXMLMetadataManagerAdapter; +import org.mycore.ocfl.metadata.MCROCFLXMLMetadataManager; import org.mycore.ocfl.repository.MCROCFLRepositoryProvider; import org.mycore.ocfl.util.MCROCFLObjectIDPrefixHelper; @@ -103,7 +103,7 @@ public static List purgeMatchAll(String regex) { @MCRCommand(syntax = "purge ocfl objects matching {0}", help = "Purge ocfl objects that are matching the RegEx {0}") public static List purgeMatchObj(String regex) { - MCROCFLXMLMetadataManagerAdapter manager = new MCROCFLXMLMetadataManagerAdapter(); + MCROCFLXMLMetadataManager manager = new MCROCFLXMLMetadataManager(); manager.setRepositoryKey(METADATA_REPOSITORY_KEY); if (!confirmPurge) { logConfirm("objects"); @@ -191,7 +191,7 @@ private static String[] getRegexParts(String regex) { @MCRCommand(syntax = "purge marked ocfl objects matching {0}", help = "Purge marked ocfl objects that are matching the RegEx {0}") public static List purgeMarkedMatchObj(String regex) { - MCROCFLXMLMetadataManagerAdapter manager = new MCROCFLXMLMetadataManagerAdapter(); + MCROCFLXMLMetadataManager manager = new MCROCFLXMLMetadataManager(); manager.setRepositoryKey(METADATA_REPOSITORY_KEY); return streamDeleted(regex, manager) .map(id -> buildPurgeCommand("object", id)) @@ -249,7 +249,7 @@ public static List restoreMatchAll(String regex) { @MCRCommand(syntax = "restore ocfl objects matching {0}", help = "Restore ocfl objects that are matching the RegEx {0}") public static List restoreMatchObj(String regex) { - MCROCFLXMLMetadataManagerAdapter manager = new MCROCFLXMLMetadataManagerAdapter(); + MCROCFLXMLMetadataManager manager = new MCROCFLXMLMetadataManager(); manager.setRepositoryKey(METADATA_REPOSITORY_KEY); return streamDeleted(regex, manager) .map(id -> buildRestoreCommand("object", id, @@ -287,7 +287,7 @@ public static List restoreMatchUsr(String regex) { .collect(Collectors.toList()); } - private static Stream streamDeleted(String regex, MCROCFLXMLMetadataManagerAdapter manager) { + private static Stream streamDeleted(String regex, MCROCFLXMLMetadataManager manager) { return manager.getRepository().listObjectIds() .filter(obj -> obj.startsWith(MCROCFLObjectIDPrefixHelper.MCROBJECT) || obj.startsWith(MCROCFLObjectIDPrefixHelper.MCRDERIVATE)) diff --git a/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCRGZIPOCFLXMLMetadataManager.java b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCRGZIPOCFLXMLMetadataManager.java new file mode 100644 index 0000000000..96841eac58 --- /dev/null +++ b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCRGZIPOCFLXMLMetadataManager.java @@ -0,0 +1,63 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.ocfl.metadata; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import org.mycore.common.content.MCRContent; +import org.mycore.datamodel.metadata.MCRObjectID; + +import io.ocfl.api.model.OcflObjectVersion; +import io.ocfl.api.model.VersionNum; + +public class MCRGZIPOCFLXMLMetadataManager extends MCROCFLXMLMetadataManager { + + @Override + protected InputStream getContentStream(MCRContent xml) throws IOException { + InputStream contentStream = super.getContentStream(xml); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (GZIPOutputStream out = new GZIPOutputStream(baos)) { + contentStream.transferTo(out); + } + byte[] byteArray = baos.toByteArray(); + return new ByteArrayInputStream(byteArray); + } + + @Override + protected InputStream getStoredContentStream(MCRObjectID mcrid, OcflObjectVersion storeObject) throws IOException { + InputStream storedContentStream = super.getStoredContentStream(mcrid, storeObject); + return new GZIPInputStream(storedContentStream); + } + + @Override + protected MCROCFLContent getContent(MCRObjectID id, String ocflObjectID, VersionNum key) { + return new MCRGZIPOCFLContent(getRepository(), ocflObjectID, buildFilePath(id), + key.toString()); + } + + @Override + protected String buildFilePath(MCRObjectID mcrid) { + return super.buildFilePath(mcrid) + ".gz"; + } +} diff --git a/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCRGZIPOCFLXMLMetadataManagerAdapter.java b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCRGZIPOCFLXMLMetadataManagerAdapter.java index dcc1872128..a74b91ea23 100644 --- a/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCRGZIPOCFLXMLMetadataManagerAdapter.java +++ b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCRGZIPOCFLXMLMetadataManagerAdapter.java @@ -18,46 +18,10 @@ package org.mycore.ocfl.metadata; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -import org.mycore.common.content.MCRContent; -import org.mycore.datamodel.metadata.MCRObjectID; - -import io.ocfl.api.model.OcflObjectVersion; -import io.ocfl.api.model.VersionNum; - -public class MCRGZIPOCFLXMLMetadataManagerAdapter extends MCROCFLXMLMetadataManagerAdapter { - - @Override - protected InputStream getContentStream(MCRContent xml) throws IOException { - InputStream contentStream = super.getContentStream(xml); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (GZIPOutputStream out = new GZIPOutputStream(baos)) { - contentStream.transferTo(out); - } - byte[] byteArray = baos.toByteArray(); - return new ByteArrayInputStream(byteArray); - } - - @Override - protected InputStream getStoredContentStream(MCRObjectID mcrid, OcflObjectVersion storeObject) throws IOException { - InputStream storedContentStream = super.getStoredContentStream(mcrid, storeObject); - return new GZIPInputStream(storedContentStream); - } - - @Override - protected MCROCFLContent getContent(MCRObjectID id, String ocflObjectID, VersionNum key) { - return new MCRGZIPOCFLContent(getRepository(), ocflObjectID, buildFilePath(id), - key.toString()); - } +/** + * @deprecated Use {@link MCRGZIPOCFLXMLMetadataManager} instead. + */ +@Deprecated(forRemoval = true) +public class MCRGZIPOCFLXMLMetadataManagerAdapter extends MCRGZIPOCFLXMLMetadataManager { - @Override - protected String buildFilePath(MCRObjectID mcrid) { - return super.buildFilePath(mcrid) + ".gz"; - } } diff --git a/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCROCFLXMLMetadataManager.java b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCROCFLXMLMetadataManager.java new file mode 100644 index 0000000000..a8e096f052 --- /dev/null +++ b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCROCFLXMLMetadataManager.java @@ -0,0 +1,590 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.ocfl.metadata; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.ZoneOffset; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.jdom2.Document; +import org.jdom2.JDOMException; +import org.mycore.common.MCRCache; +import org.mycore.common.MCRPersistenceException; +import org.mycore.common.MCRSession; +import org.mycore.common.MCRSessionMgr; +import org.mycore.common.MCRUsageException; +import org.mycore.common.MCRUserInformation; +import org.mycore.common.config.MCRConfiguration2; +import org.mycore.common.config.annotation.MCRProperty; +import org.mycore.common.content.MCRContent; +import org.mycore.common.content.MCRJDOMContent; +import org.mycore.common.content.MCRStreamContent; +import org.mycore.datamodel.common.MCRObjectIDDate; +import org.mycore.datamodel.common.MCRXMLMetadataManager; +import org.mycore.datamodel.ifs2.MCRObjectIDDateImpl; +import org.mycore.datamodel.metadata.MCRDerivate; +import org.mycore.datamodel.metadata.MCRObjectID; +import org.mycore.datamodel.metadata.history.MCRMetadataHistoryManager; +import org.mycore.ocfl.layout.MCRStorageLayoutConfig; +import org.mycore.ocfl.layout.MCRStorageLayoutExtension; +import org.mycore.ocfl.repository.MCROCFLLocalRepositoryProvider; +import org.mycore.ocfl.repository.MCROCFLRepository; +import org.mycore.ocfl.repository.MCROCFLRepositoryProvider; +import org.mycore.ocfl.util.MCROCFLDeleteUtils; +import org.mycore.ocfl.util.MCROCFLMetadataVersion; +import org.mycore.ocfl.util.MCROCFLObjectIDPrefixHelper; + +import io.ocfl.api.OcflOption; +import io.ocfl.api.OcflRepository; +import io.ocfl.api.exception.NotFoundException; +import io.ocfl.api.exception.OverwriteException; +import io.ocfl.api.model.ObjectVersionId; +import io.ocfl.api.model.OcflObjectVersion; +import io.ocfl.api.model.OcflObjectVersionFile; +import io.ocfl.api.model.VersionDetails; +import io.ocfl.api.model.VersionInfo; +import io.ocfl.api.model.VersionNum; +import io.ocfl.core.extension.OcflExtensionConfig; +import io.ocfl.core.extension.storage.layout.HashedNTupleIdEncapsulationLayoutExtension; +import io.ocfl.core.extension.storage.layout.config.HashedNTupleIdEncapsulationLayoutConfig; + +/** + * Manages persistence of MCRObject and MCRDerivate xml metadata. Provides + * methods to create, retrieve, update and delete object metadata using OCFL + */ +public class MCROCFLXMLMetadataManager implements MCRXMLMetadataManager { + + private static final String MESSAGE_CREATED = "Created"; + + private static final String MESSAGE_UPDATED = "Updated"; + + private static final String MESSAGE_DELETED = "Deleted"; + + private static final Map MESSAGE_TYPE_MAPPING = Collections.unmodifiableMap(Map.ofEntries( + Map.entry(MESSAGE_CREATED, MCROCFLMetadataVersion.CREATED), + Map.entry(MESSAGE_UPDATED, MCROCFLMetadataVersion.UPDATED), + Map.entry(MESSAGE_DELETED, MCROCFLMetadataVersion.DELETED))); + + private String repositoryKey = "Default"; + + private static char convertMessageToType(String message) throws MCRPersistenceException { + if (!MESSAGE_TYPE_MAPPING.containsKey(message)) { + throw new MCRPersistenceException("Cannot identify version type from message '" + message + "'"); + } + return MESSAGE_TYPE_MAPPING.get(message); + } + + public MCROCFLRepository getRepository() { + return MCROCFLRepositoryProvider.getRepository(getRepositoryKey()); + } + + public String getRepositoryKey() { + return repositoryKey; + } + + @MCRProperty(name = "Repository", required = false) + public void setRepositoryKey(String repositoryKey) { + this.repositoryKey = repositoryKey; + } + + @Override + public void reload() { + // nothing to reload here + } + + @Override + public void verifyStore(String base) { + // not supported yet + } + + @Override + public void create(MCRObjectID mcrid, MCRContent xml, Date lastModified) throws MCRPersistenceException { + create(mcrid, xml, lastModified, null); + } + + public void create(MCRObjectID mcrid, MCRContent xml, Date lastModified, String user) + throws MCRPersistenceException { + String ocflObjectID = getOCFLObjectID(mcrid); + VersionInfo info = buildVersionInfo(MESSAGE_CREATED, lastModified, user); + try (InputStream objectAsStream = getContentStream(xml)) { + getRepository().updateObject(ObjectVersionId.head(ocflObjectID), info, + init -> init.writeFile(objectAsStream, buildFilePath(mcrid))); + } catch (IOException | OverwriteException e) { + throw new MCRPersistenceException("Failed to create object '" + ocflObjectID + "'", e); + } + } + + protected InputStream getContentStream(MCRContent xml) throws IOException { + return xml.getInputStream(); + } + + @Override + public void delete(MCRObjectID mcrid) throws MCRPersistenceException { + delete(mcrid, null, null); + } + + public void delete(MCRObjectID mcrid, Date date, String user) throws MCRPersistenceException { + boolean equals = Objects.equals(mcrid.getTypeId(), MCRDerivate.OBJECT_TYPE); + String prefix = equals ? MCROCFLObjectIDPrefixHelper.MCRDERIVATE + : MCROCFLObjectIDPrefixHelper.MCROBJECT; + + if (MCROCFLDeleteUtils.checkPurgeObject(mcrid, prefix)) { + purge(mcrid, date, user); + return; + } + + String ocflObjectID = getOCFLObjectID(mcrid); + if (!exists(mcrid)) { + throw new MCRUsageException("Cannot delete nonexistent object '" + ocflObjectID + "'"); + } + OcflRepository repo = getRepository(); + VersionInfo headVersion = repo.describeObject(ocflObjectID).getHeadVersion().getVersionInfo(); + char versionType = convertMessageToType(headVersion.getMessage()); + if (versionType == MCROCFLMetadataVersion.DELETED) { + throw new MCRUsageException("Cannot delete already deleted object '" + ocflObjectID + "'"); + } + repo.updateObject(ObjectVersionId.head(ocflObjectID), buildVersionInfo(MESSAGE_DELETED, date, null), + init -> init.removeFile(buildFilePath(mcrid))); + } + + public void purge(MCRObjectID mcrid, Date date, String user) { + String ocflObjectID = getOCFLObjectID(mcrid); + if (!getRepository().containsObject(ocflObjectID)) { + throw new MCRUsageException("Cannot delete nonexistent object '" + ocflObjectID + "'"); + } + + OcflRepository repo = getRepository(); + repo.purgeObject(ocflObjectID); + } + + @Override + public void update(MCRObjectID mcrid, MCRContent xml, Date lastModified) throws MCRPersistenceException { + update(mcrid, xml, lastModified, null); + } + + public void update(MCRObjectID mcrid, MCRContent xml, Date lastModified, String user) + throws MCRPersistenceException { + String ocflObjectID = getOCFLObjectID(mcrid); + if (!exists(mcrid)) { + throw new MCRUsageException("Cannot update nonexistent object '" + ocflObjectID + "'"); + } + try (InputStream objectAsStream = getContentStream(xml)) { + VersionInfo versionInfo = buildVersionInfo(MESSAGE_UPDATED, lastModified, user); + getRepository().updateObject(ObjectVersionId.head(ocflObjectID), versionInfo, + init -> init.writeFile(objectAsStream, buildFilePath(mcrid), OcflOption.OVERWRITE)); + } catch (IOException e) { + throw new MCRPersistenceException("Failed to update object '" + ocflObjectID + "'", e); + } + } + + private String getOCFLObjectID(MCRObjectID mcrid) { + return getOCFLObjectID(mcrid.toString()); + } + + private String getOCFLObjectID(String mcrid) { + String objectType = MCRObjectID.getInstance(mcrid).getTypeId(); + return Objects.equals(objectType, MCRDerivate.OBJECT_TYPE) ? MCROCFLObjectIDPrefixHelper.MCRDERIVATE + mcrid + : MCROCFLObjectIDPrefixHelper.MCROBJECT + mcrid; + } + + protected String buildFilePath(MCRObjectID mcrid) { + return "metadata/" + mcrid + ".xml"; + } + + @Override + public MCRContent retrieveContent(MCRObjectID mcrid) throws IOException { + String ocflObjectID = getOCFLObjectID(mcrid); + OcflObjectVersion storeObject; + OcflRepository repository = getRepository(); + try { + storeObject = repository.getObject(ObjectVersionId.head(ocflObjectID)); + } catch (NotFoundException e) { + throw new IOException("Object '" + ocflObjectID + "' could not be found", e); + } + + if (convertMessageToType( + storeObject.getVersionInfo().getMessage()) == MCROCFLMetadataVersion.DELETED) { + throw new IOException("Cannot read already deleted object '" + ocflObjectID + "'"); + } + return new MCROCFLContent(repository, ocflObjectID, buildFilePath(mcrid)); + } + + protected InputStream getStoredContentStream(MCRObjectID mcrid, OcflObjectVersion storeObject) throws IOException { + String path = buildFilePath(mcrid); + OcflObjectVersionFile file = storeObject.getFile(path); + if (file == null) { + throw new IOException("Couldn't find path '" + path + "' in '" + storeObject.getObjectId() + "'."); + } + return file.getStream(); + } + + @Override + public MCRContent retrieveContent(MCRObjectID mcrid, String revision) throws IOException { + if (revision == null) { + return retrieveContent(mcrid); + } + String ocflObjectID = getOCFLObjectID(mcrid); + OcflObjectVersion storeObject; + OcflRepository repo = getRepository(); + try { + storeObject = repo.getObject(ObjectVersionId.version(ocflObjectID, revision)); + } catch (NotFoundException e) { + throw new IOException("Object '" + ocflObjectID + "' could not be found", e); + } + + // maybe use .head(ocflObjectID) instead to prevent requests of old versions of deleted objects + if (convertMessageToType(repo.getObject(ObjectVersionId.version(ocflObjectID, revision)).getVersionInfo() + .getMessage()) == MCROCFLMetadataVersion.DELETED) { + throw new IOException("Cannot read already deleted object '" + ocflObjectID + "'"); + } + + try (InputStream storedContentStream = getStoredContentStream(mcrid, storeObject)) { + Document xml = new MCRStreamContent(storedContentStream).asXML(); + xml.getRootElement().setAttribute("rev", revision); // bugfix: MCR-2510, PR #1373 + return new MCRJDOMContent(xml); + } catch (JDOMException e) { + throw new IOException("Can not parse XML from OCFL-Store", e); + } + } + + private VersionInfo buildVersionInfo(String message, Date versionDate, String user) { + VersionInfo versionInfo = new VersionInfo(); + versionInfo.setMessage(message); + versionInfo.setCreated((versionDate == null ? new Date() : versionDate).toInstant().atOffset(ZoneOffset.UTC)); + String userID = user != null ? user + : Optional.ofNullable(MCRSessionMgr.getCurrentSession()) + .map(MCRSession::getUserInformation) + .map(MCRUserInformation::getUserID) + .orElse(null); + versionInfo.setUser(userID, null); + return versionInfo; + } + + @Override + public List listRevisions(MCRObjectID id) { + String ocflObjectID = getOCFLObjectID(id); + Map versionMap; + + try { + versionMap = getRepository().describeObject(ocflObjectID).getVersionMap(); + } catch (NotFoundException e) { + throw new MCRUsageException("Object '" + ocflObjectID + "' could not be found", e); + } + + return versionMap.entrySet().stream().map(v -> { + VersionNum key = v.getKey(); + VersionDetails details = v.getValue(); + VersionInfo versionInfo = details.getVersionInfo(); + + MCROCFLContent content = getContent(id, ocflObjectID, key); + return new MCROCFLMetadataVersion(content, + key.toString(), + versionInfo.getUser().getName(), + Date.from(details.getCreated().toInstant()), convertMessageToType(versionInfo.getMessage())); + }).collect(Collectors.toList()); + } + + protected MCROCFLContent getContent(MCRObjectID id, String ocflObjectID, VersionNum key) { + return new MCROCFLContent(getRepository(), ocflObjectID, buildFilePath(id), + key.toString()); + } + + private boolean isMetadata(String id) { + return id.startsWith(MCROCFLObjectIDPrefixHelper.MCROBJECT) + || id.startsWith(MCROCFLObjectIDPrefixHelper.MCRDERIVATE); + } + + private String removePrefix(String id) { + return id.startsWith(MCROCFLObjectIDPrefixHelper.MCRDERIVATE) + ? id.substring(MCROCFLObjectIDPrefixHelper.MCRDERIVATE.length()) + : id.substring(MCROCFLObjectIDPrefixHelper.MCROBJECT.length()); + } + + public IntStream getStoredIDs(String project, String type) throws MCRPersistenceException { + return getRepository().listObjectIds() + .filter(this::isMetadata) + .map(this::removePrefix) + .filter(id -> id.startsWith(project + "_" + type)) + .mapToInt((fullId) -> Integer.parseInt(fullId.substring(project.length() + type.length() + 2))); + } + + @Override + public int getHighestStoredID(String project, String type) { + MCROCFLRepositoryProvider ocflRepoProvider = MCROCFLRepositoryProvider.obtainInstance(repositoryKey); + if (!(ocflRepoProvider instanceof MCROCFLLocalRepositoryProvider localRepositoryProvider)) { + return calculateHighestStoreIDForRemoteRepositories(project, type); + } + return calculateHighestStoreIDForLocalRepositories(project, type, localRepositoryProvider); + } + + private int calculateHighestStoreIDForRemoteRepositories(String project, String type) { + return getStoredIDs(project, type).max().orElse(0); + } + + private int calculateHighestStoreIDForLocalRepositories(String project, String type, + MCROCFLLocalRepositoryProvider localRepositoryProvider) { + int highestStoredID = 0; + int maxDepth = Integer.MAX_VALUE; + + OcflExtensionConfig config = localRepositoryProvider.getExtensionConfig(); + Path basePath; + + // optimization for known layouts + if (Objects.equals(config.getExtensionName(), MCRStorageLayoutExtension.EXTENSION_NAME)) { + maxDepth = ((MCRStorageLayoutConfig) config).getSlotLayout().split("-").length; + basePath = localRepositoryProvider.getRepositoryRoot() + .resolve(MCROCFLObjectIDPrefixHelper.MCROBJECT.replace(":", "")) + .resolve(project).resolve(type); + highestStoredID = traverseMCRStorageDirectory(basePath, maxDepth); + } else if (Objects.equals(config.getExtensionName(), + HashedNTupleIdEncapsulationLayoutExtension.EXTENSION_NAME)) { + maxDepth = ((HashedNTupleIdEncapsulationLayoutConfig) config).getNumberOfTuples() + 1; + basePath = localRepositoryProvider.getRepositoryRoot(); + } else { + // for another repository provider implementation start with root directory + basePath = MCRConfiguration2.getString("MCR.OCFL.Repository." + repositoryKey + ".RepositoryRoot") + .map(Path::of).orElse(null); + } + + if (basePath != null && highestStoredID == 0) { + Pattern pattern = Pattern.compile("^.*" + project + "_{1}" + type + "_{1}\\d+$"); + try (Stream stream = Files.find(basePath, maxDepth, + (filePath, fileAttr) -> pattern.matcher(filePath.getFileName().toString()).matches())) { + highestStoredID = stream.map(entry -> entry.getFileName().toString()) + .map(fileName -> Integer.parseInt(fileName.substring(fileName.lastIndexOf('_') + 1))) + .max(Integer::compareTo).orElse(0); + } catch (Exception e) { + // nothing found, let the HistoryManager take over + } + } + return Math.max(highestStoredID, MCRMetadataHistoryManager.getHighestStoredID(project, type) + .map(MCRObjectID::getNumberAsInteger) + .orElse(0)); + } + + /** + * This method recursively parses a directory structure in MCRStorageLayout, + * like this: + *

+     * ocfl-root
+     * +-- mcrobject
+     *     +-- mir
+     *         +-- mods
+     *             +-- 0000
+     *                 +-- 00
+     *                     +-- mir_mods_0000000018
+     *                         ...
+     *                     +-- mir_mods_0000004567
+     *                         ...
+     *                     +-- mir_mods_0000009997
+     *                 +-- 01
+     *                     +-- mir_mods_0000010001
+     *                         ...
+     *                     +-- mir_mods_0000014567
+     *                         ...
+     *                     +-- mir_mods_0000017654
+     *  
+ * + * and searches on each level for the directory with the highest number at the end of it's name + * finally it returns the highest available number part of a MyCoRe object id. + * + * @param path - the root path + * @param depth - the level of subdirectories to look into + * @return the highest number, or 0 if such cannot be found + */ + private int traverseMCRStorageDirectory(Path path, int depth) { + int max = -1; + Path newPath = path; + try (DirectoryStream ds = Files.newDirectoryStream(path, + p -> p.getFileName().toString().matches("^.*\\d+$"))) { + for (Path entry : ds) { + int current = Integer.parseInt(entry.getFileName().toString().replaceAll("^.*_", "")); + if (max < current) { + max = current; + newPath = entry; + } + } + } catch (IOException | NumberFormatException e) { + // fallback to slower full tree search + return 0; + } + if (depth <= 1) { + return Math.max(0, max); + } else { + return traverseMCRStorageDirectory(newPath, depth - 1); + } + } + + @Override + public boolean exists(MCRObjectID mcrid) throws MCRPersistenceException { + String ocflObjectID = getOCFLObjectID(mcrid); + if (!getRepository().containsObject(ocflObjectID)) { + return false; + } + if (isDeleted(ocflObjectID)) { + return false; + } + VersionDetails versionDetails = getRepository().describeVersion(ObjectVersionId.head(ocflObjectID)); + return versionDetails.getFile(buildFilePath(mcrid)) != null; + } + + @Override + public List listIDsForBase(String base) { + return getRepository().listObjectIds() + .filter(this::isMetadata) + .filter(this::isNotDeleted) + .map(this::removePrefix) + .filter(s -> s.startsWith(base)) + .collect(Collectors.toList()); + + } + + @Override + public List listIDsOfType(String type) { + return getRepository().listObjectIds() + .filter(this::isMetadata) + .filter(this::isNotDeleted) + .map(this::removePrefix) + .filter(s -> type.equals(s.split("_")[1])) + .collect(Collectors.toList()); + } + + @Override + public List listIDs() { + OcflRepository repo = getRepository(); + return repo + .listObjectIds() + .filter(this::isMetadata) + .filter(this::isNotDeleted) + .map(this::removePrefix) + .collect(Collectors.toList()); + } + + private boolean isDeleted(String ocflID) { + return convertMessageToType(getRepository().describeVersion(ObjectVersionId.head(ocflID)).getVersionInfo() + .getMessage()) == MCROCFLMetadataVersion.DELETED; + } + + private boolean isNotDeleted(String ocflID) { + return !isDeleted(ocflID); + } + + @Override + public Collection getObjectTypes() { + return getRepository() + .listObjectIds() + .filter(this::isMetadata) + .map(this::removePrefix) + .map(s -> s.split("_")[1]) + .distinct() + .collect(Collectors.toList()); + } + + @Override + public Collection getObjectBaseIds() { + return getRepository() + .listObjectIds() + .filter(this::isMetadata) + .map(this::removePrefix) + .map(s -> s.substring(0, s.lastIndexOf('_'))) + .distinct() + .collect(Collectors.toList()); + } + + @Override + public List retrieveObjectDates(List ids) throws IOException { + try { + return ids.stream() + .map(this::getOCFLObjectID) + .filter(this::isMetadata) + .filter(this::isNotDeleted) + .map(ocflId -> { + try { + return new MCRObjectIDDateImpl(new Date(getLastModified(ocflId)), removePrefix(ocflId)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }) + .collect(Collectors.toList()); + } catch (UncheckedIOException ignoredUnchecked) { + throw ignoredUnchecked.getCause(); + } + } + + @Override + public long getLastModified(MCRObjectID id) throws IOException { + return getLastModified(getOCFLObjectID(id)); + } + + public long getLastModified(String ocflObjectId) throws IOException { + try { + return Date.from(getRepository() + .getObject(ObjectVersionId.head(ocflObjectId)) + .getVersionInfo() + .getCreated() + .toInstant()) + .getTime(); + } catch (NotFoundException e) { + throw new IOException(e); + } + } + + @Override + public MCRCache.ModifiedHandle getLastModifiedHandle(MCRObjectID id, long expire, TimeUnit unit) { + return new MCROCFLXMLMetadataManager.StoreModifiedHandle(id, expire, unit); + } + + private final class StoreModifiedHandle implements MCRCache.ModifiedHandle { + + private final long expire; + + private final MCRObjectID id; + + private StoreModifiedHandle(MCRObjectID id, long time, TimeUnit unit) { + this.expire = unit.toMillis(time); + this.id = id; + } + + @Override + public long getCheckPeriod() { + return expire; + } + + @Override + public long getLastModified() throws IOException { + return MCROCFLXMLMetadataManager.this.getLastModified(id); + } + } +} diff --git a/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCROCFLXMLMetadataManagerAdapter.java b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCROCFLXMLMetadataManagerAdapter.java index 353fd6229f..9b30a42ed9 100644 --- a/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCROCFLXMLMetadataManagerAdapter.java +++ b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/MCROCFLXMLMetadataManagerAdapter.java @@ -18,573 +18,13 @@ package org.mycore.ocfl.metadata; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.ZoneOffset; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import org.jdom2.Document; -import org.jdom2.JDOMException; -import org.mycore.common.MCRCache; -import org.mycore.common.MCRPersistenceException; -import org.mycore.common.MCRSession; -import org.mycore.common.MCRSessionMgr; -import org.mycore.common.MCRUsageException; -import org.mycore.common.MCRUserInformation; -import org.mycore.common.config.MCRConfiguration2; -import org.mycore.common.config.annotation.MCRProperty; -import org.mycore.common.content.MCRContent; -import org.mycore.common.content.MCRJDOMContent; -import org.mycore.common.content.MCRStreamContent; -import org.mycore.datamodel.common.MCRObjectIDDate; -import org.mycore.datamodel.common.MCRXMLMetadataManagerAdapter; -import org.mycore.datamodel.ifs2.MCRObjectIDDateImpl; -import org.mycore.datamodel.metadata.MCRDerivate; -import org.mycore.datamodel.metadata.MCRObjectID; -import org.mycore.datamodel.metadata.history.MCRMetadataHistoryManager; -import org.mycore.ocfl.layout.MCRStorageLayoutConfig; -import org.mycore.ocfl.layout.MCRStorageLayoutExtension; -import org.mycore.ocfl.repository.MCROCFLLocalRepositoryProvider; -import org.mycore.ocfl.repository.MCROCFLRepository; -import org.mycore.ocfl.repository.MCROCFLRepositoryProvider; -import org.mycore.ocfl.util.MCROCFLDeleteUtils; -import org.mycore.ocfl.util.MCROCFLMetadataVersion; -import org.mycore.ocfl.util.MCROCFLObjectIDPrefixHelper; - -import io.ocfl.api.OcflOption; -import io.ocfl.api.OcflRepository; -import io.ocfl.api.exception.NotFoundException; -import io.ocfl.api.exception.OverwriteException; -import io.ocfl.api.model.ObjectVersionId; -import io.ocfl.api.model.OcflObjectVersion; -import io.ocfl.api.model.OcflObjectVersionFile; -import io.ocfl.api.model.VersionDetails; -import io.ocfl.api.model.VersionInfo; -import io.ocfl.api.model.VersionNum; -import io.ocfl.core.extension.OcflExtensionConfig; -import io.ocfl.core.extension.storage.layout.HashedNTupleIdEncapsulationLayoutExtension; -import io.ocfl.core.extension.storage.layout.config.HashedNTupleIdEncapsulationLayoutConfig; - /** * Manages persistence of MCRObject and MCRDerivate xml metadata. Provides * methods to create, retrieve, update and delete object metadata using OCFL + * + * @deprecated Use {@link MCROCFLXMLMetadataManager} instead. */ -public class MCROCFLXMLMetadataManagerAdapter implements MCRXMLMetadataManagerAdapter { - - private static final String MESSAGE_CREATED = "Created"; - - private static final String MESSAGE_UPDATED = "Updated"; - - private static final String MESSAGE_DELETED = "Deleted"; - - private static final Map MESSAGE_TYPE_MAPPING = Collections.unmodifiableMap(Map.ofEntries( - Map.entry(MESSAGE_CREATED, MCROCFLMetadataVersion.CREATED), - Map.entry(MESSAGE_UPDATED, MCROCFLMetadataVersion.UPDATED), - Map.entry(MESSAGE_DELETED, MCROCFLMetadataVersion.DELETED))); - - private String repositoryKey = "Default"; - - private static char convertMessageToType(String message) throws MCRPersistenceException { - if (!MESSAGE_TYPE_MAPPING.containsKey(message)) { - throw new MCRPersistenceException("Cannot identify version type from message '" + message + "'"); - } - return MESSAGE_TYPE_MAPPING.get(message); - } - - public MCROCFLRepository getRepository() { - return MCROCFLRepositoryProvider.getRepository(getRepositoryKey()); - } - - public String getRepositoryKey() { - return repositoryKey; - } - - @MCRProperty(name = "Repository", required = false) - public void setRepositoryKey(String repositoryKey) { - this.repositoryKey = repositoryKey; - } - - @Override - public void reload() { - // nothing to reload here - } - - @Override - public void verifyStore(String base) { - // not supported yet - } - - @Override - public void create(MCRObjectID mcrid, MCRContent xml, Date lastModified) throws MCRPersistenceException { - create(mcrid, xml, lastModified, null); - } - - public void create(MCRObjectID mcrid, MCRContent xml, Date lastModified, String user) - throws MCRPersistenceException { - String ocflObjectID = getOCFLObjectID(mcrid); - VersionInfo info = buildVersionInfo(MESSAGE_CREATED, lastModified, user); - try (InputStream objectAsStream = getContentStream(xml)) { - getRepository().updateObject(ObjectVersionId.head(ocflObjectID), info, - init -> init.writeFile(objectAsStream, buildFilePath(mcrid))); - } catch (IOException | OverwriteException e) { - throw new MCRPersistenceException("Failed to create object '" + ocflObjectID + "'", e); - } - } - - protected InputStream getContentStream(MCRContent xml) throws IOException { - return xml.getInputStream(); - } - - @Override - public void delete(MCRObjectID mcrid) throws MCRPersistenceException { - delete(mcrid, null, null); - } - - public void delete(MCRObjectID mcrid, Date date, String user) throws MCRPersistenceException { - boolean equals = Objects.equals(mcrid.getTypeId(), MCRDerivate.OBJECT_TYPE); - String prefix = equals ? MCROCFLObjectIDPrefixHelper.MCRDERIVATE - : MCROCFLObjectIDPrefixHelper.MCROBJECT; - - if (MCROCFLDeleteUtils.checkPurgeObject(mcrid, prefix)) { - purge(mcrid, date, user); - return; - } - - String ocflObjectID = getOCFLObjectID(mcrid); - if (!exists(mcrid)) { - throw new MCRUsageException("Cannot delete nonexistent object '" + ocflObjectID + "'"); - } - OcflRepository repo = getRepository(); - VersionInfo headVersion = repo.describeObject(ocflObjectID).getHeadVersion().getVersionInfo(); - char versionType = convertMessageToType(headVersion.getMessage()); - if (versionType == MCROCFLMetadataVersion.DELETED) { - throw new MCRUsageException("Cannot delete already deleted object '" + ocflObjectID + "'"); - } - repo.updateObject(ObjectVersionId.head(ocflObjectID), buildVersionInfo(MESSAGE_DELETED, date, null), - init -> init.removeFile(buildFilePath(mcrid))); - } - - public void purge(MCRObjectID mcrid, Date date, String user) { - String ocflObjectID = getOCFLObjectID(mcrid); - if (!getRepository().containsObject(ocflObjectID)) { - throw new MCRUsageException("Cannot delete nonexistent object '" + ocflObjectID + "'"); - } - - OcflRepository repo = getRepository(); - repo.purgeObject(ocflObjectID); - } - - @Override - public void update(MCRObjectID mcrid, MCRContent xml, Date lastModified) throws MCRPersistenceException { - update(mcrid, xml, lastModified, null); - } - - public void update(MCRObjectID mcrid, MCRContent xml, Date lastModified, String user) - throws MCRPersistenceException { - String ocflObjectID = getOCFLObjectID(mcrid); - if (!exists(mcrid)) { - throw new MCRUsageException("Cannot update nonexistent object '" + ocflObjectID + "'"); - } - try (InputStream objectAsStream = getContentStream(xml)) { - VersionInfo versionInfo = buildVersionInfo(MESSAGE_UPDATED, lastModified, user); - getRepository().updateObject(ObjectVersionId.head(ocflObjectID), versionInfo, - init -> init.writeFile(objectAsStream, buildFilePath(mcrid), OcflOption.OVERWRITE)); - } catch (IOException e) { - throw new MCRPersistenceException("Failed to update object '" + ocflObjectID + "'", e); - } - } - - private String getOCFLObjectID(MCRObjectID mcrid) { - return getOCFLObjectID(mcrid.toString()); - } - - private String getOCFLObjectID(String mcrid) { - String objectType = MCRObjectID.getInstance(mcrid).getTypeId(); - return Objects.equals(objectType, MCRDerivate.OBJECT_TYPE) ? MCROCFLObjectIDPrefixHelper.MCRDERIVATE + mcrid - : MCROCFLObjectIDPrefixHelper.MCROBJECT + mcrid; - } - - protected String buildFilePath(MCRObjectID mcrid) { - return "metadata/" + mcrid + ".xml"; - } - - @Override - public MCRContent retrieveContent(MCRObjectID mcrid) throws IOException { - String ocflObjectID = getOCFLObjectID(mcrid); - OcflObjectVersion storeObject; - OcflRepository repository = getRepository(); - try { - storeObject = repository.getObject(ObjectVersionId.head(ocflObjectID)); - } catch (NotFoundException e) { - throw new IOException("Object '" + ocflObjectID + "' could not be found", e); - } - - if (convertMessageToType( - storeObject.getVersionInfo().getMessage()) == MCROCFLMetadataVersion.DELETED) { - throw new IOException("Cannot read already deleted object '" + ocflObjectID + "'"); - } - return new MCROCFLContent(repository, ocflObjectID, buildFilePath(mcrid)); - } - - protected InputStream getStoredContentStream(MCRObjectID mcrid, OcflObjectVersion storeObject) throws IOException { - String path = buildFilePath(mcrid); - OcflObjectVersionFile file = storeObject.getFile(path); - if (file == null) { - throw new IOException("Couldn't find path '" + path + "' in '" + storeObject.getObjectId() + "'."); - } - return file.getStream(); - } - - @Override - public MCRContent retrieveContent(MCRObjectID mcrid, String revision) throws IOException { - if (revision == null) { - return retrieveContent(mcrid); - } - String ocflObjectID = getOCFLObjectID(mcrid); - OcflObjectVersion storeObject; - OcflRepository repo = getRepository(); - try { - storeObject = repo.getObject(ObjectVersionId.version(ocflObjectID, revision)); - } catch (NotFoundException e) { - throw new IOException("Object '" + ocflObjectID + "' could not be found", e); - } - - // maybe use .head(ocflObjectID) instead to prevent requests of old versions of deleted objects - if (convertMessageToType(repo.getObject(ObjectVersionId.version(ocflObjectID, revision)).getVersionInfo() - .getMessage()) == MCROCFLMetadataVersion.DELETED) { - throw new IOException("Cannot read already deleted object '" + ocflObjectID + "'"); - } - - try (InputStream storedContentStream = getStoredContentStream(mcrid, storeObject)) { - Document xml = new MCRStreamContent(storedContentStream).asXML(); - xml.getRootElement().setAttribute("rev", revision); // bugfix: MCR-2510, PR #1373 - return new MCRJDOMContent(xml); - } catch (JDOMException e) { - throw new IOException("Can not parse XML from OCFL-Store", e); - } - } - - private VersionInfo buildVersionInfo(String message, Date versionDate, String user) { - VersionInfo versionInfo = new VersionInfo(); - versionInfo.setMessage(message); - versionInfo.setCreated((versionDate == null ? new Date() : versionDate).toInstant().atOffset(ZoneOffset.UTC)); - String userID = user != null ? user - : Optional.ofNullable(MCRSessionMgr.getCurrentSession()) - .map(MCRSession::getUserInformation) - .map(MCRUserInformation::getUserID) - .orElse(null); - versionInfo.setUser(userID, null); - return versionInfo; - } - - @Override - public List listRevisions(MCRObjectID id) { - String ocflObjectID = getOCFLObjectID(id); - Map versionMap; - - try { - versionMap = getRepository().describeObject(ocflObjectID).getVersionMap(); - } catch (NotFoundException e) { - throw new MCRUsageException("Object '" + ocflObjectID + "' could not be found", e); - } - - return versionMap.entrySet().stream().map(v -> { - VersionNum key = v.getKey(); - VersionDetails details = v.getValue(); - VersionInfo versionInfo = details.getVersionInfo(); - - MCROCFLContent content = getContent(id, ocflObjectID, key); - return new MCROCFLMetadataVersion(content, - key.toString(), - versionInfo.getUser().getName(), - Date.from(details.getCreated().toInstant()), convertMessageToType(versionInfo.getMessage())); - }).collect(Collectors.toList()); - } - - protected MCROCFLContent getContent(MCRObjectID id, String ocflObjectID, VersionNum key) { - return new MCROCFLContent(getRepository(), ocflObjectID, buildFilePath(id), - key.toString()); - } - - private boolean isMetadata(String id) { - return id.startsWith(MCROCFLObjectIDPrefixHelper.MCROBJECT) - || id.startsWith(MCROCFLObjectIDPrefixHelper.MCRDERIVATE); - } - - private String removePrefix(String id) { - return id.startsWith(MCROCFLObjectIDPrefixHelper.MCRDERIVATE) - ? id.substring(MCROCFLObjectIDPrefixHelper.MCRDERIVATE.length()) - : id.substring(MCROCFLObjectIDPrefixHelper.MCROBJECT.length()); - } - - public IntStream getStoredIDs(String project, String type) throws MCRPersistenceException { - return getRepository().listObjectIds() - .filter(this::isMetadata) - .map(this::removePrefix) - .filter(id -> id.startsWith(project + "_" + type)) - .mapToInt((fullId) -> Integer.parseInt(fullId.substring(project.length() + type.length() + 2))); - } - - @Override - public int getHighestStoredID(String project, String type) { - MCROCFLRepositoryProvider ocflRepoProvider = MCROCFLRepositoryProvider.obtainInstance(repositoryKey); - if (!(ocflRepoProvider instanceof MCROCFLLocalRepositoryProvider localRepositoryProvider)) { - return calculateHighestStoreIDForRemoteRepositories(project, type); - } - return calculateHighestStoreIDForLocalRepositories(project, type, localRepositoryProvider); - } - - private int calculateHighestStoreIDForRemoteRepositories(String project, String type) { - return getStoredIDs(project, type).max().orElse(0); - } - - private int calculateHighestStoreIDForLocalRepositories(String project, String type, - MCROCFLLocalRepositoryProvider localRepositoryProvider) { - int highestStoredID = 0; - int maxDepth = Integer.MAX_VALUE; - - OcflExtensionConfig config = localRepositoryProvider.getExtensionConfig(); - Path basePath; - - // optimization for known layouts - if (Objects.equals(config.getExtensionName(), MCRStorageLayoutExtension.EXTENSION_NAME)) { - maxDepth = ((MCRStorageLayoutConfig) config).getSlotLayout().split("-").length; - basePath = localRepositoryProvider.getRepositoryRoot() - .resolve(MCROCFLObjectIDPrefixHelper.MCROBJECT.replace(":", "")) - .resolve(project).resolve(type); - highestStoredID = traverseMCRStorageDirectory(basePath, maxDepth); - } else if (Objects.equals(config.getExtensionName(), - HashedNTupleIdEncapsulationLayoutExtension.EXTENSION_NAME)) { - maxDepth = ((HashedNTupleIdEncapsulationLayoutConfig) config).getNumberOfTuples() + 1; - basePath = localRepositoryProvider.getRepositoryRoot(); - } else { - // for another repository provider implementation start with root directory - basePath = MCRConfiguration2.getString("MCR.OCFL.Repository." + repositoryKey + ".RepositoryRoot") - .map(Path::of).orElse(null); - } - - if (basePath != null && highestStoredID == 0) { - Pattern pattern = Pattern.compile("^.*" + project + "_{1}" + type + "_{1}\\d+$"); - try (Stream stream = Files.find(basePath, maxDepth, - (filePath, fileAttr) -> pattern.matcher(filePath.getFileName().toString()).matches())) { - highestStoredID = stream.map(entry -> entry.getFileName().toString()) - .map(fileName -> Integer.parseInt(fileName.substring(fileName.lastIndexOf('_') + 1))) - .max(Integer::compareTo).orElse(0); - } catch (Exception e) { - // nothing found, let the HistoryManager take over - } - } - return Math.max(highestStoredID, MCRMetadataHistoryManager.getHighestStoredID(project, type) - .map(MCRObjectID::getNumberAsInteger) - .orElse(0)); - } - - /** - * This method recursively parses a directory structure in MCRStorageLayout, - * like this: - *
-     * ocfl-root
-     * +-- mcrobject
-     *     +-- mir
-     *         +-- mods
-     *             +-- 0000
-     *                 +-- 00
-     *                     +-- mir_mods_0000000018
-     *                         ...
-     *                     +-- mir_mods_0000004567
-     *                         ...
-     *                     +-- mir_mods_0000009997
-     *                 +-- 01
-     *                     +-- mir_mods_0000010001
-     *                         ...
-     *                     +-- mir_mods_0000014567
-     *                         ...
-     *                     +-- mir_mods_0000017654
-     *  
- * - * and searches on each level for the directory with the highest number at the end of it's name - * finally it returns the highest available number part of a MyCoRe object id. - * - * @param path - the root path - * @param depth - the level of subdirectories to look into - * @return the highest number, or 0 if such cannot be found - */ - private int traverseMCRStorageDirectory(Path path, int depth) { - int max = -1; - Path newPath = path; - try (DirectoryStream ds = Files.newDirectoryStream(path, - p -> p.getFileName().toString().matches("^.*\\d+$"))) { - for (Path entry : ds) { - int current = Integer.parseInt(entry.getFileName().toString().replaceAll("^.*_", "")); - if (max < current) { - max = current; - newPath = entry; - } - } - } catch (IOException | NumberFormatException e) { - // fallback to slower full tree search - return 0; - } - if (depth <= 1) { - return Math.max(0, max); - } else { - return traverseMCRStorageDirectory(newPath, depth - 1); - } - } - - @Override - public boolean exists(MCRObjectID mcrid) throws MCRPersistenceException { - String ocflObjectID = getOCFLObjectID(mcrid); - if (!getRepository().containsObject(ocflObjectID)) { - return false; - } - if (isDeleted(ocflObjectID)) { - return false; - } - VersionDetails versionDetails = getRepository().describeVersion(ObjectVersionId.head(ocflObjectID)); - return versionDetails.getFile(buildFilePath(mcrid)) != null; - } - - @Override - public List listIDsForBase(String base) { - return getRepository().listObjectIds() - .filter(this::isMetadata) - .filter(this::isNotDeleted) - .map(this::removePrefix) - .filter(s -> s.startsWith(base)) - .collect(Collectors.toList()); - - } - - @Override - public List listIDsOfType(String type) { - return getRepository().listObjectIds() - .filter(this::isMetadata) - .filter(this::isNotDeleted) - .map(this::removePrefix) - .filter(s -> type.equals(s.split("_")[1])) - .collect(Collectors.toList()); - } - - @Override - public List listIDs() { - OcflRepository repo = getRepository(); - return repo - .listObjectIds() - .filter(this::isMetadata) - .filter(this::isNotDeleted) - .map(this::removePrefix) - .collect(Collectors.toList()); - } - - private boolean isDeleted(String ocflID) { - return convertMessageToType(getRepository().describeVersion(ObjectVersionId.head(ocflID)).getVersionInfo() - .getMessage()) == MCROCFLMetadataVersion.DELETED; - } - - private boolean isNotDeleted(String ocflID) { - return !isDeleted(ocflID); - } - - @Override - public Collection getObjectTypes() { - return getRepository() - .listObjectIds() - .filter(this::isMetadata) - .map(this::removePrefix) - .map(s -> s.split("_")[1]) - .distinct() - .collect(Collectors.toList()); - } - - @Override - public Collection getObjectBaseIds() { - return getRepository() - .listObjectIds() - .filter(this::isMetadata) - .map(this::removePrefix) - .map(s -> s.substring(0, s.lastIndexOf('_'))) - .distinct() - .collect(Collectors.toList()); - } - - @Override - public List retrieveObjectDates(List ids) throws IOException { - try { - return ids.stream() - .map(this::getOCFLObjectID) - .filter(this::isMetadata) - .filter(this::isNotDeleted) - .map(ocflId -> { - try { - return new MCRObjectIDDateImpl(new Date(getLastModified(ocflId)), removePrefix(ocflId)); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }) - .collect(Collectors.toList()); - } catch (UncheckedIOException ignoredUnchecked) { - throw ignoredUnchecked.getCause(); - } - } - - @Override - public long getLastModified(MCRObjectID id) throws IOException { - return getLastModified(getOCFLObjectID(id)); - } - - public long getLastModified(String ocflObjectId) throws IOException { - try { - return Date.from(getRepository() - .getObject(ObjectVersionId.head(ocflObjectId)) - .getVersionInfo() - .getCreated() - .toInstant()) - .getTime(); - } catch (NotFoundException e) { - throw new IOException(e); - } - } - - @Override - public MCRCache.ModifiedHandle getLastModifiedHandle(MCRObjectID id, long expire, TimeUnit unit) { - return new MCROCFLXMLMetadataManagerAdapter.StoreModifiedHandle(id, expire, unit); - } - - private final class StoreModifiedHandle implements MCRCache.ModifiedHandle { - - private final long expire; - - private final MCRObjectID id; - - private StoreModifiedHandle(MCRObjectID id, long time, TimeUnit unit) { - this.expire = unit.toMillis(time); - this.id = id; - } - - @Override - public long getCheckPeriod() { - return expire; - } +@Deprecated(forRemoval = true) +public class MCROCFLXMLMetadataManagerAdapter extends MCROCFLXMLMetadataManager { - @Override - public long getLastModified() throws IOException { - return MCROCFLXMLMetadataManagerAdapter.this.getLastModified(id); - } - } } diff --git a/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/migration/MCROCFLMigration.java b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/migration/MCROCFLMigration.java index ec99d5910a..bbfac0977d 100644 --- a/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/migration/MCROCFLMigration.java +++ b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/migration/MCROCFLMigration.java @@ -36,13 +36,13 @@ import org.mycore.datamodel.common.MCRXMLMetadataManager; import org.mycore.datamodel.metadata.MCRMetadataManager; import org.mycore.datamodel.metadata.MCRObjectID; -import org.mycore.ocfl.metadata.MCROCFLXMLMetadataManagerAdapter; +import org.mycore.ocfl.metadata.MCROCFLXMLMetadataManager; public class MCROCFLMigration { private static final Logger LOGGER = LogManager.getLogger(); - private final MCROCFLXMLMetadataManagerAdapter target; + private final MCROCFLXMLMetadataManager target; private final List invalidState; @@ -59,11 +59,11 @@ public MCROCFLMigration(String newRepoKey) { } public MCROCFLMigration(String newRepoKey, List pruners) { - this(newRepoKey, pruners, new MCROCFLXMLMetadataManagerAdapter()); + this(newRepoKey, pruners, new MCROCFLXMLMetadataManager()); } public MCROCFLMigration(String newRepoKey, List pruners, - MCROCFLXMLMetadataManagerAdapter target) { + MCROCFLXMLMetadataManager target) { this.target = target; if (newRepoKey != null) { diff --git a/mycore-ocfl/src/test/resources/mycore.properties b/mycore-ocfl/src/test/resources/mycore.properties index 64b9baeae8..78575226e6 100644 --- a/mycore-ocfl/src/test/resources/mycore.properties +++ b/mycore-ocfl/src/test/resources/mycore.properties @@ -7,7 +7,7 @@ ##################### MCR.Metadata.Manager.Repository=Test -MCR.Metadata.Manager.Class=org.mycore.ocfl.metadata.MCROCFLXMLMetadataManagerAdapter +MCR.Metadata.Manager.Class=org.mycore.ocfl.metadata.MCROCFLXMLMetadataManager ##################### # NIO FS Properties From 08e422ba8e47b4140ea063fd365b0c43955ed483 Mon Sep 17 00:00:00 2001 From: Torsten Krause Date: Thu, 29 Jan 2026 13:42:04 +0100 Subject: [PATCH 2/4] MCR-3600 rename getInstance to obtainInstance to satisfy PMD rules --- .../mycore/access/mcrimpl/MCRAccessStore.java | 2 +- .../strategies/MCRParentRuleStrategy.java | 2 +- .../org/mycore/common/xml/MCRURIResolver.java | 8 ++++---- .../datamodel/common/MCRCreatorCache.java | 2 +- .../common/MCRDefaultObjectIDGenerator.java | 4 ++-- .../common/MCRXMLMetadataEventHandler.java | 2 +- .../common/MCRXMLMetadataManager.java | 14 ++++++++++++++ .../mycore/datamodel/ifs2/MCRIFS2Commands.java | 2 +- .../datamodel/metadata/MCRMetadataManager.java | 10 +++++----- .../datamodel/metadata/MCRObjectUtils.java | 2 +- .../history/MCRMetadataHistoryCommands.java | 14 +++++++------- .../objectinfo/MCRObjectInfoCommands.java | 6 +++--- .../mycore/frontend/cli/MCRCommandUtils.java | 6 +++--- .../frontend/cli/MCRDerivateCommands.java | 2 +- .../mycore/frontend/cli/MCRObjectCommands.java | 18 +++++++++--------- .../frontend/servlets/MCRObjectServlet.java | 2 +- .../services/zipper/MCRCompressServlet.java | 4 ++-- .../common/MCRXMLMetadataManagerTest.java | 2 +- .../datamodel/metadata/MCRObjectIDTest.java | 2 +- .../org/mycore/test/MCRMetadataExtension.java | 2 +- .../test/tests/MCRMetadataExtensionTest.java | 2 +- .../migration/cli/MCRMigrationCommands.java | 14 +++++++------- .../cli/MCRMigrationCommandsTest.java | 8 ++++---- .../org/mycore/mods/MCRMODSEmbargoUtils.java | 2 +- .../mods/csl/MCRListModsItemDataProvider.java | 2 +- .../csl/MCRListModsItemDataProviderTest.java | 2 +- .../neo4j/frontend/cli/MCRNeo4JCommands.java | 8 ++++---- .../org/mycore/oai/MCROAIObjectManager.java | 2 +- .../mycore/ocfl/commands/MCROCFLCommands.java | 10 +++++----- .../metadata/migration/MCROCFLMigration.java | 4 ++-- .../mycore/orcid/works/MCRWorksPublisher.java | 2 +- .../java/org/mycore/pi/cli/MCRPICommands.java | 2 +- .../v1/utils/MCRRestAPIObjectsHelper.java | 10 +++++----- .../mycore/restapi/v2/MCRRestDerivates.java | 6 +++--- .../org/mycore/restapi/v2/MCRRestObjects.java | 10 +++++----- .../org/mycore/restapi/v2/MCRRestUtils.java | 2 +- .../org/mycore/solr/index/MCRSolrIndexer.java | 14 +++++++------- .../document/MCRSolrInputDocumentFactory.java | 2 +- .../file/MCRSolrFileIndexBaseAccumulator.java | 2 +- .../handlers/MCRSolrIndexHandlerFactory.java | 4 ++-- 40 files changed, 114 insertions(+), 100 deletions(-) diff --git a/mycore-base/src/main/java/org/mycore/access/mcrimpl/MCRAccessStore.java b/mycore-base/src/main/java/org/mycore/access/mcrimpl/MCRAccessStore.java index 58bed1028b..21406787c9 100644 --- a/mycore-base/src/main/java/org/mycore/access/mcrimpl/MCRAccessStore.java +++ b/mycore-base/src/main/java/org/mycore/access/mcrimpl/MCRAccessStore.java @@ -107,7 +107,7 @@ public Collection getDefinition(String type) { Collection elements; if (MCRConfiguration2.getOrThrow("MCR.Metadata.Type." + type, Boolean::parseBoolean)) { - elements = MCRXMLMetadataManager.getInstance().listIDsOfType(type); + elements = MCRXMLMetadataManager.obtainInstance().listIDsOfType(type); } else { return Collections.emptySet(); } diff --git a/mycore-base/src/main/java/org/mycore/access/strategies/MCRParentRuleStrategy.java b/mycore-base/src/main/java/org/mycore/access/strategies/MCRParentRuleStrategy.java index c653d35761..d994770e4c 100644 --- a/mycore-base/src/main/java/org/mycore/access/strategies/MCRParentRuleStrategy.java +++ b/mycore-base/src/main/java/org/mycore/access/strategies/MCRParentRuleStrategy.java @@ -68,7 +68,7 @@ public boolean checkPermission(String id, String permission) { private static String getParentID(String objectID) { Document parentDoc; try { - parentDoc = MCRXMLMetadataManager.getInstance().retrieveXML(MCRObjectID.getInstance(objectID)); + parentDoc = MCRXMLMetadataManager.obtainInstance().retrieveXML(MCRObjectID.getInstance(objectID)); } catch (IOException | JDOMException e) { LOGGER.error("Could not read object: {}", objectID, e); return null; diff --git a/mycore-base/src/main/java/org/mycore/common/xml/MCRURIResolver.java b/mycore-base/src/main/java/org/mycore/common/xml/MCRURIResolver.java index 9764d432ca..72f2c34f9b 100644 --- a/mycore-base/src/main/java/org/mycore/common/xml/MCRURIResolver.java +++ b/mycore-base/src/main/java/org/mycore/common/xml/MCRURIResolver.java @@ -513,7 +513,7 @@ public Source resolve(String href, String base) throws TransformerException { MCRObjectID mcrid = MCRObjectID.getInstance(id); try { - MCRXMLMetadataManager xmlmm = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager xmlmm = MCRXMLMetadataManager.obtainInstance(); MCRContent content; if (params.containsKey("r")) { @@ -795,7 +795,7 @@ private static boolean shouldSortCategories(String classId) { } private static long getSystemLastModified() { - long xmlLastModified = MCRXMLMetadataManager.getInstance().getLastModified(); + long xmlLastModified = MCRXMLMetadataManager.obtainInstance().getLastModified(); long classLastModified = dao.getLastModified(); return Math.max(xmlLastModified, classLastModified); } @@ -1509,7 +1509,7 @@ public Source resolve(String href, String base) throws TransformerException { String id = href.substring(href.indexOf(':') + 1); LOGGER.debug("Reading version info of MCRObject with ID {}", id); MCRObjectID mcrId = MCRObjectID.getInstance(id); - MCRXMLMetadataManager metadataManager = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager metadataManager = MCRXMLMetadataManager.obtainInstance(); try { List> versions = metadataManager.listRevisions(mcrId); if (versions != null && !versions.isEmpty()) { @@ -1564,7 +1564,7 @@ public Source resolve(String href, String base) throws TransformerException { MCRObjectID mcrId = MCRObjectID.getInstance(parts[parts.length - 1]); LOGGER.info("Resolving deleted object {}", mcrId); try { - MCRContent lastPresentVersion = MCRXMLMetadataManager.getInstance().retrieveContent(mcrId); + MCRContent lastPresentVersion = MCRXMLMetadataManager.obtainInstance().retrieveContent(mcrId); if (lastPresentVersion == null) { LOGGER.warn("Could not resolve deleted object {}", mcrId); return new JDOMSource(MCRObjectFactory.getSampleObject(mcrId)); diff --git a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRCreatorCache.java b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRCreatorCache.java index 4fbac4dfb5..937262448c 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRCreatorCache.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRCreatorCache.java @@ -57,7 +57,7 @@ public String load(final MCRObjectID objectId) { return null; }).orElseGet(() -> { try { - return Optional.ofNullable(MCRXMLMetadataManager.getInstance().listRevisions(objectId)) + return Optional.ofNullable(MCRXMLMetadataManager.obtainInstance().listRevisions(objectId)) .map(versions -> versions.stream() .sorted( Collections diff --git a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultObjectIDGenerator.java b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultObjectIDGenerator.java index c7e6a92ceb..4c8f8f59e7 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultObjectIDGenerator.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRDefaultObjectIDGenerator.java @@ -101,7 +101,7 @@ public synchronized MCRObjectID getLastID(String baseId) { @Override public Collection getBaseIDs() { - return MCRXMLMetadataManager.getInstance().getObjectBaseIds(); + return MCRXMLMetadataManager.obtainInstance().getObjectBaseIds(); } /** @@ -111,7 +111,7 @@ public Collection getBaseIDs() { */ private int getLastIDNumber(String baseId) { int lastIDKnown = lastNumber.getOrDefault(baseId, 0); - int highestStoredID = MCRXMLMetadataManager.getInstance().getHighestStoredID(baseId); + int highestStoredID = MCRXMLMetadataManager.obtainInstance().getHighestStoredID(baseId); return Math.max(lastIDKnown, highestStoredID); } diff --git a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataEventHandler.java b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataEventHandler.java index dbc40f9b7b..74e0b2773c 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataEventHandler.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataEventHandler.java @@ -129,7 +129,7 @@ protected final void handleDerivateDeleted(MCREvent evt, MCRDerivate der) { } private void handleStoreEvent(MCREvent evt, MCRBase obj) { - MCRXMLMetadataManager manager = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager manager = MCRXMLMetadataManager.obtainInstance(); MCREvent.EventType eventType = evt.getEventType(); MCRObjectID id = obj.getId(); try { diff --git a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManager.java b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManager.java index b1246f65cc..d9987c2d18 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManager.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/common/MCRXMLMetadataManager.java @@ -85,8 +85,22 @@ public interface MCRXMLMetadataManager { * to perform the metadata operations. * * @return the XML metadata manager + * + * @deprecated Use {@link MCRXMLMetadataManager#obtainInstance()} instead */ + @Deprecated(forRemoval = true) static MCRXMLMetadataManager getInstance() { + return obtainInstance(); + } + + /** + * Returns the singleton instance of {@link MCRXMLMetadataManager}. Reads the property + * MCR.Metadata.Manager.Class to instantiate the configured XML metadata manager adapter to be used + * to perform the metadata operations. + * + * @return the XML metadata manager + */ + static MCRXMLMetadataManager obtainInstance() { return LazyInstanceHolder.SINGLETON_INSTANCE; } diff --git a/mycore-base/src/main/java/org/mycore/datamodel/ifs2/MCRIFS2Commands.java b/mycore-base/src/main/java/org/mycore/datamodel/ifs2/MCRIFS2Commands.java index e8a4a42180..36e310a9a9 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/ifs2/MCRIFS2Commands.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/ifs2/MCRIFS2Commands.java @@ -87,7 +87,7 @@ private static void initFileStores() { } private static void initMetadataStores() { - final MCRXMLMetadataManager metadataManager = MCRXMLMetadataManager.getInstance(); + final MCRXMLMetadataManager metadataManager = MCRXMLMetadataManager.obtainInstance(); metadataManager.getObjectBaseIds().forEach(id -> { final String[] parts = id.split("_"); metadataManager.getHighestStoredID(parts[0], parts[1]); diff --git a/mycore-base/src/main/java/org/mycore/datamodel/metadata/MCRMetadataManager.java b/mycore-base/src/main/java/org/mycore/datamodel/metadata/MCRMetadataManager.java index 91d8534a91..8e0eb433b9 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/metadata/MCRMetadataManager.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/metadata/MCRMetadataManager.java @@ -153,7 +153,7 @@ private static void checkWritePermission(MCRObjectID objectId, MCRObjectID deriv private static byte[] retrieveObjectBackup(MCRObjectID objectId) throws MCRPersistenceException { byte[] objectBackup; try { - objectBackup = MCRXMLMetadataManager.getInstance().retrieveBLOB(objectId); + objectBackup = MCRXMLMetadataManager.obtainInstance().retrieveBLOB(objectId); } catch (IOException ioExc) { throw new MCRPersistenceException("Unable to retrieve xml blob of " + objectId, ioExc); } @@ -457,7 +457,7 @@ public static void deleteMCRObject(final MCRObjectID id) * the xml couldn't be read */ public static boolean exists(final MCRObjectID id) throws MCRPersistenceException { - return MCRXMLMetadataManager.getInstance().exists(id); + return MCRXMLMetadataManager.obtainInstance().exists(id); } /** @@ -526,7 +526,7 @@ public static void fireUpdateEvent(final MCRObject mcrObject) throws MCRPersiste */ public static MCRDerivate retrieveMCRDerivate(final MCRObjectID id) throws MCRPersistenceException { try { - Document xml = MCRXMLMetadataManager.getInstance().retrieveXML(id); + Document xml = MCRXMLMetadataManager.obtainInstance().retrieveXML(id); if (xml == null) { throw new MCRPersistenceException("Could not retrieve xml of derivate: " + id); } @@ -546,7 +546,7 @@ public static MCRDerivate retrieveMCRDerivate(final MCRObjectID id) throws MCRPe */ public static MCRObject retrieveMCRObject(final MCRObjectID id) throws MCRPersistenceException { try { - Document xml = MCRXMLMetadataManager.getInstance().retrieveXML(id); + Document xml = MCRXMLMetadataManager.obtainInstance().retrieveXML(id); if (xml == null) { throw new MCRPersistenceException("Could not retrieve xml of object: " + id); } @@ -810,7 +810,7 @@ private static void restore(final MCRDerivate mcrDerivate, final MCRObjectID mcr // before updating it again. Otherwise the exception could be thrown again and // the object will be in an invalid state (the not existing derivate will be // linked with the object). - MCRXMLMetadataManager.getInstance().update(mcrObjectId, obj.createXML(), new Date()); + MCRXMLMetadataManager.obtainInstance().update(mcrObjectId, obj.createXML(), new Date()); // update and call event handlers update(obj); diff --git a/mycore-base/src/main/java/org/mycore/datamodel/metadata/MCRObjectUtils.java b/mycore-base/src/main/java/org/mycore/datamodel/metadata/MCRObjectUtils.java index 438fa7bd4b..f5a51f8127 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/metadata/MCRObjectUtils.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/metadata/MCRObjectUtils.java @@ -224,7 +224,7 @@ public static T restore(MCRObjectID mcrId, String revision) T mcrBase = (T) (mcrId.getTypeId().equals(MCRDerivate.OBJECT_TYPE) ? new MCRDerivate() : new MCRObject()); // get content - MCRXMLMetadataManager xmlMetadataManager = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager xmlMetadataManager = MCRXMLMetadataManager.obtainInstance(); MCRContent content = xmlMetadataManager.retrieveContent(mcrId, revision); if (content == null) { throw new MCRPersistenceException("No such object " + mcrId + " with revision " + revision + "."); diff --git a/mycore-base/src/main/java/org/mycore/datamodel/metadata/history/MCRMetadataHistoryCommands.java b/mycore-base/src/main/java/org/mycore/datamodel/metadata/history/MCRMetadataHistoryCommands.java index 2377e09f8d..68fdaf61d4 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/metadata/history/MCRMetadataHistoryCommands.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/metadata/history/MCRMetadataHistoryCommands.java @@ -94,7 +94,7 @@ public static void clearSingleHistory(String mcrId) { @MCRCommand(syntax = "clear metadata history completely", help = "clears metadata history completely") public static List clearHistory() { - return MCRXMLMetadataManager.getInstance() + return MCRXMLMetadataManager.obtainInstance() .getObjectBaseIds() .stream() .map(s -> "clear metadata history of base " + s) @@ -103,7 +103,7 @@ public static List clearHistory() { @MCRCommand(syntax = "build metadata history completely", help = "build metadata history completely") public static List buildHistory() { - return MCRXMLMetadataManager.getInstance() + return MCRXMLMetadataManager.obtainInstance() .getObjectBaseIds() .stream() .map(s -> "build metadata history of base " + s) @@ -113,7 +113,7 @@ public static List buildHistory() { @MCRCommand(syntax = "build metadata history of base {0}", help = "build metadata history of all objects with base id {0}") public static List buildHistory(String baseId) { - MCRXMLMetadataManager mm = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager mm = MCRXMLMetadataManager.obtainInstance(); mm.verifyStore(baseId); ExecutorService executorService = Executors.newWorkStealingPool(); MCRSession currentSession = MCRSessionMgr.getCurrentSession(); @@ -161,7 +161,7 @@ private static Stream getHistoryItems(MCRObjectID objId) { private static Stream buildDerivateHistory(MCRObjectID derId) { try { - List> versions = MCRXMLMetadataManager.getInstance() + List> versions = MCRXMLMetadataManager.obtainInstance() .listRevisions(derId); if (versions == null || versions.isEmpty()) { return buildSimpleDerivateHistory(derId); @@ -176,7 +176,7 @@ private static Stream buildDerivateHistory(MCRObjectID derId private static Stream buildObjectHistory(MCRObjectID objId) { try { - List> versions = MCRXMLMetadataManager.getInstance() + List> versions = MCRXMLMetadataManager.obtainInstance() .listRevisions(objId); if (versions == null || versions.isEmpty()) { return buildSimpleObjectHistory(objId); @@ -194,7 +194,7 @@ private static Stream buildSimpleDerivateHistory(MCRObjectID if (MCRMetadataManager.exists(derId)) { MCRDerivate der = MCRMetadataManager.retrieveMCRDerivate(derId); Instant lastModified = Instant - .ofEpochMilli(MCRXMLMetadataManager.getInstance().getLastModified(derId)); + .ofEpochMilli(MCRXMLMetadataManager.obtainInstance().getLastModified(derId)); String creator; try { creator = MCRCreatorCache.getCreator(der.getId()); @@ -222,7 +222,7 @@ private static Stream buildSimpleObjectHistory(MCRObjectID o if (MCRMetadataManager.exists(objId)) { MCRObject obj = MCRMetadataManager.retrieveMCRObject(objId); Instant lastModified = Instant - .ofEpochMilli(MCRXMLMetadataManager.getInstance().getLastModified(objId)); + .ofEpochMilli(MCRXMLMetadataManager.obtainInstance().getLastModified(objId)); String creator; try { creator = MCRCreatorCache.getCreator(obj.getId()); diff --git a/mycore-base/src/main/java/org/mycore/datamodel/objectinfo/MCRObjectInfoCommands.java b/mycore-base/src/main/java/org/mycore/datamodel/objectinfo/MCRObjectInfoCommands.java index 61de2b47db..103d47899d 100644 --- a/mycore-base/src/main/java/org/mycore/datamodel/objectinfo/MCRObjectInfoCommands.java +++ b/mycore-base/src/main/java/org/mycore/datamodel/objectinfo/MCRObjectInfoCommands.java @@ -52,7 +52,7 @@ public static void deleteAllObjectInfo() { help = "reads all objects and creates the corresponding objectinfo", order = 10) public static List createAllObjectInfo() { - MCRXMLMetadataManager mm = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager mm = MCRXMLMetadataManager.obtainInstance(); return mm.getObjectBaseIds() .stream().filter(b -> !b.endsWith(MCRDerivate.OBJECT_TYPE)) .map(b -> "create objectinfo for base " + b) @@ -63,7 +63,7 @@ public static List createAllObjectInfo() { help = "reads all objects with base id {0} and creates the corresponding objectinfo") public static List createObjectInfoForBase(String baseId) { String[] idParts = baseId.split("_"); - MCRXMLMetadataManager mm = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager mm = MCRXMLMetadataManager.obtainInstance(); if (idParts[1].equals(MCRDerivate.OBJECT_TYPE)) { return List.of(); } @@ -89,7 +89,7 @@ public static void createObjectInfoForObject(String idStr) { LogManager.getLogger().info(() -> "objectinfo for object " + idStr + " created."); } else { - List> versions = MCRXMLMetadataManager.getInstance() + List> versions = MCRXMLMetadataManager.obtainInstance() .listRevisions(id); if (versions == null || versions.isEmpty()) { // we do not know if the object ever existed diff --git a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandUtils.java b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandUtils.java index efe2d85be2..f3f00246eb 100644 --- a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandUtils.java +++ b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRCommandUtils.java @@ -70,7 +70,7 @@ public static Stream getIdsForType(final String type) throws MCRUsageExc if (type == null || type.isEmpty()) { throw new MCRUsageException("Type required to enumerate IDs!"); } - List idList = MCRXMLMetadataManager.getInstance().listIDsOfType(type); + List idList = MCRXMLMetadataManager.obtainInstance().listIDsOfType(type); if (idList.isEmpty()) { LOGGER.warn("No IDs found for type {}.", type); } @@ -114,7 +114,7 @@ public static Stream getIdsForBaseId(final String base) throws MCRUsageE if (MCRObjectID.getIDParts(base).length != 2) { throw new MCRUsageException("Base ID ({project}_{type}) required to enumerate IDs!"); } - List idList = MCRXMLMetadataManager.getInstance().listIDsForBase(base); + List idList = MCRXMLMetadataManager.obtainInstance().listIDsForBase(base); if (idList.isEmpty()) { LOGGER.warn("No IDs found for base {}.", base); } @@ -233,7 +233,7 @@ public static List selectWithXpath(String xPath, Predicate filte .compile(xPath, Filters.fpassthrough(), null, MCRConstants.getStandardNamespaces()); return MCRXMLMetadataManager - .getInstance() + .obtainInstance() .listIDs() .stream() .filter(filter) diff --git a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRDerivateCommands.java b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRDerivateCommands.java index e31fbc1c12..aae72d3579 100644 --- a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRDerivateCommands.java +++ b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRDerivateCommands.java @@ -693,7 +693,7 @@ public static void checkObjectsInDerivates(String baseId) { LOGGER.error("The given base ID {} has not the syntax of project_type", baseId); return; } - MCRXMLMetadataManager mgr = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager mgr = MCRXMLMetadataManager.obtainInstance(); String project = baseId.substring(0, projectPartPosition + 1); List idList = mgr.listIDsForBase(project + MCRDerivate.OBJECT_TYPE); int counter = 0; diff --git a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java index 6c43ed9747..d7864ee0e2 100644 --- a/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java +++ b/mycore-base/src/main/java/org/mycore/frontend/cli/MCRObjectCommands.java @@ -209,7 +209,7 @@ public static List deleteAllObjects(String type) { help = "Removes all MCRObjects in topological order.", order = 25) public static List deleteTopologicalAllObjects() { - final List objectIds = MCRXMLMetadataManager.getInstance().listIDs(); + final List objectIds = MCRXMLMetadataManager.obtainInstance().listIDs(); String[] objects = objectIds.stream().filter(OBJECT_ID_PREDICATE).toArray(String[]::new); MCRTopologicalSort ts = new MCRTopologicalSort<>(); MCRTopologicalSort.prepareMCRObjects(ts, objects); @@ -230,7 +230,7 @@ public static List deleteTopologicalAllObjects() { help = "Checks if there are circular dependencies in the parent child relationships of MCRObjects.", order = 25) public static void checkForCircles() { - final List objectIds = MCRXMLMetadataManager.getInstance().listIDs(); + final List objectIds = MCRXMLMetadataManager.obtainInstance().listIDs(); String[] objects = objectIds.stream().filter(OBJECT_ID_PREDICATE).toArray(String[]::new); MCRTopologicalSort ts = new MCRTopologicalSort<>(); MCRTopologicalSort.prepareMCRObjects(ts, objects); @@ -704,7 +704,7 @@ private static void exportWith(String fromID, String toID, String dirname, Strin "with the stylesheet {2}-object.xsl. For {2}, the default is xsl/save.", order = 120) public static List exportAllObjectsOfTypeWithStylesheet(String type, String dirname, String style) { - List objectIds = MCRXMLMetadataManager.getInstance().listIDsOfType(type); + List objectIds = MCRXMLMetadataManager.obtainInstance().listIDsOfType(type); return buildExportCommands(new File(dirname), style, objectIds); } @@ -726,7 +726,7 @@ public static List exportAllObjectsOfTypeWithStylesheet(String type, Str "with the stylesheet {2}-object.xsl. For {2}, the default is xsl/save.", order = 130) public static List exportAllObjectsOfBaseWithStylesheet(String base, String dirname, String style) { - List objectIds = MCRXMLMetadataManager.getInstance().listIDsForBase(base); + List objectIds = MCRXMLMetadataManager.obtainInstance().listIDsForBase(base); return buildExportCommands(new File(dirname), style, objectIds); } @@ -786,7 +786,7 @@ private static boolean exportObject(File dir, String extension, MCRContent content; try { // if object doesn't exist - no exception is caught! - content = MCRXMLMetadataManager.getInstance().retrieveContent(MCRObjectID.getInstance(nid)); + content = MCRXMLMetadataManager.obtainInstance().retrieveContent(MCRObjectID.getInstance(nid)); } catch (MCRException ex) { return false; } @@ -828,7 +828,7 @@ public static void listRevisions(String id) { MCRObjectID mcrId = MCRObjectID.getInstance(id); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT); try { - List> revisions = MCRXMLMetadataManager.getInstance() + List> revisions = MCRXMLMetadataManager.obtainInstance() .listRevisions(mcrId); LOGGER.info(() -> { StringBuilder log = new StringBuilder("Revisions:\n"); @@ -957,7 +957,7 @@ private static void xslt(String objectId, String xslFilePath, boolean force) thr } MCRSourceContent style = new MCRSourceContent(xslSource); MCRObjectID mcrId = MCRObjectID.getInstance(objectId); - Document document = MCRXMLMetadataManager.getInstance().retrieveXML(mcrId); + Document document = MCRXMLMetadataManager.obtainInstance().retrieveXML(mcrId); // do XSL transform TransformerFactory transformerFactory = TransformerFactory.newInstance(); transformerFactory.setErrorListener(new MCRErrorListener()); @@ -1150,7 +1150,7 @@ private static void doValidateObjectAgainstSchema(MCRObjectID objID, Transformer // MCRMetadataManager -> retrieveMCRObject() -> MCRObject.createXML already validates the contents // we need to offer transformation first though, so manual talking to MCRXMLMetadataManager // for the object contents, then manually using a validating XML parser later - MCRXMLMetadataManager mgr = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager mgr = MCRXMLMetadataManager.obtainInstance(); Document doc; try { doc = mgr.retrieveXML(objID); @@ -1228,7 +1228,7 @@ public static List repairMetadataSearchForBase(String baseID) { help = "Creates a cache for all object ids in the configuration directory.", order = 175) public static void createObjectIDCache() { - MCRXMLMetadataManager metadataManager = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager metadataManager = MCRXMLMetadataManager.obtainInstance(); metadataManager.getObjectBaseIds().forEach(id -> { LOGGER.info("Creating cache for base {}", id); int highestStoredID = metadataManager.getHighestStoredID(id); diff --git a/mycore-base/src/main/java/org/mycore/frontend/servlets/MCRObjectServlet.java b/mycore-base/src/main/java/org/mycore/frontend/servlets/MCRObjectServlet.java index 9f3b89a525..ff3419c896 100644 --- a/mycore-base/src/main/java/org/mycore/frontend/servlets/MCRObjectServlet.java +++ b/mycore-base/src/main/java/org/mycore/frontend/servlets/MCRObjectServlet.java @@ -116,7 +116,7 @@ private MCRContent requestVersionedObject(final MCRObjectID mcrid, final HttpSer currentSession.getUserInformation().getUserID(), currentSession.getCurrentIP())); return null; } - MCRXMLMetadataManager xmlMetadataManager = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager xmlMetadataManager = MCRXMLMetadataManager.obtainInstance(); if (xmlMetadataManager.listRevisions(mcrid) != null) { MCRContent content = xmlMetadataManager.retrieveContent(mcrid, rev); if (content != null) { diff --git a/mycore-base/src/main/java/org/mycore/services/zipper/MCRCompressServlet.java b/mycore-base/src/main/java/org/mycore/services/zipper/MCRCompressServlet.java index 5d2fa11a49..65fccc94a2 100644 --- a/mycore-base/src/main/java/org/mycore/services/zipper/MCRCompressServlet.java +++ b/mycore-base/src/main/java/org/mycore/services/zipper/MCRCompressServlet.java @@ -168,11 +168,11 @@ protected void render(MCRServletJob job, Exception ex) throws Exception { } private void sendObject(MCRObjectID id, MCRServletJob job, T container) throws Exception { - MCRContent content = MCRXMLMetadataManager.getInstance().retrieveContent(id); + MCRContent content = MCRXMLMetadataManager.obtainInstance().retrieveContent(id); if (content == null) { throw new FileNotFoundException("Could not find object: " + id); } - long lastModified = MCRXMLMetadataManager.getInstance().getLastModified(id); + long lastModified = MCRXMLMetadataManager.obtainInstance().getLastModified(id); HttpServletRequest req = job.getRequest(); byte[] metaDataContent = getMetaDataContent(content, req); sendMetadataCompressed("metadata.xml", metaDataContent, lastModified, container); diff --git a/mycore-base/src/test/java/org/mycore/datamodel/common/MCRXMLMetadataManagerTest.java b/mycore-base/src/test/java/org/mycore/datamodel/common/MCRXMLMetadataManagerTest.java index bab790ed55..a3c1c47343 100644 --- a/mycore-base/src/test/java/org/mycore/datamodel/common/MCRXMLMetadataManagerTest.java +++ b/mycore-base/src/test/java/org/mycore/datamodel/common/MCRXMLMetadataManagerTest.java @@ -208,7 +208,7 @@ public void listIDs() { } private MCRXMLMetadataManager getStore() { - return MCRXMLMetadataManager.getInstance(); + return MCRXMLMetadataManager.obtainInstance(); } private static class XMLInfo { diff --git a/mycore-base/src/test/java/org/mycore/datamodel/metadata/MCRObjectIDTest.java b/mycore-base/src/test/java/org/mycore/datamodel/metadata/MCRObjectIDTest.java index d77c7d8620..633a2ac7bd 100644 --- a/mycore-base/src/test/java/org/mycore/datamodel/metadata/MCRObjectIDTest.java +++ b/mycore-base/src/test/java/org/mycore/datamodel/metadata/MCRObjectIDTest.java @@ -78,7 +78,7 @@ public void setNextFreeIdString() { assertEquals(1, id1.getNumberAsInteger(), "First id should be int 1"); MCRObjectID id2 = MCRMetadataManager.getMCRObjectIDGenerator().getNextFreeId(BASE_ID); assertEquals(2, id2.getNumberAsInteger(), "Second id should be int 2"); - MCRXMLMetadataManager.getInstance().create(id2, new Document(new Element("test")), new Date()); + MCRXMLMetadataManager.obtainInstance().create(id2, new Document(new Element("test")), new Date()); MCRObjectID id3 = MCRMetadataManager.getMCRObjectIDGenerator().getNextFreeId(BASE_ID); assertEquals(3, id3.getNumberAsInteger(), "Second id should be int 3"); } diff --git a/mycore-base/src/test/java/org/mycore/test/MCRMetadataExtension.java b/mycore-base/src/test/java/org/mycore/test/MCRMetadataExtension.java index 1cff35757c..5fe8086417 100644 --- a/mycore-base/src/test/java/org/mycore/test/MCRMetadataExtension.java +++ b/mycore-base/src/test/java/org/mycore/test/MCRMetadataExtension.java @@ -80,7 +80,7 @@ public void beforeEach(ExtensionContext context) throws Exception { (key, value) -> LOGGER.debug("MCR Metadata Store Property: {}={}", key, value)); Files.createDirectories(getStoreBaseDir(context)); Files.createDirectories(getSvnBaseDir(context)); - MCRXMLMetadataManager.getInstance().reload(); + MCRXMLMetadataManager.obtainInstance().reload(); } @Override diff --git a/mycore-base/src/test/java/org/mycore/test/tests/MCRMetadataExtensionTest.java b/mycore-base/src/test/java/org/mycore/test/tests/MCRMetadataExtensionTest.java index 5dc62ac758..c23b73d3d8 100644 --- a/mycore-base/src/test/java/org/mycore/test/tests/MCRMetadataExtensionTest.java +++ b/mycore-base/src/test/java/org/mycore/test/tests/MCRMetadataExtensionTest.java @@ -53,7 +53,7 @@ public void testXMLMetadataManagerAvailable() { assertTrue(Files.isDirectory(storeBaseDir), "Store base dir should be a directory"); assertTrue(storeBaseDir.getFileName().toString().startsWith("mcr-store"), "Store base dir should start with 'mcr-store'"); - assertFalse(MCRXMLMetadataManager.getInstance().exists(MCRObjectID.getInstance("MyCoRe_test_00004711")), + assertFalse(MCRXMLMetadataManager.obtainInstance().exists(MCRObjectID.getInstance("MyCoRe_test_00004711")), "MCRXMLMetadataManager should be available but the object should not exist"); } diff --git a/mycore-migration/src/main/java/org/mycore/migration/cli/MCRMigrationCommands.java b/mycore-migration/src/main/java/org/mycore/migration/cli/MCRMigrationCommands.java index 278d7d977e..54549a825a 100644 --- a/mycore-migration/src/main/java/org/mycore/migration/cli/MCRMigrationCommands.java +++ b/mycore-migration/src/main/java/org/mycore/migration/cli/MCRMigrationCommands.java @@ -98,7 +98,7 @@ public class MCRMigrationCommands { + " (default: NeverAddChildrenOrderStrategy) to decide if should become .", order = 30) public static void migrateNormalizedObject(String mcrObjectIDStr) throws IOException, JDOMException { - MCRXMLMetadataManager mm = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager mm = MCRXMLMetadataManager.obtainInstance(); // TODO: check if child order needs to be migrated @@ -137,10 +137,10 @@ public static void migrateNormalizedObject(String mcrObjectIDStr) throws IOExcep + " (default: NeverAddChildrenOrderStrategy) to decide if should become .", order = 30) public static void migrateNormalizedObjects() throws SVNException, IOException, JDOMException { - Collection objectBaseIds = MCRXMLMetadataManager.getInstance().getObjectBaseIds(); + Collection objectBaseIds = MCRXMLMetadataManager.obtainInstance().getObjectBaseIds(); objectBaseIds.removeIf(id -> id.endsWith("_derivate")); for (String baseId : objectBaseIds) { - MCRXMLMetadataManager.getInstance().getHighestStoredID(baseId); //init store + MCRXMLMetadataManager.obtainInstance().getHighestStoredID(baseId); //init store MCRStore store = MCRStoreManager.getStore(baseId); LOGGER.info(() -> "MCR store " + baseId + " has been initialized: " + store.getClass().getName()); if (store instanceof MCRVersioningMetadataStore versioningStore) { @@ -154,7 +154,7 @@ public static void migrateNormalizedObjects() throws SVNException, IOException, help = "Create missing servflags for createdby and modifiedby. (MCR-786)", order = 20) public static List addServFlags() { - SortedSet ids = new TreeSet<>(MCRXMLMetadataManager.getInstance().listIDs()); + SortedSet ids = new TreeSet<>(MCRXMLMetadataManager.obtainInstance().listIDs()); List cmds = new ArrayList<>(ids.size()); for (String id : ids) { cmds.add("migrate author servflags for " + id); @@ -171,7 +171,7 @@ public static void addServFlags(String id) MCRBase obj = MCRMetadataManager.retrieve(objectID); MCRObjectService service = obj.getService(); if (!service.isFlagTypeSet(MCRObjectService.FLAG_TYPE_CREATEDBY)) { //the egg - List> versions = MCRXMLMetadataManager.getInstance() + List> versions = MCRXMLMetadataManager.obtainInstance() .listRevisions(objectID); String createUser; String modifyUser; @@ -226,7 +226,7 @@ public static void fixDerivateLinks(String xpath, String id) throws IOException, MCRObjectID objectID = MCRObjectID.getInstance(id); // find derivate links - Document xml = MCRXMLMetadataManager.getInstance().retrieveXML(objectID); + Document xml = MCRXMLMetadataManager.obtainInstance().retrieveXML(objectID); Element mcrObjectXML = xml.getRootElement(); XPathExpression expression = XPathFactory.instance().compile(xpath, Filters.element()); List derivateLinkElements = expression.evaluate(mcrObjectXML); @@ -272,7 +272,7 @@ public static void fixDerivateLinks(String xpath, String id) throws IOException, // store the mcr object if its changed if (changedObject) { // we use MCRXMLMetadataMananger because we don't want to validate the old mcr object - MCRXMLMetadataManager.getInstance().update(objectID, xml, new Date()); + MCRXMLMetadataManager.obtainInstance().update(objectID, xml, new Date()); // manually fire update event MCRObject newObject = MCRMetadataManager.retrieveMCRObject(objectID); newObject.setImportMode(true); diff --git a/mycore-migration/src/test/java/org/mycore/migration/cli/MCRMigrationCommandsTest.java b/mycore-migration/src/test/java/org/mycore/migration/cli/MCRMigrationCommandsTest.java index ae20e27109..ab745227a6 100644 --- a/mycore-migration/src/test/java/org/mycore/migration/cli/MCRMigrationCommandsTest.java +++ b/mycore-migration/src/test/java/org/mycore/migration/cli/MCRMigrationCommandsTest.java @@ -107,7 +107,7 @@ public void migrateChildrenOrder() throws IOException, JDOMException { MCRMigrationCommands.migrateNormalizedObject(testID1.toString()); - Document migratedObject1 = MCRXMLMetadataManager.getInstance().retrieveXML(testID1); + Document migratedObject1 = MCRXMLMetadataManager.obtainInstance().retrieveXML(testID1); checkChildrenOrder(migratedObject1, testID5, testID2, testID4); verifyNoChildrenElementLeft(migratedObject1); checkNoGeneratedClassifications(migratedObject1); @@ -118,14 +118,14 @@ public void migrateChildrenOrder() throws IOException, JDOMException { .getChild(MCRObjectStructure.ELEMENT_DERIVATE_OBJECTS), "The derobjects should be removed"); MCRMigrationCommands.migrateNormalizedObject(testID2.toString()); - Document migratedObject2 = MCRXMLMetadataManager.getInstance().retrieveXML(testID2); + Document migratedObject2 = MCRXMLMetadataManager.obtainInstance().retrieveXML(testID2); checkChildrenOrder(migratedObject2, testID7, testID3, testID6); verifyNoChildrenElementLeft(migratedObject2); checkNoGeneratedClassifications(migratedObject2); checkRelatedItemIsEmpty(migratedObject2); MCRMigrationCommands.migrateNormalizedObject(testID7.toString()); - Document migratedObject7 = MCRXMLMetadataManager.getInstance().retrieveXML(testID7); + Document migratedObject7 = MCRXMLMetadataManager.obtainInstance().retrieveXML(testID7); checkNoGeneratedClassifications(migratedObject7); checkRelatedItemIsEmpty(migratedObject7); @@ -148,7 +148,7 @@ private MCRObjectID createTestData(String file) throws IOException, JDOMExceptio Document build = builder.build(resourceAsStream); String id = build.getRootElement().getAttributeValue("ID"); objectID = MCRObjectID.getInstance(id); - MCRXMLMetadataManager.getInstance().create(objectID, build, new Date()); + MCRXMLMetadataManager.obtainInstance().create(objectID, build, new Date()); } return objectID; } diff --git a/mycore-mods/src/main/java/org/mycore/mods/MCRMODSEmbargoUtils.java b/mycore-mods/src/main/java/org/mycore/mods/MCRMODSEmbargoUtils.java index 070b507d62..df35064343 100644 --- a/mycore-mods/src/main/java/org/mycore/mods/MCRMODSEmbargoUtils.java +++ b/mycore-mods/src/main/java/org/mycore/mods/MCRMODSEmbargoUtils.java @@ -67,7 +67,7 @@ protected void handleObjectUpdated(MCREvent evt, MCRObject obj) { } public static String getCachedEmbargo(final MCRObjectID objectId) { - MCRCache.ModifiedHandle modifiedHandle = MCRXMLMetadataManager.getInstance().getLastModifiedHandle(objectId, 10, + MCRCache.ModifiedHandle modifiedHandle = MCRXMLMetadataManager.obtainInstance().getLastModifiedHandle(objectId, 10, TimeUnit.MINUTES); String embargo = null; try { diff --git a/mycore-mods/src/main/java/org/mycore/mods/csl/MCRListModsItemDataProvider.java b/mycore-mods/src/main/java/org/mycore/mods/csl/MCRListModsItemDataProvider.java index 161a32fe43..02d30e4716 100644 --- a/mycore-mods/src/main/java/org/mycore/mods/csl/MCRListModsItemDataProvider.java +++ b/mycore-mods/src/main/java/org/mycore/mods/csl/MCRListModsItemDataProvider.java @@ -58,7 +58,7 @@ public void addContent(MCRContent content) throws IOException, JDOMException, SA Element copy = object.clone().detach(); String objectID = copy.getAttributeValue(MCRXMLConstants.ID); MCRObjectID mcrObjectID = MCRObjectID.getInstance(objectID); - CSLItemData itemData = CSL_CACHE.getIfUpToDate(objectID, MCRXMLMetadataManager.getInstance() + CSLItemData itemData = CSL_CACHE.getIfUpToDate(objectID, MCRXMLMetadataManager.obtainInstance() .getLastModified(mcrObjectID)); if (itemData == null) { MCRModsItemDataProvider midp = new MCRModsItemDataProvider(); diff --git a/mycore-mods/src/test/java/org/mycore/mods/csl/MCRListModsItemDataProviderTest.java b/mycore-mods/src/test/java/org/mycore/mods/csl/MCRListModsItemDataProviderTest.java index f1f949f338..65da8a6656 100644 --- a/mycore-mods/src/test/java/org/mycore/mods/csl/MCRListModsItemDataProviderTest.java +++ b/mycore-mods/src/test/java/org/mycore/mods/csl/MCRListModsItemDataProviderTest.java @@ -63,7 +63,7 @@ public void setUp() throws Exception { List testContent = getTestContent(); for (MCRContent content : testContent) { Document jdom = content.asXML(); - MCRXMLMetadataManager.getInstance().create(MCRObjectID.getInstance(getIDFromContent(content)), jdom, + MCRXMLMetadataManager.obtainInstance().create(MCRObjectID.getInstance(getIDFromContent(content)), jdom, new Date()); } } diff --git a/mycore-neo4j/src/main/java/org/mycore/mcr/neo4j/frontend/cli/MCRNeo4JCommands.java b/mycore-neo4j/src/main/java/org/mycore/mcr/neo4j/frontend/cli/MCRNeo4JCommands.java index 5ca7187c36..9506cda452 100644 --- a/mycore-neo4j/src/main/java/org/mycore/mcr/neo4j/frontend/cli/MCRNeo4JCommands.java +++ b/mycore-neo4j/src/main/java/org/mycore/mcr/neo4j/frontend/cli/MCRNeo4JCommands.java @@ -57,7 +57,7 @@ public static void cleanForMCRID(final String id) { public static void cleanForBase(final String baseId) { LOGGER.info("Start clean data from Neo4J instance for MCRBase {}", baseId); List selectedObjectIds = MCRXMLMetadataManager - .getInstance().listIDsForBase(baseId); + .obtainInstance().listIDsForBase(baseId); for (String objectId : selectedObjectIds) { String queryString = "MATCH (n {id:'" + objectId + "'}) DETACH DELETE n"; LOGGER.info("Query: {}", queryString); @@ -102,7 +102,7 @@ public static void synchronizeForBase(final String baseId) { MCRNeo4JManager clazz = MCRConfiguration2.getInstanceOfOrThrow(MCRNeo4JManager.class, NEO4J_MANAGER_CLASS_PROPERTY); List selectedObjectIds = MCRXMLMetadataManager - .getInstance().listIDsForBase(baseId); + .obtainInstance().listIDsForBase(baseId); for (String objectId : selectedObjectIds) { MCRObject mcrObject = MCRMetadataManager.retrieveMCRExpandedObject(MCRObjectID.getInstance(objectId)); clazz.updateNodeByMCRObject(mcrObject); @@ -117,7 +117,7 @@ public static void synchronizeForType(final String type) { MCRNeo4JManager clazz = MCRConfiguration2.getInstanceOfOrThrow(MCRNeo4JManager.class, NEO4J_MANAGER_CLASS_PROPERTY); List selectedObjectIds = MCRXMLMetadataManager - .getInstance().listIDsOfType(type); + .obtainInstance().listIDsOfType(type); for (String objectId : selectedObjectIds) { MCRObject mcrObject = MCRMetadataManager.retrieveMCRExpandedObject(MCRObjectID.getInstance(objectId)); clazz.updateNodeByMCRObject(mcrObject); @@ -130,7 +130,7 @@ public static void synchronizeAll() { MCRNeo4JManager clazz = MCRConfiguration2.getInstanceOfOrThrow(MCRNeo4JManager.class, NEO4J_MANAGER_CLASS_PROPERTY); List selectedObjectIds = MCRXMLMetadataManager - .getInstance() + .obtainInstance() .listIDs(); for (String objectId : selectedObjectIds) { MCRObject mcrObject = MCRMetadataManager.retrieveMCRExpandedObject(MCRObjectID.getInstance(objectId)); diff --git a/mycore-oai/src/main/java/org/mycore/oai/MCROAIObjectManager.java b/mycore-oai/src/main/java/org/mycore/oai/MCROAIObjectManager.java index f60e810188..551bcb463b 100644 --- a/mycore-oai/src/main/java/org/mycore/oai/MCROAIObjectManager.java +++ b/mycore-oai/src/main/java/org/mycore/oai/MCROAIObjectManager.java @@ -173,7 +173,7 @@ protected boolean exists(String oaiId) { String mcrId = oaiId.substring(getOAIIDPrefix().length()); try { MCRObjectID mcrObjId = MCRObjectID.getInstance(mcrId); - return MCRXMLMetadataManager.getInstance().exists(mcrObjId); + return MCRXMLMetadataManager.obtainInstance().exists(mcrObjId); } catch (Exception ex) { return false; } diff --git a/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLCommands.java b/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLCommands.java index 9854220a9a..d85a59fee0 100644 --- a/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLCommands.java +++ b/mycore-ocfl/src/main/java/org/mycore/ocfl/commands/MCROCFLCommands.java @@ -330,9 +330,9 @@ public static void restoreObjFromOCFLVersioned(String mcridString, String revisi manager.setRepositoryKey(MCRConfiguration2.getStringOrThrow("MCR.Metadata.Manager.Repository")); MCRContent content = manager.retrieveContent(mcrid, revision); try { - MCRXMLMetadataManager.getInstance().update(mcrid, content, new Date(content.lastModified())); + MCRXMLMetadataManager.obtainInstance().update(mcrid, content, new Date(content.lastModified())); } catch (MCRUsageException e) { - MCRXMLMetadataManager.getInstance().create(mcrid, content, new Date(content.lastModified())); + MCRXMLMetadataManager.obtainInstance().create(mcrid, content, new Date(content.lastModified())); } } @@ -344,7 +344,7 @@ public static List restoreDerivateFromOCFL(String derivateId, String rev MCROCFLFileSystemProvider.get().getFileSystem().restoreRoot(derivateId, revision); // update metadata MCRObjectID mcrDerivateId = MCRObjectID.getInstance(derivateId); - Document derivateXml = MCRXMLMetadataManager.getInstance().retrieveXML(mcrDerivateId); + Document derivateXml = MCRXMLMetadataManager.obtainInstance().retrieveXML(mcrDerivateId); MCRDerivate mcrDerivate = new MCRDerivate(derivateXml); MCRMetadataManager.update(mcrDerivate); // tile @@ -469,7 +469,7 @@ public static void purgeMarkedUsers() { @MCRCommand(syntax = "migrate derivates to ocfl", help = "migrates all ifs2 derivates to ocfl") public static List migrateDerivates() { - List derivateIds = MCRXMLMetadataManager.getInstance().listIDsOfType("derivate"); + List derivateIds = MCRXMLMetadataManager.obtainInstance().listIDsOfType("derivate"); return derivateIds.stream().map(derivateId -> { return "migrate derivate " + derivateId + " to ocfl"; }).toList(); @@ -497,7 +497,7 @@ public static List validateDerivates() throws IOException { Files.deleteIfExists(errorFilePath); LOGGER.info("Validation errors will be written to: '{}'. If this file does not exists, all derivates " + "are successfully migrated to ocfl and can be removed from ifs2.", errorFilePath); - List derivateIds = MCRXMLMetadataManager.getInstance().listIDsOfType("derivate"); + List derivateIds = MCRXMLMetadataManager.obtainInstance().listIDsOfType("derivate"); return derivateIds.stream().map(derivateId -> { return "validate ocfl derivate " + derivateId; }).toList(); diff --git a/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/migration/MCROCFLMigration.java b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/migration/MCROCFLMigration.java index bbfac0977d..58c2d6d803 100644 --- a/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/migration/MCROCFLMigration.java +++ b/mycore-ocfl/src/main/java/org/mycore/ocfl/metadata/migration/MCROCFLMigration.java @@ -169,7 +169,7 @@ private void migrateID(String id) { } } - MCRXMLMetadataManager instance = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager instance = MCRXMLMetadataManager.obtainInstance(); // does it even exist? if (instance.exists(objectID)) { @@ -230,7 +230,7 @@ private MCRContent retriveActualContent(MCRAbstractMetadataVersion rev) throw private List> readRevisions(MCRObjectID objectID) { List> revisions = null; - MCRXMLMetadataManager instance = MCRXMLMetadataManager.getInstance(); + MCRXMLMetadataManager instance = MCRXMLMetadataManager.obtainInstance(); try { revisions = instance.listRevisions(objectID); diff --git a/mycore-orcid/src/main/java/org/mycore/orcid/works/MCRWorksPublisher.java b/mycore-orcid/src/main/java/org/mycore/orcid/works/MCRWorksPublisher.java index 8ee718c236..26937ebe43 100644 --- a/mycore-orcid/src/main/java/org/mycore/orcid/works/MCRWorksPublisher.java +++ b/mycore-orcid/src/main/java/org/mycore/orcid/works/MCRWorksPublisher.java @@ -109,7 +109,7 @@ private Builder buildInvocation(WebTarget target) { /** Retrieves the MyCoRe object, transforms it to ORCID work xml and validates */ private Document buildWorkXMLFrom(MCRObjectID objectID) throws IOException, JDOMException { - MCRContent mcrObject = MCRXMLMetadataManager.getInstance().retrieveContent(objectID); + MCRContent mcrObject = MCRXMLMetadataManager.obtainInstance().retrieveContent(objectID); MCRContent workXML = T_MCR2WORK.transform(mcrObject); return MCRXMLParserFactory.getValidatingParser().parseXML(workXML); } diff --git a/mycore-pi/src/main/java/org/mycore/pi/cli/MCRPICommands.java b/mycore-pi/src/main/java/org/mycore/pi/cli/MCRPICommands.java index 32da3a7786..a5270b32ae 100644 --- a/mycore-pi/src/main/java/org/mycore/pi/cli/MCRPICommands.java +++ b/mycore-pi/src/main/java/org/mycore/pi/cli/MCRPICommands.java @@ -119,7 +119,7 @@ public static void removeFlagsFromObject(String mycoreIDString) { order = 30) public static void migrateURNGranularToServiceID(String serviceID) { EntityManager em = MCREntityManagerProvider.getCurrentEntityManager(); - MCRXMLMetadataManager.getInstance().listIDsOfType(MCRDerivate.OBJECT_TYPE).forEach(derivateID -> { + MCRXMLMetadataManager.obtainInstance().listIDsOfType(MCRDerivate.OBJECT_TYPE).forEach(derivateID -> { MCRDerivate derivate = MCRMetadataManager.retrieveMCRDerivate(MCRObjectID.getInstance(derivateID)); String urn = derivate.getDerivate().getURN(); diff --git a/mycore-restapi/src/main/java/org/mycore/restapi/v1/utils/MCRRestAPIObjectsHelper.java b/mycore-restapi/src/main/java/org/mycore/restapi/v1/utils/MCRRestAPIObjectsHelper.java index 1437dca9d2..5f9f61465c 100644 --- a/mycore-restapi/src/main/java/org/mycore/restapi/v1/utils/MCRRestAPIObjectsHelper.java +++ b/mycore-restapi/src/main/java/org/mycore/restapi/v1/utils/MCRRestAPIObjectsHelper.java @@ -378,17 +378,17 @@ private static Set retrieveMCRIDs(FilterParams params) { Set mcrIDs = new HashSet<>(); if (params.projectIDs.isEmpty()) { if (params.typeIDs.isEmpty()) { - mcrIDs = MCRXMLMetadataManager.getInstance().listIDs().stream().filter(id -> !id.contains("_derivate_")) + mcrIDs = MCRXMLMetadataManager.obtainInstance().listIDs().stream().filter(id -> !id.contains("_derivate_")) .collect(Collectors.toSet()); } else { for (String type : params.typeIDs) { - mcrIDs.addAll(MCRXMLMetadataManager.getInstance().listIDsOfType(type)); + mcrIDs.addAll(MCRXMLMetadataManager.obtainInstance().listIDsOfType(type)); } } } else { for (String project : params.projectIDs) { for (String type : params.typeIDs) { - mcrIDs.addAll(MCRXMLMetadataManager.getInstance().listIDsForBase(project + "_" + type)); + mcrIDs.addAll(MCRXMLMetadataManager.obtainInstance().listIDsForBase(project + "_" + type)); } } } @@ -399,7 +399,7 @@ private static List filterByModificationDates(Set mcrID throws MCRRestAPIException { List objIdDates; try { - objIdDates = MCRXMLMetadataManager.getInstance().retrieveObjectDates(new ArrayList<>(mcrIDs)); + objIdDates = MCRXMLMetadataManager.obtainInstance().retrieveObjectDates(new ArrayList<>(mcrIDs)); } catch (IOException e) { MCRRestAPIException restAPIException = new MCRRestAPIException(Status.INTERNAL_SERVER_ERROR, new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, GENERAL_ERROR_MSG, e.getMessage())); @@ -492,7 +492,7 @@ protected static MCRObjectIDDate createMCRObjectIDDate(MCRObjectID id) { { try { - lastModified = MCRXMLMetadataManager.getInstance().getLastModified(id); + lastModified = MCRXMLMetadataManager.obtainInstance().getLastModified(id); } catch (IOException e) { lastModified = 0; LOGGER.error("Exception while getting last modified of {}", id, e); diff --git a/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestDerivates.java b/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestDerivates.java index 8b262e66af..1c2391006a 100644 --- a/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestDerivates.java +++ b/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestDerivates.java @@ -155,7 +155,7 @@ public static void validateDerivateRelation(MCRObjectID mcrId, MCRObjectID derId @XmlElementWrapper(name = MCRObjectStructure.ELEMENT_DERIVATE_OBJECTS) public Response listDerivates() throws IOException { - long modified = MCRXMLMetadataManager.getInstance().getLastModified(mcrId); + long modified = MCRXMLMetadataManager.obtainInstance().getLastModified(mcrId); if (modified < 0) { throw MCRErrorResponse.ofStatusCode(Response.Status.NOT_FOUND.getStatusCode()) .withErrorCode(MCRErrorCodeConstants.MCROBJECT_NOT_FOUND) @@ -189,13 +189,13 @@ public Response listDerivates() public Response getDerivate(@Parameter(example = "mir_derivate_00004711") @PathParam(PARAM_DERID) MCRObjectID derid) throws IOException { validateDerivateRelation(mcrId, derid); - long modified = MCRXMLMetadataManager.getInstance().getLastModified(derid); + long modified = MCRXMLMetadataManager.obtainInstance().getLastModified(derid); Date lastModified = new Date(modified); Optional cachedResponse = MCRRestUtils.getCachedResponse(request, lastModified); if (cachedResponse.isPresent()) { return cachedResponse.get(); } - MCRContent mcrContent = MCRXMLMetadataManager.getInstance().retrieveContent(derid); + MCRContent mcrContent = MCRXMLMetadataManager.obtainInstance().retrieveContent(derid); return Response.ok() .entity(mcrContent, new Annotation[] { MCRParams.Factory diff --git a/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestObjects.java b/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestObjects.java index 7d531e7d37..65a8fe7f82 100644 --- a/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestObjects.java +++ b/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestObjects.java @@ -414,7 +414,7 @@ public Response getObject(@Parameter(example = "mir_mods_00004711") @PathParam(P throws IOException { long modified; try { - modified = MCRXMLMetadataManager.getInstance().getLastModified(id); + modified = MCRXMLMetadataManager.obtainInstance().getLastModified(id); } catch (IOException io) { throw MCRErrorResponse.ofStatusCode(Response.Status.NOT_FOUND.getStatusCode()) .withCause(io) @@ -447,7 +447,7 @@ public Response getObject(@Parameter(example = "mir_mods_00004711") @PathParam(P tags = MCRRestUtils.TAG_MYCORE_OBJECT) public Response getObjectMetadata(@Parameter(example = "mir_mods_00004712") @PathParam(PARAM_MCRID) MCRObjectID id) throws IOException { - long modified = MCRXMLMetadataManager.getInstance().getLastModified(id); + long modified = MCRXMLMetadataManager.obtainInstance().getLastModified(id); if (modified < 0) { throw new NotFoundException("MCRObject " + id + " not found"); } @@ -566,7 +566,7 @@ private Response getThumbnail(String id, int size, String ext) { @MCRRestRequiredPermission(MCRAccessManager.PERMISSION_HISTORY_VIEW) public Response getObjectVersions(@Parameter(example = "mir_mods_00004713") @PathParam(PARAM_MCRID) MCRObjectID id) throws IOException { - long modified = MCRXMLMetadataManager.getInstance().getLastModified(id); + long modified = MCRXMLMetadataManager.obtainInstance().getLastModified(id); if (modified < 0) { throw MCRErrorResponse.ofStatusCode(Response.Status.NOT_FOUND.getStatusCode()) .withErrorCode(MCRErrorCodeConstants.MCROBJECT_NOT_FOUND) @@ -578,7 +578,7 @@ public Response getObjectVersions(@Parameter(example = "mir_mods_00004713") @Pat if (cachedResponse.isPresent()) { return cachedResponse.get(); } - List> versions = MCRXMLMetadataManager.getInstance().listRevisions(id); + List> versions = MCRXMLMetadataManager.obtainInstance().listRevisions(id); return Response.ok() .entity(new GenericEntity<>(versions, TypeUtils.parameterize(List.class, MCRAbstractMetadataVersion.class))) .lastModified(lastModified) @@ -598,7 +598,7 @@ public Response getObjectVersions(@Parameter(example = "mir_mods_00004713") @Pat public Response getObjectVersion(@Parameter(example = "mir_mods_00004714") @PathParam(PARAM_MCRID) MCRObjectID id, @PathParam("revision") String revision) throws IOException { - MCRContent mcrContent = MCRXMLMetadataManager.getInstance().retrieveContent(id, revision); + MCRContent mcrContent = MCRXMLMetadataManager.obtainInstance().retrieveContent(id, revision); if (mcrContent == null) { throw MCRErrorResponse.ofStatusCode(Response.Status.NOT_FOUND.getStatusCode()) .withErrorCode(MCRErrorCodeConstants.MCROBJECT_REVISION_NOT_FOUND) diff --git a/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestUtils.java b/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestUtils.java index c2acede886..08cb09d337 100644 --- a/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestUtils.java +++ b/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestUtils.java @@ -50,7 +50,7 @@ private MCRRestUtils() { public static Response getCachedResponseIgnoreExceptions(Request request, MCRObjectID id) { try { - long lastModified = MCRXMLMetadataManager.getInstance().getLastModified(id); + long lastModified = MCRXMLMetadataManager.obtainInstance().getLastModified(id); if (lastModified >= 0) { Date lmDate = new Date(lastModified); Optional cachedResponse = getCachedResponse(request, lmDate); diff --git a/mycore-solr/src/main/java/org/mycore/solr/index/MCRSolrIndexer.java b/mycore-solr/src/main/java/org/mycore/solr/index/MCRSolrIndexer.java index 0bead0f867..0db763bdec 100644 --- a/mycore-solr/src/main/java/org/mycore/solr/index/MCRSolrIndexer.java +++ b/mycore-solr/src/main/java/org/mycore/solr/index/MCRSolrIndexer.java @@ -331,7 +331,7 @@ protected static boolean useNestedDocuments() { * Rebuilds solr's metadata index. */ public static void rebuildMetadataIndex(List solrCores) { - rebuildMetadataIndex(MCRXMLMetadataManager.getInstance().listIDs(), solrCores); + rebuildMetadataIndex(MCRXMLMetadataManager.obtainInstance().listIDs(), solrCores); } /** @@ -341,7 +341,7 @@ public static void rebuildMetadataIndex(List solrCores) { * of the objects to index */ public static void rebuildMetadataIndex(String type, List solrCores) { - List identfiersOfType = MCRXMLMetadataManager.getInstance().listIDsOfType(type); + List identfiersOfType = MCRXMLMetadataManager.obtainInstance().listIDsOfType(type); rebuildMetadataIndex(identfiersOfType, solrCores); } @@ -352,7 +352,7 @@ public static void rebuildMetadataIndex(String type, List solrCores * of the objects to index */ public static void rebuildMetadataIndexForObjectBase(String base, List solrCores) { - List identfiersOfBase = MCRXMLMetadataManager.getInstance().listIDsForBase(base); + List identfiersOfBase = MCRXMLMetadataManager.obtainInstance().listIDsForBase(base); rebuildMetadataIndex(identfiersOfBase, solrCores); } @@ -416,7 +416,7 @@ public static void rebuildMetadataIndex(List list, List sol * Rebuilds solr's content index. */ public static void rebuildContentIndex(List cores) { - rebuildContentIndex(MCRXMLMetadataManager.getInstance().listIDsOfType(MCRDerivate.OBJECT_TYPE), cores); + rebuildContentIndex(MCRXMLMetadataManager.obtainInstance().listIDsOfType(MCRDerivate.OBJECT_TYPE), cores); } /** @@ -571,7 +571,7 @@ public static void optimize(List cores) { * database. All solr zombie documents will be removed, and all not indexed mycore objects will be indexed. */ public static void synchronizeMetadataIndex(List client) throws IOException, SolrServerException { - Collection objectTypes = MCRXMLMetadataManager.getInstance().getObjectTypes(); + Collection objectTypes = MCRXMLMetadataManager.obtainInstance().getObjectTypes(); for (String objectType : objectTypes) { synchronizeMetadataIndex(client, objectType); } @@ -584,14 +584,14 @@ public static void synchronizeMetadataIndex(List client) throws IOE */ public static void synchronizeMetadataIndex(List cores, String objectType) throws IOException, SolrServerException { - synchronizeMetadataIndex(cores, objectType, () -> MCRXMLMetadataManager.getInstance().listIDsOfType(objectType), + synchronizeMetadataIndex(cores, objectType, () -> MCRXMLMetadataManager.obtainInstance().listIDsOfType(objectType), "objectType:" + objectType); } public static void synchronizeMetadataIndexForObjectBase(List cores, String objectBase) throws IOException, SolrServerException { final String solrQuery = "objectType:" + objectBase.split("_")[1] + " _root_:" + objectBase + "_*"; - synchronizeMetadataIndex(cores, objectBase, () -> MCRXMLMetadataManager.getInstance() + synchronizeMetadataIndex(cores, objectBase, () -> MCRXMLMetadataManager.obtainInstance() .listIDsForBase(objectBase), solrQuery); } diff --git a/mycore-solr/src/main/java/org/mycore/solr/index/document/MCRSolrInputDocumentFactory.java b/mycore-solr/src/main/java/org/mycore/solr/index/document/MCRSolrInputDocumentFactory.java index 5e97cf0f99..ccf0286f4d 100644 --- a/mycore-solr/src/main/java/org/mycore/solr/index/document/MCRSolrInputDocumentFactory.java +++ b/mycore-solr/src/main/java/org/mycore/solr/index/document/MCRSolrInputDocumentFactory.java @@ -52,7 +52,7 @@ public abstract Iterator getDocuments(Map contentMap = new HashMap<>(); for (MCRObjectID id : ids) { - MCRContent content = MCRXMLMetadataManager.getInstance().retrieveContent(id); + MCRContent content = MCRXMLMetadataManager.obtainInstance().retrieveContent(id); contentMap.put(id, content); } return getIndexHandler(contentMap); From da2b0200b8cc660fffab534ce65444a78713fa6f Mon Sep 17 00:00:00 2001 From: Torsten Krause Date: Thu, 29 Jan 2026 13:50:05 +0100 Subject: [PATCH 3/4] MCR-3600 allow legacy features annotated with @Deprecated --- rules.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rules.xml b/rules.xml index 845ed3d812..52c5ff20de 100644 --- a/rules.xml +++ b/rules.xml @@ -243,6 +243,8 @@ //MethodDeclaration [ @Name='getInstance' + and + not(ModifierList/Annotation/ClassType[pmd-java:typeIs('java.lang.Deprecated')]) ] [ not(pmd-java:modifiers()='static') @@ -263,6 +265,8 @@ //MethodDeclaration [ @Name='getInstance' + and + not(ModifierList/Annotation/ClassType[pmd-java:typeIs('java.lang.Deprecated')]) ] [ not(ClassType[pmd-java:typeIs(../../../@CanonicalName)]) @@ -283,6 +287,8 @@ //MethodDeclaration [ @Name='getInstance' + and + not(ModifierList/Annotation/ClassType[pmd-java:typeIs('java.lang.Deprecated')]) ] [ not(../..[pmd-java:modifiers()='final']) @@ -303,6 +309,8 @@ //MethodDeclaration [ @Name='getInstance' + and + not(ModifierList/Annotation/ClassType[pmd-java:typeIs('java.lang.Deprecated')]) ] [ not(../ConstructorDeclaration[pmd-java:modifiers()='private']) @@ -323,6 +331,8 @@ //MethodDeclaration [ @Name='getInstance' + and + not(ModifierList/Annotation/ClassType[pmd-java:typeIs('java.lang.Deprecated')]) ] [ ../ConstructorDeclaration[not(deep-equal(pmd-java:modifiers(),('private')))] From 269cd9ab67791cb648bb8e60596159cb86ca9d2d Mon Sep 17 00:00:00 2001 From: Torsten Krause Date: Thu, 29 Jan 2026 14:17:30 +0100 Subject: [PATCH 4/4] MCR-3600 fix checkstyle issues --- .../src/main/java/org/mycore/mods/MCRMODSEmbargoUtils.java | 4 ++-- .../mycore/restapi/v1/utils/MCRRestAPIObjectsHelper.java | 4 ++-- .../src/main/java/org/mycore/restapi/v2/MCRRestObjects.java | 6 +++--- .../src/main/java/org/mycore/solr/index/MCRSolrIndexer.java | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mycore-mods/src/main/java/org/mycore/mods/MCRMODSEmbargoUtils.java b/mycore-mods/src/main/java/org/mycore/mods/MCRMODSEmbargoUtils.java index df35064343..b2c048ce94 100644 --- a/mycore-mods/src/main/java/org/mycore/mods/MCRMODSEmbargoUtils.java +++ b/mycore-mods/src/main/java/org/mycore/mods/MCRMODSEmbargoUtils.java @@ -67,8 +67,8 @@ protected void handleObjectUpdated(MCREvent evt, MCRObject obj) { } public static String getCachedEmbargo(final MCRObjectID objectId) { - MCRCache.ModifiedHandle modifiedHandle = MCRXMLMetadataManager.obtainInstance().getLastModifiedHandle(objectId, 10, - TimeUnit.MINUTES); + MCRCache.ModifiedHandle modifiedHandle = MCRXMLMetadataManager.obtainInstance() + .getLastModifiedHandle(objectId, 10, TimeUnit.MINUTES); String embargo = null; try { embargo = embargoCache.getIfUpToDate(objectId, modifiedHandle); diff --git a/mycore-restapi/src/main/java/org/mycore/restapi/v1/utils/MCRRestAPIObjectsHelper.java b/mycore-restapi/src/main/java/org/mycore/restapi/v1/utils/MCRRestAPIObjectsHelper.java index 5f9f61465c..72488c1020 100644 --- a/mycore-restapi/src/main/java/org/mycore/restapi/v1/utils/MCRRestAPIObjectsHelper.java +++ b/mycore-restapi/src/main/java/org/mycore/restapi/v1/utils/MCRRestAPIObjectsHelper.java @@ -378,8 +378,8 @@ private static Set retrieveMCRIDs(FilterParams params) { Set mcrIDs = new HashSet<>(); if (params.projectIDs.isEmpty()) { if (params.typeIDs.isEmpty()) { - mcrIDs = MCRXMLMetadataManager.obtainInstance().listIDs().stream().filter(id -> !id.contains("_derivate_")) - .collect(Collectors.toSet()); + mcrIDs = MCRXMLMetadataManager.obtainInstance().listIDs().stream() + .filter(id -> !id.contains("_derivate_")).collect(Collectors.toSet()); } else { for (String type : params.typeIDs) { mcrIDs.addAll(MCRXMLMetadataManager.obtainInstance().listIDsOfType(type)); diff --git a/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestObjects.java b/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestObjects.java index 65a8fe7f82..c3bbff45b4 100644 --- a/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestObjects.java +++ b/mycore-restapi/src/main/java/org/mycore/restapi/v2/MCRRestObjects.java @@ -578,11 +578,11 @@ public Response getObjectVersions(@Parameter(example = "mir_mods_00004713") @Pat if (cachedResponse.isPresent()) { return cachedResponse.get(); } - List> versions = MCRXMLMetadataManager.obtainInstance().listRevisions(id); + List> versions = + MCRXMLMetadataManager.obtainInstance().listRevisions(id); return Response.ok() .entity(new GenericEntity<>(versions, TypeUtils.parameterize(List.class, MCRAbstractMetadataVersion.class))) - .lastModified(lastModified) - .build(); + .lastModified(lastModified).build(); } @GET diff --git a/mycore-solr/src/main/java/org/mycore/solr/index/MCRSolrIndexer.java b/mycore-solr/src/main/java/org/mycore/solr/index/MCRSolrIndexer.java index 0db763bdec..0bfc88738d 100644 --- a/mycore-solr/src/main/java/org/mycore/solr/index/MCRSolrIndexer.java +++ b/mycore-solr/src/main/java/org/mycore/solr/index/MCRSolrIndexer.java @@ -584,8 +584,8 @@ public static void synchronizeMetadataIndex(List client) throws IOE */ public static void synchronizeMetadataIndex(List cores, String objectType) throws IOException, SolrServerException { - synchronizeMetadataIndex(cores, objectType, () -> MCRXMLMetadataManager.obtainInstance().listIDsOfType(objectType), - "objectType:" + objectType); + synchronizeMetadataIndex(cores, objectType, + () -> MCRXMLMetadataManager.obtainInstance().listIDsOfType(objectType), "objectType:" + objectType); } public static void synchronizeMetadataIndexForObjectBase(List cores, String objectBase)