001// Copyright 2013 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry5.internal.services.assets; 016 017import org.apache.tapestry5.commons.Resource; 018import org.apache.tapestry5.commons.util.CollectionFactory; 019import org.apache.tapestry5.services.assets.AssetChecksumGenerator; 020import org.apache.tapestry5.services.assets.StreamableResource; 021import org.apache.tapestry5.services.assets.StreamableResourceProcessing; 022import org.apache.tapestry5.services.assets.StreamableResourceSource; 023 024import java.io.IOException; 025import java.io.InputStream; 026import java.util.Map; 027import java.util.zip.Adler32; 028 029public class AssetChecksumGeneratorImpl implements AssetChecksumGenerator 030{ 031 032 private final StreamableResourceSource streamableResourceSource; 033 034 private final ResourceChangeTracker tracker; 035 036 private final Map<Integer, String> cache = CollectionFactory.newConcurrentMap(); 037 038 public AssetChecksumGeneratorImpl(StreamableResourceSource streamableResourceSource, ResourceChangeTracker tracker) 039 { 040 this.streamableResourceSource = streamableResourceSource; 041 this.tracker = tracker; 042 043 tracker.clearOnInvalidation(cache); 044 } 045 046 public String generateChecksum(Resource resource) throws IOException 047 { 048 StreamableResource streamable = streamableResourceSource.getStreamableResource(resource, StreamableResourceProcessing.COMPRESSION_DISABLED, 049 tracker); 050 051 return generateChecksum(streamable); 052 } 053 054 public String generateChecksum(StreamableResource resource) throws IOException 055 { 056 return cache.computeIfAbsent(resource.hashCode(), 057 r -> { 058 try { 059 return toChecksum(resource.openStream()); 060 } catch (IOException e) { 061 throw new RuntimeException(e); 062 } 063 }); 064 } 065 066 private String toChecksum(InputStream is) throws IOException 067 { 068 // Adler32 is very fast and suitable for these purposes (MD5 and SHA are slower, and 069 // are targetted at cryptographic solutions). 070 Adler32 checksum = new Adler32(); 071 072 byte[] buffer = new byte[1024]; 073 074 try 075 { 076 while (true) 077 { 078 int length = is.read(buffer); 079 080 if (length < 0) 081 { 082 break; 083 } 084 085 checksum.update(buffer, 0, length); 086 } 087 088 // Reduces it down to just 32 bits which we express in hex. 089 return Long.toHexString(checksum.getValue()); 090 } finally 091 { 092 is.close(); 093 } 094 } 095}