blob: 9b2b4dffdbf752c817754d8d0a2355dee207d25d [file]
// Copyright 2005 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry.asset;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.codec.binary.Hex;
import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.hivemind.ClassResolver;
import org.apache.hivemind.util.IOUtils;
import org.apache.tapestry.event.ReportStatusEvent;
import org.apache.tapestry.event.ReportStatusListener;
import org.apache.tapestry.event.ResetEventListener;
/**
* Implementation of {@link org.apache.tapestry.asset.ResourceDigestSource} that calculates an
* DIGEST checksum digest and converts it to a string of hex digits.
*
* @author Howard M. Lewis Ship
* @since 4.0
*/
public class ResourceDigestSourceImpl implements ResourceDigestSource, ResetEventListener,
ReportStatusListener
{
private static final int BUFFER_SIZE = 5000;
private String _serviceId;
private ClassResolver _classResolver;
/**
* Map keyed on resource path of DIGEST checksum (as a string).
*/
private final Map _cache = new HashMap();
public synchronized String getDigestForResource(String resourcePath)
{
if (resourcePath == null) return null;
String result = (String) _cache.get(resourcePath);
if (result == null)
{
result = computeMD5(resourcePath);
_cache.put(resourcePath, result);
}
return result;
}
public synchronized void resetEventDidOccur()
{
_cache.clear();
}
public synchronized void reportStatus(ReportStatusEvent event)
{
event.title(_serviceId);
event.property("resource count", _cache.size());
Iterator i = _cache.entrySet().iterator();
while (i.hasNext())
{
Map.Entry entry = (Map.Entry) i.next();
event.property(entry.getKey().toString(), entry.getValue());
}
}
private String computeMD5(String resourcePath)
{
URL url = _classResolver.getResource(resourcePath);
if (url == null)
throw new ApplicationRuntimeException(AssetMessages.noSuchResource(resourcePath));
InputStream stream = null;
try
{
MessageDigest digest = MessageDigest.getInstance("MD5");
stream = new BufferedInputStream(url.openStream());
digestStream(digest, stream);
stream.close();
stream = null;
byte[] bytes = digest.digest();
char[] encoded = Hex.encodeHex(bytes);
return new String(encoded);
}
catch (IOException ex)
{
throw new ApplicationRuntimeException(AssetMessages.unableToReadResource(
resourcePath,
ex));
}
catch (Exception ex)
{
throw new ApplicationRuntimeException(ex);
}
finally
{
IOUtils.close(stream);
}
}
private void digestStream(MessageDigest digest, InputStream stream) throws IOException
{
byte[] buffer = new byte[BUFFER_SIZE];
while (true)
{
int length = stream.read(buffer);
if (length < 0)
return;
digest.update(buffer, 0, length);
}
}
public void setClassResolver(ClassResolver classResolver)
{
_classResolver = classResolver;
}
public void setServiceId(String serviceId)
{
_serviceId = serviceId;
}
}