...
 
Commits (2)
......@@ -680,6 +680,12 @@
<xsl:with-param name="staticURL" select="$staticURL" />
<xsl:with-param name="bold" select="true()" />
</xsl:call-template>
<!-- Versioning -->
<xsl:call-template name="printValueLineVersion">
<xsl:with-param name="nodes" select="." />
<xsl:with-param name="bold" select="true()" />
</xsl:call-template>
<!-- License -->
......
......@@ -26,6 +26,7 @@ import java.util.Locale;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mycore.common.MCRException;
import org.mycore.common.MCRUsageException;
import org.mycore.common.content.MCRJDOMContent;
import org.mycore.common.events.MCREvent;
import org.mycore.common.events.MCREventHandlerBase;
......@@ -129,16 +130,25 @@ public class MCROCFLMetadataEventHandler extends MCREventHandlerBase {
String objName = obj.getId().toString();
if (!repo.containsObject(objName)) {
throw new MCRException("Refusing to delete not existing object!");
}
try {
repo.updateObject(ObjectVersionId.head(objName), new CommitInfo().setMessage(
"Delete on " + MCRISO8601Date.now().format("yyyy-MM-dd HH:mm:ss", Locale.GERMAN)), init -> {
init.removeFile(objName + ".xml");
});
} catch (Exception e) {
throw new MCRException("Error while creating object in OCFL store!", e);
throw new MCRUsageException("Refusing to delete non-existent object: " + objName);
} else {
try {
if (MCROCFLRepositoryHandler.instance().getAbsoluteDelete()) {
repo.purgeObject(objName);
} else {
if (!repo.describeObject(objName).getHeadVersion().containsFile(objName + ".xml")) {
throw new MCRUsageException("Object does not exist (anymore?): " + objName);
} else {
repo.updateObject(ObjectVersionId.head(objName), new CommitInfo().setMessage(
"Delete on " + MCRISO8601Date.now().format("yyyy-MM-dd HH:mm:ss", Locale.GERMAN)),
init -> {
init.removeFile(objName + ".xml");
});
}
}
} catch (Exception e) {
throw new MCRException("Error while creating object in OCFL store!", e);
}
}
}
......
......@@ -20,7 +20,9 @@ public class MCROCFLRepositoryHandler {
private static MCROCFLRepositoryHandler handler = new MCROCFLRepositoryHandler();
// TODO: discuss
protected final Boolean opinionated = CONFIG.getBoolean("MCR.Metadata.Store.OCFL.Opinionated", false);
protected final Boolean absoluteDelete = CONFIG.getBoolean("MCR.Metadata.Store.OCFLDeletionIsAbsolute", false);
protected final MCROCFLAbstractRepo repoClass = CONFIG.<MCROCFLAbstractRepo>getInstanceOf(
"MCR.Metadata.Store.OCFL.RepoClass", MCROCFLDefaultRepo.class.getName());
protected final Path dirRepo, dirWork;
......@@ -90,5 +92,9 @@ public class MCROCFLRepositoryHandler {
public Path getWorkPath() {
return this.dirWork;
}
public Boolean getAbsoluteDelete() {
return this.absoluteDelete;
}
}
/*
* This file is part of *** M y C o R e ***
* See http://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 <http://www.gnu.org/licenses/>.
*/
package org.mycore.datamodel.ifs2;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Date;
import java.util.stream.Stream;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom2.JDOMException;
import org.mycore.common.MCRUsageException;
import org.mycore.common.content.MCRByteContent;
import org.mycore.common.content.MCRContent;
import edu.wisc.library.ocfl.api.OcflRepository;
import edu.wisc.library.ocfl.api.model.VersionDetails;
import edu.wisc.library.ocfl.api.model.VersionId;
/**
* Provides information about a stored version of metadata and allows to
* retrieve that version from SVN
*
* @author Christoph Neidahl
*/
@XmlRootElement(name = "revision")
@XmlAccessorType(XmlAccessType.FIELD)
public class MCROCFLMetadataVersion extends MCRMetadataVersion {
/**
* The logger
*/
protected static final Logger LOGGER = LogManager.getLogger();
/**
* The metadata document this version belongs to
*/
@XmlTransient
private MCROCFLVersionedMetadata vm;
/**
* The revision number of this version
*/
@XmlAttribute(name = "r")
private long revision;
/**
* The user that created this version
*/
@XmlAttribute
private String user;
/**
* The date this version was created
*/
@XmlAttribute
private Date date;
/**
* Was this version result of a create, update or delete?
*/
@XmlAttribute()
private Type type;
/**
* A version that was created in store
*/
public static final char CREATED = 'A';
/**
* A version that was updated in store
*/
public static final char UPDATED = 'M';
/**
* A version that was deleted in store
*/
public static final char DELETED = 'D';
/**
* Creates a new metadata version info object
*
* @param vm the metadata document this version belongs to
* @param version the version entry from OCFL identifying this version
* @param type the type of commit
*/
public MCROCFLMetadataVersion(MCROCFLVersionedMetadata vm, VersionDetails version, char type) {
this (vm, version, vm.getRevision(), type);
}
/**
* Creates a new metadata version info object
*
* @param vm the metadata document this version belongs to
* @param logEntry the log entry from SVN holding data on this version
* @param type the type of commit
*/
public MCROCFLMetadataVersion(MCROCFLVersionedMetadata vm, VersionDetails version, long revision, char type) {
this.vm = vm;
this.revision = revision;
user = "system";
date = Date.from(version.getCreated().toInstant());
this.type = Type.fromValue(type);
}
/**
* Returns the metadata object this version belongs to
*
* @return the metadata object this version belongs to
*/
@Override
public MCRVersionedMetadata getMetadataObject() {
return vm;
}
/**
* Returns the type of operation this version comes from
*
* @see #CREATED
* @see #UPDATED
* @see #DELETED
*/
public char getType() {
return type.charValue;
}
/**
* Returns the SVN revision number of this version
*
* @return the SVN revision number of this version
*/
public long getRevision() {
return revision;
}
/**
* Returns the user that created this version
*
* @return the user that created this version
*/
public String getUser() {
return user;
}
/**
* Returns the date and time this version was created
*
* @return the date and time this version was created
*/
public Date getDate() {
return date;
}
/**
* Retrieves this version of the metadata
*
* @return the metadata document as it was in this version
* @throws MCRUsageException if this is a deleted version, which can not be
* retrieved
*/
public MCRContent retrieve() throws IOException {
if (type == Type.deleted) {
String msg = "You can not retrieve a deleted version, retrieve a previous version instead";
throw new MCRUsageException(msg);
}
try {
OcflRepository repository = vm.getStoreOCFL().getOCFLRepository();
String objName = vm.getStoreOCFL().getID() + "_" + vm.getStoreOCFL().idLeadingZeroes(vm.getID());
LOGGER.warn("Requested retrieval of {}", objName);
File versionFile = Paths
.get(vm.getStoreOCFL().getOCFLRepositoryPath().toString(), repository.describeObject(objName)
.getVersion(new VersionId(revision)).getFile(objName + ".xml").getStorageRelativePath())
.toFile();
LOGGER.warn(versionFile.getAbsolutePath());
byte[] versionBytes = new byte[(int) versionFile.length()];
LOGGER.warn("Size of metadata: {}", versionFile.length());
FileInputStream versionContent = new FileInputStream(versionFile);
versionContent.read(versionBytes);
versionContent.close();
return new MCRByteContent(versionBytes, 0, versionBytes.length, getDate().getTime());
} catch (Exception e) {
throw new IOException(e);
}
}
/**
* Replaces the current version of the metadata object with this version, which
* means that a new version is created that is identical to this old version.
* The stored metadata document is updated to this old version of the metadata.
*/
public void restore() throws IOException, JDOMException {
vm.update(retrieve());
}
}
/*
* This file is part of *** M y C o R e ***
* See http://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 <http://www.gnu.org/licenses/>.
*/
package org.mycore.datamodel.ifs2;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.NumberFormat;
import java.util.Iterator;
import java.util.Locale;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom2.JDOMException;
import org.mycore.common.MCRException;
import org.mycore.common.MCRPersistenceException;
import org.mycore.common.MCRUsageException;
import org.mycore.common.content.MCRContent;
import org.mycore.datamodel.common.MCRISO8601Date;
import org.mycore.datamodel.common.MCROCFLRepositoryHandler;
import org.tmatesoft.svn.core.io.SVNRepository;
import edu.wisc.library.ocfl.api.OcflRepository;
import edu.wisc.library.ocfl.api.model.CommitInfo;
import edu.wisc.library.ocfl.api.model.ObjectVersionId;
/**
* Stores metadata objects both in a local filesystem structure and in a
* Subversion repository. Changes can be tracked and restored. To enable
* versioning, configure the repository URL, for example
*
* MCR.IFS2.Store.DocPortal_document.SVNRepositoryURL=file:///foo/svnroot/
*
* @author Frank Lützenkirchen
*/
public class MCROCFLVersioningMetadataStore extends MCRVersioningMetadataStore {
protected static final Logger LOGGER = LogManager.getLogger();
protected OcflRepository repo;
@Override
protected void init(String type) {
super.init(type);
checkRepo();
}
@Override
protected void init(MCRStoreConfig config) {
super.init(config);
checkRepo();
}
protected void setupSVN(String type) {
LOGGER.warn ("Unsupported: void setupSVN(String type)");
}
protected void checkRepo() {
LOGGER.info("Testing if OCFL repository is configured correctly.");
repo = MCROCFLRepositoryHandler.instance().getRepository();
}
/**
* The SVN store was configurable in whether or not it should touch the
* modification date on commits. The OCFL repository handler is only capable of
* writing the metadata from the object straight to the target.
*
* This method is only kept for compatibility purposes.
*
* @return true, if last modified of file should be same as timestamp of OCFL
* commit (always)
*/
public static boolean shouldSyncLastModifiedOnSVNCommit() {
return true;
}
@Override
public SVNRepository getRepository() {
throw new MCRException ("Unsupported: SVNRepository getRepository()");
}
/**
* Returns the OCFL repository used to manage metadata versions in this store.
*
* @return the OCFL repository used to manage metadata versions in this store.
*/
public OcflRepository getOCFLRepository() {
return MCROCFLRepositoryHandler.instance().getRepository();
}
/**
* Returns the path of the OCFL repository used to manage metadata versions in
* this store.
*
* @return the path of the OCFL repository used to manage metadata versions in
* this store.
*/
public Path getOCFLRepositoryPath() {
return MCROCFLRepositoryHandler.instance().getRepositoryPath();
}
/**
* Used to check the local SVN repository for errors
*
* Kept for compatibility reasons with existing code, OCFL repository does this
* on its own.
*
* @throws MCRPersistenceException if 'svn verify' fails (doesn't apply anymore)
*/
public void verify() throws MCRPersistenceException {
LOGGER.warn("Unsupported: void verify()");
}
@Override
public MCROCFLVersionedMetadata create(MCRContent xml, int id) throws IOException, JDOMException {
if (id <= 0) {
throw new MCRException("ID of metadata object must be a positive integer!");
}
String objName = getID() + "_" + idLeadingZeroes(id);
if (getOCFLRepository().containsObject(objName)) {
throw new MCRException("Metadata object " + objName + " already exists in store!");
}
MCROCFLVersionedMetadata meta = buildMetadataObject(id);
meta.create(xml);
return meta;
}
@Override
public MCROCFLVersionedMetadata create(MCRContent xml) throws IOException, JDOMException {
return (MCROCFLVersionedMetadata) super.create(xml);
}
/**
* Returns the metadata stored under the given ID, or null. Note that this
* metadata may not exist currently in the store, it may be a deleted version,
* which can be restored then.
*
* @param id the ID of the XML document
* @return the metadata stored under that ID, or null when there is no such
* metadata object
*/
@Override
public MCRVersionedMetadata retrieve(int id) throws IOException {
LOGGER.debug("Retrieving {}_{} ({}).", getID(), idLeadingZeroes(id), id);
MCROCFLVersionedMetadata requestedMetadata = new MCROCFLVersionedMetadata(this, getSlot(id), id, super.forceDocType,
false);
LOGGER.debug("Retrieved metadata object: {}", requestedMetadata);
LOGGER.debug("Contained metadata: {}", requestedMetadata.getMetadata().asString());
return requestedMetadata;
}
@Override
public void delete(int id) throws IOException {
delete(getID() + "_" + idLeadingZeroes(id));
}
public void delete(String objName) {
if (!repo.containsObject(objName)) {
throw new MCRUsageException("Refusing to delete non-existent object: " + objName);
} else if (MCROCFLRepositoryHandler.instance().getAbsoluteDelete()) {
repo.purgeObject(objName);
} else {
if (!repo.describeObject(objName).getHeadVersion().containsFile(objName + ".xml")) {
throw new MCRUsageException("Object does not exist (anymore?): " + objName);
} else {
repo.updateObject(ObjectVersionId.head(objName),
new CommitInfo().setMessage(
"Delete on " + MCRISO8601Date.now().format("yyyy-MM-dd HH:mm:ss", Locale.GERMAN)),
init -> {
init.removeFile(objName + ".xml");
});
}
}
}
/**
* Returns true if data for the given ID is existing in the store.
*
* @param id the ID of the data
* @return true, if data for the given ID is existing in the store.
*/
@Override
public boolean exists(final int id) throws IOException {
return repo.containsObject(getID() + "_" + idLeadingZeroes(id));
}
protected MCROCFLVersionedMetadata buildMetadataObject(int id) {
return new MCROCFLVersionedMetadata(this, null, id, super.forceDocType, false);
}
@Override
protected MCROCFLVersionedMetadata buildMetadataObject(Path fo, int id) {
return buildMetadataObject(id);
}
public String idLeadingZeroes(int id) {
NumberFormat formatter = NumberFormat.getIntegerInstance(Locale.ROOT);
formatter.setMinimumIntegerDigits(getIDLength());
formatter.setGroupingUsed(false);
return formatter.format(id);
}
}