001/*
002 * The contents of this file are subject to the license and copyright
003 * detailed in the LICENSE and NOTICE files at the root of the source
004 * tree.
005 */
006
007package org.fcrepo.persistence.ocfl.impl;
008
009import org.fcrepo.kernel.api.operations.CreateVersionResourceOperation;
010import org.fcrepo.kernel.api.operations.ResourceOperation;
011import org.fcrepo.kernel.api.operations.ResourceOperationType;
012import org.fcrepo.persistence.api.exceptions.PersistentItemConflictException;
013import org.fcrepo.persistence.api.exceptions.PersistentStorageException;
014import org.fcrepo.persistence.common.ResourceHeaderUtils;
015import org.fcrepo.persistence.ocfl.api.FedoraToOcflObjectIndex;
016import org.fcrepo.storage.ocfl.CommitType;
017import org.slf4j.Logger;
018import org.slf4j.LoggerFactory;
019
020/**
021 * Persister for creating a new OCFL version of a resource. The new version is not created until the session
022 * is committed.
023 *
024 * @author pwinckles
025 */
026public class CreateVersionPersister extends AbstractPersister {
027
028    private static final Logger LOG = LoggerFactory.getLogger(CreateVersionPersister.class);
029
030    protected CreateVersionPersister(final FedoraToOcflObjectIndex index) {
031        super(CreateVersionResourceOperation.class, ResourceOperationType.UPDATE, index);
032    }
033
034    @Override
035    public void persist(final OcflPersistentStorageSession session, final ResourceOperation operation)
036            throws PersistentStorageException {
037
038        final var resourceId = operation.getResourceId();
039        LOG.debug("creating new version of <{}> in session <{}>", resourceId, session);
040
041        final var archivalGroupId = findArchivalGroupInAncestry(resourceId, session);
042
043        if (archivalGroupId.isPresent() && !archivalGroupId.get().equals(resourceId)) {
044            throw new PersistentItemConflictException(
045                    String.format("Resource <%s> is contained in Archival Group <%s> and cannot be versioned directly."
046                            + " Version the Archival Group instead.", resourceId, archivalGroupId));
047        }
048
049        final var ocflMapping = getMapping(operation.getTransaction(), resourceId);
050        final var ocflObjectSession = session.findOrCreateSession(ocflMapping.getOcflObjectId());
051
052        // Touching the last modified date is necessary so that resource that do not have any outstanding changes are
053        // still versioned
054        final var headers = new ResourceHeadersAdapter(ocflObjectSession.readHeaders(resourceId.getResourceId()))
055                .asKernelHeaders();
056        ResourceHeaderUtils.touchMementoCreateHeaders(headers);
057
058        ocflObjectSession.writeHeaders(new ResourceHeadersAdapter(headers).asStorageHeaders());
059        // The version is not actually created until the session is committed
060        ocflObjectSession.commitType(CommitType.NEW_VERSION);
061    }
062
063}