001/* 002 * (C) Copyright 2019 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019package org.nuxeo.ecm.core.blob; 020 021import java.io.OutputStream; 022import java.security.DigestOutputStream; 023import java.security.MessageDigest; 024import java.security.NoSuchAlgorithmException; 025import java.util.Objects; 026import java.util.function.Consumer; 027import java.util.function.Supplier; 028 029import org.apache.commons.codec.binary.Hex; 030import org.apache.commons.lang3.mutable.MutableObject; 031import org.nuxeo.ecm.core.api.NuxeoException; 032 033/** 034 * Represents computation of blob keys based on a message digest. 035 * 036 * @since 11.1 037 */ 038public class KeyStrategyDigest implements KeyStrategy { 039 040 public final String digestAlgorithm; 041 042 public KeyStrategyDigest(String digestAlgorithm) { 043 Objects.requireNonNull(digestAlgorithm); 044 this.digestAlgorithm = digestAlgorithm; 045 } 046 047 @Override 048 public boolean useDeDuplication() { 049 return true; 050 } 051 052 @Override 053 public String getDigestFromKey(String key) { 054 return key; 055 } 056 057 @Override 058 public BlobWriteContext getBlobWriteContext(BlobContext blobContext) { 059 MutableObject<String> keyHolder = new MutableObject<>(); 060 WriteObserver writeObserver = new WriteObserverDigest(digestAlgorithm, keyHolder::setValue); 061 Supplier<String> keyComputer = keyHolder::getValue; 062 return new BlobWriteContext(blobContext, writeObserver, keyComputer, this); 063 } 064 065 /** 066 * Write observer computing a digest. The final digest is made available to the key consumer. 067 * 068 * @since 11.1 069 */ 070 public static class WriteObserverDigest implements WriteObserver { 071 072 protected final MessageDigest messageDigest; 073 074 protected final Consumer<String> keyConsumer; 075 076 protected DigestOutputStream dos; 077 078 public WriteObserverDigest(String digestAlgorithm, Consumer<String> keyConsumer) { 079 try { 080 messageDigest = MessageDigest.getInstance(digestAlgorithm); 081 } catch (NoSuchAlgorithmException e) { 082 throw new NuxeoException(e); 083 } 084 this.keyConsumer = keyConsumer; 085 } 086 087 @Override 088 public OutputStream wrap(OutputStream out) { 089 dos = new DigestOutputStream(out, messageDigest); 090 return dos; 091 } 092 093 @Override 094 public void done() { 095 String key = Hex.encodeHexString(dos.getMessageDigest().digest()); 096 keyConsumer.accept(key); 097 } 098 } 099 100 @Override 101 public boolean equals(Object obj) { 102 if (!(obj instanceof KeyStrategyDigest)) { 103 return false; 104 } 105 KeyStrategyDigest other = (KeyStrategyDigest) obj; 106 return digestAlgorithm.equals(other.digestAlgorithm); 107 } 108 109 @Override 110 public int hashCode() { 111 return digestAlgorithm.hashCode(); 112 } 113 114}