    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}