001// Copyright 2006, 2007, 2008 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;
016
017import org.apache.commons.codec.binary.Hex;
018import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
019import org.apache.tapestry5.ioc.internal.util.InternalUtils;
020import org.apache.tapestry5.services.ResourceDigestGenerator;
021
022import java.io.BufferedInputStream;
023import java.io.IOException;
024import java.io.InputStream;
025import java.net.URL;
026import java.security.MessageDigest;
027import java.util.Collection;
028import java.util.Set;
029
030/**
031 * Implementation of {@link ResourceDigestGenerator} that generates MD5 digests.
032 */
033public class ResourceDigestGeneratorImpl implements ResourceDigestGenerator
034{
035    private static final int BUFFER_SIZE = 5000;
036
037    private final Set<String> digestExtensions;
038
039    public ResourceDigestGeneratorImpl(Collection<String> configuration)
040    {
041        digestExtensions = CollectionFactory.newSet(configuration);
042    }
043
044    public String generateDigest(URL url)
045    {
046        InputStream stream = null;
047
048        try
049        {
050            MessageDigest digest = MessageDigest.getInstance("MD5");
051
052            stream = new BufferedInputStream(url.openStream());
053
054            digestStream(digest, stream);
055
056            stream.close();
057            stream = null;
058
059            byte[] bytes = digest.digest();
060            char[] encoded = Hex.encodeHex(bytes);
061
062            return new String(encoded);
063        }
064        catch (Exception ex)
065        {
066            throw new RuntimeException(ex);
067        }
068        finally
069        {
070            InternalUtils.close(stream);
071        }
072    }
073
074    private void digestStream(MessageDigest digest, InputStream stream) throws IOException
075    {
076        byte[] buffer = new byte[BUFFER_SIZE];
077
078        while (true)
079        {
080            int length = stream.read(buffer);
081
082            if (length < 0) return;
083
084            digest.update(buffer, 0, length);
085        }
086    }
087
088    /**
089     * Current implementation is based on the path extension, and a configured list of extensions.
090     */
091    public boolean requiresDigest(String path)
092    {
093        int dotx = path.lastIndexOf('.');
094        String extension = path.substring(dotx + 1).toLowerCase();
095
096        return digestExtensions.contains(extension);
097    }
098
099}