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<StreamableResource, 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 String result = cache.get(resource); 057 058 if (result == null) 059 { 060 result = toChecksum(resource.openStream()); 061 062 cache.put(resource, result); 063 } 064 065 return result; 066 } 067 068 private String toChecksum(InputStream is) throws IOException 069 { 070 // Adler32 is very fast and suitable for these purposes (MD5 and SHA are slower, and 071 // are targetted at cryptographic solutions). 072 Adler32 checksum = new Adler32(); 073 074 byte[] buffer = new byte[1024]; 075 076 try 077 { 078 while (true) 079 { 080 int length = is.read(buffer); 081 082 if (length < 0) 083 { 084 break; 085 } 086 087 checksum.update(buffer, 0, length); 088 } 089 090 // Reduces it down to just 32 bits which we express in hex. 091 return Long.toHexString(checksum.getValue()); 092 } finally 093 { 094 is.close(); 095 } 096 } 097}