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        final var txId = tx.getId();
070        try {
071            final PersistentStorageSession pSession = this.psManager.getSession(txId);
072
073            String mimeType = contentType;
074            long size = contentSize;
075            final NonRdfSourceOperationBuilder builder;
076            if (externalContent == null || externalContent.isCopy()) {
077                var contentInputStream = contentBody;
078                if (externalContent != null) {
079                    LOGGER.debug("External content COPY '{}', '{}'", fedoraId, externalContent.getURL());
080                    contentInputStream = externalContent.fetchExternalContent();
081                }
082
083                builder = factory.updateInternalBinaryBuilder(fedoraId, contentInputStream);
084            } else {
085                builder = factory.updateExternalBinaryBuilder(fedoraId,
086                        externalContent.getHandling(),
087                        externalContent.getURI());
088
089                if (contentSize == -1L) {
090                    size = externalContent.getContentSize();
091                }
092                if (!digests.isEmpty()) {
093                    final var multiDigestWrapper = new MultiDigestInputStreamWrapper(
094                            externalContent.fetchExternalContent(),
095                            digests,
096                            Collections.emptyList());
097                    multiDigestWrapper.checkFixity();
098                }
099            }
100
101            if (externalContent != null && externalContent.getContentType() != null) {
102                mimeType = externalContent.getContentType();
103            }
104
105            builder.mimeType(mimeType)
106                   .contentSize(size)
107                   .filename(filename)
108                   .contentDigests(digests)
109                   .userPrincipal(userPrincipal);
110            final var replaceOp = builder.build();
111
112            lockArchivalGroupResource(tx, pSession, fedoraId);
113            tx.lockResource(fedoraId);
114            tx.lockResource(fedoraId.asDescription());
115
116            pSession.persist(replaceOp);
117            recordEvent(txId, fedoraId, replaceOp);
118        } catch (final PersistentStorageException ex) {
119            throw new RepositoryRuntimeException(format("failed to replace binary %s",
120                  fedoraId), ex);
121        }
122    }
123
124}