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 */ 006package org.fcrepo.kernel.impl.services; 007 008import org.fcrepo.kernel.api.Transaction; 009import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 010import org.fcrepo.kernel.api.identifiers.FedoraId; 011import org.fcrepo.kernel.api.models.ExternalContent; 012import org.fcrepo.kernel.api.operations.NonRdfSourceOperationBuilder; 013import org.fcrepo.kernel.api.operations.NonRdfSourceOperationFactory; 014import org.fcrepo.kernel.api.services.ReplaceBinariesService; 015import org.fcrepo.persistence.api.PersistentStorageSession; 016import org.fcrepo.persistence.api.PersistentStorageSessionManager; 017import org.fcrepo.persistence.api.exceptions.PersistentStorageException; 018import org.fcrepo.persistence.common.MultiDigestInputStreamWrapper; 019import org.slf4j.Logger; 020import org.springframework.stereotype.Component; 021 022import javax.inject.Inject; 023 024import java.io.InputStream; 025import java.net.URI; 026import java.util.Collection; 027import java.util.Collections; 028 029import static java.lang.String.format; 030import static org.slf4j.LoggerFactory.getLogger; 031 032/** 033 * Implementation of a service for replacing/updating binary resources 034 * 035 * @author bbpennel 036 */ 037@Component 038public class ReplaceBinariesServiceImpl extends AbstractService implements ReplaceBinariesService { 039 040 private static final Logger LOGGER = getLogger(ReplaceBinariesServiceImpl.class); 041 042 @Inject 043 private PersistentStorageSessionManager psManager; 044 045 @Inject 046 private NonRdfSourceOperationFactory factory; 047 048 @Override 049 public void perform(final Transaction tx, 050 final String userPrincipal, 051 final FedoraId fedoraId, 052 final String filename, 053 final String contentType, 054 final Collection<URI> digests, 055 final InputStream contentBody, 056 final long contentSize, 057 final ExternalContent externalContent) { 058 try { 059 final PersistentStorageSession pSession = this.psManager.getSession(tx); 060 061 String mimeType = contentType; 062 long size = contentSize; 063 final NonRdfSourceOperationBuilder builder; 064 if (externalContent == null || externalContent.isCopy()) { 065 var contentInputStream = contentBody; 066 if (externalContent != null) { 067 LOGGER.debug("External content COPY '{}', '{}'", fedoraId, externalContent.getURL()); 068 contentInputStream = externalContent.fetchExternalContent(); 069 } 070 071 builder = factory.updateInternalBinaryBuilder(tx, fedoraId, contentInputStream); 072 } else { 073 builder = factory.updateExternalBinaryBuilder(tx, fedoraId, 074 externalContent.getHandling(), 075 externalContent.getURI()); 076 077 if (contentSize == -1L) { 078 size = externalContent.getContentSize(); 079 } 080 if (!digests.isEmpty()) { 081 final var multiDigestWrapper = new MultiDigestInputStreamWrapper( 082 externalContent.fetchExternalContent(), 083 digests, 084 Collections.emptyList()); 085 multiDigestWrapper.checkFixity(); 086 } 087 } 088 089 if (externalContent != null && externalContent.getContentType() != null) { 090 mimeType = externalContent.getContentType(); 091 } 092 093 builder.mimeType(mimeType) 094 .contentSize(size) 095 .filename(filename) 096 .contentDigests(digests) 097 .userPrincipal(userPrincipal); 098 final var replaceOp = builder.build(); 099 100 lockArchivalGroupResource(tx, pSession, fedoraId); 101 tx.lockResource(fedoraId); 102 // Descriptions are always under the binary, so just lock it. 103 tx.lockResource(fedoraId.asDescription()); 104 105 pSession.persist(replaceOp); 106 this.searchIndex.addUpdateIndex(tx, pSession.getHeaders(fedoraId, null)); 107 recordEvent(tx, fedoraId, replaceOp); 108 } catch (final PersistentStorageException ex) { 109 throw new RepositoryRuntimeException(format("failed to replace binary %s", 110 fedoraId), ex); 111 } 112 } 113 114}