001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.kernel.impl.services;
019
020import org.fcrepo.kernel.api.Transaction;
021import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
022import org.fcrepo.kernel.api.identifiers.FedoraId;
023import org.fcrepo.kernel.api.models.ExternalContent;
024import org.fcrepo.kernel.api.operations.NonRdfSourceOperationBuilder;
025import org.fcrepo.kernel.api.operations.NonRdfSourceOperationFactory;
026import org.fcrepo.kernel.api.services.ReplaceBinariesService;
027import org.fcrepo.persistence.api.PersistentStorageSession;
028import org.fcrepo.persistence.api.PersistentStorageSessionManager;
029import org.fcrepo.persistence.api.exceptions.PersistentStorageException;
030import org.fcrepo.persistence.common.MultiDigestInputStreamWrapper;
031import org.slf4j.Logger;
032import org.springframework.stereotype.Component;
033
034import javax.inject.Inject;
035import java.io.InputStream;
036import java.net.URI;
037import java.util.Collection;
038import java.util.Collections;
039
040import static java.lang.String.format;
041import static org.slf4j.LoggerFactory.getLogger;
042
043/**
044 * Implementation of a service for replacing/updating binary resources
045 *
046 * @author bbpennel
047 */
048@Component
049public class ReplaceBinariesServiceImpl extends AbstractService implements ReplaceBinariesService {
050
051    private static final Logger LOGGER = getLogger(ReplaceBinariesServiceImpl.class);
052
053    @Inject
054    private PersistentStorageSessionManager psManager;
055
056    @Inject
057    private NonRdfSourceOperationFactory factory;
058
059    @Override
060    public void perform(final Transaction tx,
061                        final String userPrincipal,
062                        final FedoraId fedoraId,
063                        final String filename,
064                        final String contentType,
065                        final Collection<URI> digests,
066                        final InputStream contentBody,
067                        final long contentSize,
068                        final ExternalContent externalContent) {
069        try {
070            final PersistentStorageSession pSession = this.psManager.getSession(tx);
071
072            String mimeType = contentType;
073            long size = contentSize;
074            final NonRdfSourceOperationBuilder builder;
075            if (externalContent == null || externalContent.isCopy()) {
076                var contentInputStream = contentBody;
077                if (externalContent != null) {
078                    LOGGER.debug("External content COPY '{}', '{}'", fedoraId, externalContent.getURL());
079                    contentInputStream = externalContent.fetchExternalContent();
080                }
081
082                builder = factory.updateInternalBinaryBuilder(tx, fedoraId, contentInputStream);
083            } else {
084                builder = factory.updateExternalBinaryBuilder(tx, fedoraId,
085                        externalContent.getHandling(),
086                        externalContent.getURI());
087
088                if (contentSize == -1L) {
089                    size = externalContent.getContentSize();
090                }
091                if (!digests.isEmpty()) {
092                    final var multiDigestWrapper = new MultiDigestInputStreamWrapper(
093                            externalContent.fetchExternalContent(),
094                            digests,
095                            Collections.emptyList());
096                    multiDigestWrapper.checkFixity();
097                }
098            }
099
100            if (externalContent != null && externalContent.getContentType() != null) {
101                mimeType = externalContent.getContentType();
102            }
103
104            builder.mimeType(mimeType)
105                   .contentSize(size)
106                   .filename(filename)
107                   .contentDigests(digests)
108                   .userPrincipal(userPrincipal);
109            final var replaceOp = builder.build();
110
111            lockArchivalGroupResource(tx, pSession, fedoraId);
112            tx.lockResource(fedoraId);
113            tx.lockResource(fedoraId.asDescription());
114
115            pSession.persist(replaceOp);
116            this.searchIndex.addUpdateIndex(tx, pSession.getHeaders(fedoraId, null));
117            recordEvent(tx, fedoraId, replaceOp);
118        } catch (final PersistentStorageException ex) {
119            throw new RepositoryRuntimeException(format("failed to replace binary %s",
120                    fedoraId), ex);
121        }
122    }
123
124}