001 // Copyright 2005 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
015 package org.apache.tapestry.asset;
016
017 import java.io.BufferedInputStream;
018 import java.io.IOException;
019 import java.io.InputStream;
020 import java.net.URL;
021 import java.security.MessageDigest;
022 import java.util.HashMap;
023 import java.util.Iterator;
024 import java.util.Map;
025
026 import org.apache.commons.codec.binary.Hex;
027 import org.apache.hivemind.ApplicationRuntimeException;
028 import org.apache.hivemind.ClassResolver;
029 import org.apache.hivemind.util.IOUtils;
030 import org.apache.tapestry.event.ReportStatusEvent;
031 import org.apache.tapestry.event.ReportStatusListener;
032 import org.apache.tapestry.event.ResetEventListener;
033
034 /**
035 * Implementation of {@link org.apache.tapestry.asset.ResourceDigestSource} that calculates an
036 * DIGEST checksum digest and converts it to a string of hex digits.
037 *
038 * @author Howard M. Lewis Ship
039 * @since 4.0
040 */
041 public class ResourceDigestSourceImpl implements ResourceDigestSource, ResetEventListener,
042 ReportStatusListener
043 {
044 private String _serviceId;
045
046 private ClassResolver _classResolver;
047
048 private static final int BUFFER_SIZE = 5000;
049
050 /**
051 * Map keyed on resource path of DIGEST checksum (as a string).
052 */
053
054 private final Map _cache = new HashMap();
055
056 public synchronized String getDigestForResource(String resourcePath)
057 {
058 String result = (String) _cache.get(resourcePath);
059
060 if (result == null)
061 {
062 result = computeMD5(resourcePath);
063 _cache.put(resourcePath, result);
064 }
065
066 return result;
067 }
068
069 public synchronized void resetEventDidOccur()
070 {
071 _cache.clear();
072 }
073
074 public synchronized void reportStatus(ReportStatusEvent event)
075 {
076 event.title(_serviceId);
077 event.property("resource count", _cache.size());
078
079 Iterator i = _cache.entrySet().iterator();
080
081 while (i.hasNext())
082 {
083 Map.Entry entry = (Map.Entry) i.next();
084
085 event.property(entry.getKey().toString(), entry.getValue());
086 }
087 }
088
089 private String computeMD5(String resourcePath)
090 {
091 URL url = _classResolver.getResource(resourcePath);
092
093 if (url == null)
094 throw new ApplicationRuntimeException(AssetMessages.noSuchResource(resourcePath));
095
096 InputStream stream = null;
097
098 try
099 {
100 MessageDigest digest = MessageDigest.getInstance("MD5");
101
102 stream = new BufferedInputStream(url.openStream());
103
104 digestStream(digest, stream);
105
106 stream.close();
107 stream = null;
108
109 byte[] bytes = digest.digest();
110 char[] encoded = Hex.encodeHex(bytes);
111
112 return new String(encoded);
113 }
114 catch (IOException ex)
115 {
116 throw new ApplicationRuntimeException(AssetMessages.unableToReadResource(
117 resourcePath,
118 ex));
119 }
120 catch (Exception ex)
121 {
122 throw new ApplicationRuntimeException(ex);
123 }
124 finally
125 {
126 IOUtils.close(stream);
127 }
128 }
129
130 private void digestStream(MessageDigest digest, InputStream stream) throws IOException
131 {
132 byte[] buffer = new byte[BUFFER_SIZE];
133
134 while (true)
135 {
136 int length = stream.read(buffer);
137
138 if (length < 0)
139 return;
140
141 digest.update(buffer, 0, length);
142 }
143 }
144
145 public void setClassResolver(ClassResolver classResolver)
146 {
147 _classResolver = classResolver;
148 }
149
150 public void setServiceId(String serviceId)
151 {
152 _serviceId = serviceId;
153 }
154 }