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.modeshape.utils; 019 020import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 021import org.fcrepo.kernel.api.exception.UnsupportedAlgorithmException; 022import org.fcrepo.kernel.api.utils.CacheEntry; 023import org.fcrepo.kernel.api.utils.ContentDigest; 024import org.fcrepo.kernel.api.utils.FixityResult; 025 026import org.slf4j.Logger; 027 028import java.io.IOException; 029import java.io.InputStream; 030import java.net.URI; 031import java.security.DigestInputStream; 032import java.security.MessageDigest; 033import java.security.NoSuchAlgorithmException; 034import java.util.Collection; 035import java.util.HashMap; 036import java.util.Map; 037import java.util.stream.Collectors; 038 039import static java.util.Collections.singletonList; 040import static org.slf4j.LoggerFactory.getLogger; 041 042/** 043 * Cache entry that wraps a binary stream and provides 044 * fixity methods against it 045 * 046 * @author fasseg 047 */ 048public abstract class BasicCacheEntry implements CacheEntry { 049 050 private static final int DEV_NULL_BUFFER_SIZE = 4096; 051 052 private static final byte[] devNull = new byte[DEV_NULL_BUFFER_SIZE]; 053 054 private static final Logger LOGGER = getLogger(BasicCacheEntry.class); 055 056 /** 057 * Calculate the fixity of a CacheEntry by piping it through 058 * a simple fixity-calculating InputStream 059 * 060 * @param algorithm the digest algorithm to be used 061 * @return the fixity of this cache entry 062 */ 063 @Override 064 public Collection<FixityResult> checkFixity(final String algorithm) { 065 066 try (FixityInputStream fixityInputStream = new FixityInputStream( 067 this.getInputStream(), MessageDigest.getInstance(algorithm))) { 068 069 // actually calculate the digest by consuming the stream 070 while (fixityInputStream.read(devNull) != -1) { } 071 072 final URI calculatedChecksum = 073 ContentDigest.asURI(algorithm, fixityInputStream.getMessageDigest().digest()); 074 075 final FixityResult result = 076 new FixityResultImpl(getExternalIdentifier(), 077 fixityInputStream.getByteCount(), 078 calculatedChecksum, 079 algorithm); 080 081 return singletonList(result); 082 } catch (final IOException e) { 083 LOGGER.debug("Got error closing input stream: {}", e); 084 throw new RepositoryRuntimeException(e); 085 } catch (final NoSuchAlgorithmException e1) { 086 throw new RepositoryRuntimeException(e1); 087 } 088 } 089 090 /** 091 * Calculate fixity with list of digest algorithms of a CacheEntry by piping it through 092 * a simple fixity-calculating InputStream 093 * 094 * @param algorithms the digest algorithms to be used 095 * @return the checksums for the digest algorithms 096 * @throws UnsupportedAlgorithmException exception 097 */ 098 @Override 099 public Collection<URI> checkFixity(final Collection<String> algorithms) throws UnsupportedAlgorithmException { 100 101 try (InputStream binaryStream = this.getInputStream()) { 102 103 final Map<String, DigestInputStream> digestInputStreams = new HashMap<>(); 104 InputStream digestStream = binaryStream; 105 for (String digestAlg : algorithms) { 106 107 try { 108 digestStream = new DigestInputStream(digestStream, MessageDigest.getInstance(digestAlg)); 109 digestInputStreams.put(digestAlg, (DigestInputStream)digestStream); 110 } catch (NoSuchAlgorithmException e) { 111 throw new UnsupportedAlgorithmException("Unsupported digest algorithm: " + digestAlg); 112 } 113 } 114 115 // calculate the digest by consuming the stream 116 while (digestStream.read(devNull) != -1) { } 117 118 return digestInputStreams.entrySet().stream() 119 .map(entry -> ContentDigest.asURI(entry.getKey(), entry.getValue().getMessageDigest().digest())) 120 .collect(Collectors.toSet()); 121 } catch (final IOException e) { 122 LOGGER.debug("Got error closing input stream: {}", e); 123 throw new RepositoryRuntimeException(e); 124 } 125 } 126}