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 static org.apache.jena.rdf.model.ModelFactory.createDefaultModel; 009import static org.apache.jena.rdf.model.ResourceFactory.createTypedLiteral; 010import static org.apache.jena.vocabulary.RDF.type; 011import static org.fcrepo.kernel.api.RdfLexicon.HAS_FIXITY_RESULT; 012import static org.fcrepo.kernel.api.RdfLexicon.HAS_FIXITY_STATE; 013import static org.fcrepo.kernel.api.RdfLexicon.HAS_MESSAGE_DIGEST; 014import static org.fcrepo.kernel.api.RdfLexicon.HAS_SIZE; 015import static org.fcrepo.kernel.api.RdfLexicon.PREMIS_EVENT_OUTCOME_DETAIL; 016import static org.fcrepo.kernel.api.RdfLexicon.PREMIS_FIXITY; 017 018import org.apache.jena.rdf.model.Literal; 019import org.apache.jena.rdf.model.Model; 020import org.apache.jena.rdf.model.Resource; 021import org.fcrepo.kernel.api.RdfStream; 022import org.fcrepo.kernel.api.exception.InvalidChecksumException; 023import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 024import org.fcrepo.kernel.api.exception.UnsupportedAlgorithmException; 025import org.fcrepo.kernel.api.models.Binary; 026import org.fcrepo.kernel.api.rdf.DefaultRdfStream; 027import org.fcrepo.kernel.api.services.FixityService; 028import org.fcrepo.config.DigestAlgorithm; 029import org.fcrepo.persistence.common.MultiDigestInputStreamWrapper; 030import org.springframework.stereotype.Component; 031 032import java.io.IOException; 033import java.net.URI; 034import java.util.ArrayList; 035import java.util.Collection; 036import java.util.List; 037import java.util.UUID; 038import java.util.stream.Collectors; 039 040/** 041 * Implementation of {@link org.fcrepo.kernel.api.services.FixityService} 042 * 043 * @author dbernstein 044 * @author whikloj 045 */ 046@Component 047public class FixityServiceImpl extends AbstractService implements FixityService { 048 049 private static final Literal successResource = createTypedLiteral("SUCCESS"); 050 private static final Literal badChecksumResource = createTypedLiteral("BAD_CHECKSUM"); 051 052 @Override 053 public Collection<URI> getFixity(final Binary binary, final Collection<String> algorithms) 054 throws UnsupportedAlgorithmException { 055 final var digestAlgs = algorithms.stream() 056 .map(DigestAlgorithm::fromAlgorithm) 057 .collect(Collectors.toList()); 058 059 try (final var content = binary.getContent()) { 060 final MultiDigestInputStreamWrapper digestWrapper = new MultiDigestInputStreamWrapper(content, null, 061 digestAlgs); 062 return digestWrapper.getDigests(); 063 } catch (final IOException e) { 064 // input stream closed prematurely. 065 throw new RepositoryRuntimeException("Problem reading content stream from " + binary.getId(), e); 066 } 067 } 068 069 @Override 070 public RdfStream checkFixity(final Binary binary) 071 throws InvalidChecksumException { 072 final Model model = createDefaultModel(); 073 final Resource subject = model.createResource(binary.getId()); 074 final Resource fixityResult = model.createResource( 075 binary.getFedoraId().resolve("#" + UUID.randomUUID().toString()).getFullId()); 076 model.add(subject, HAS_FIXITY_RESULT, fixityResult); 077 model.add(fixityResult, type, PREMIS_FIXITY); 078 model.add(fixityResult, type, PREMIS_EVENT_OUTCOME_DETAIL); 079 model.add(fixityResult, HAS_SIZE, createTypedLiteral(binary.getContentSize())); 080 081 // Built for more than one digest in anticipation of FCREPO-3419 082 final List<URI> existingDigestList = new ArrayList<>(); 083 existingDigestList.addAll(binary.getContentDigests()); 084 085 final var digestAlgs = existingDigestList.stream() 086 .map(URI::toString) 087 .map(a -> a.replace("urn:", "").split(":")[0]) 088 .map(DigestAlgorithm::fromAlgorithm) 089 .collect(Collectors.toList()); 090 091 try (final var content = binary.getContent()) { 092 final MultiDigestInputStreamWrapper digestWrapper = new MultiDigestInputStreamWrapper(content, 093 existingDigestList, digestAlgs); 094 digestWrapper.getDigests().forEach(d -> 095 model.add(fixityResult, HAS_MESSAGE_DIGEST, model.createResource(d.toString()))); 096 digestWrapper.checkFixity(); 097 model.add(fixityResult, HAS_FIXITY_STATE, successResource); 098 } catch (final IOException e) { 099 // input stream closed prematurely. 100 throw new RepositoryRuntimeException("Problem reading content stream from " + binary.getId(), e); 101 } catch (final InvalidChecksumException e) { 102 model.add(fixityResult, HAS_FIXITY_STATE, badChecksumResource); 103 } 104 return DefaultRdfStream.fromModel(subject.asNode(), model); 105 } 106}