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 static final int BUFFER_SIZE = 5000;
045
046 private String _serviceId;
047
048 private ClassResolver _classResolver;
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 if (resourcePath == null) return null;
059
060 String result = (String) _cache.get(resourcePath);
061
062 if (result == null)
063 {
064 result = computeMD5(resourcePath);
065 _cache.put(resourcePath, result);
066 }
067
068 return result;
069 }
070
071 public synchronized void resetEventDidOccur()
072 {
073 _cache.clear();
074 }
075
076 public synchronized void reportStatus(ReportStatusEvent event)
077 {
078 event.title(_serviceId);
079 event.property("resource count", _cache.size());
080
081 Iterator i = _cache.entrySet().iterator();
082
083 while (i.hasNext())
084 {
085 Map.Entry entry = (Map.Entry) i.next();
086
087 event.property(entry.getKey().toString(), entry.getValue());
088 }
089 }
090
091 private String computeMD5(String resourcePath)
092 {
093 URL url = _classResolver.getResource(resourcePath);
094
095 if (url == null)
096 throw new ApplicationRuntimeException(AssetMessages.noSuchResource(resourcePath));
097
098 InputStream stream = null;
099
100 try
101 {
102 MessageDigest digest = MessageDigest.getInstance("MD5");
103
104 stream = new BufferedInputStream(url.openStream());
105
106 digestStream(digest, stream);
107
108 stream.close();
109 stream = null;
110
111 byte[] bytes = digest.digest();
112 char[] encoded = Hex.encodeHex(bytes);
113
114 return new String(encoded);
115 }
116 catch (IOException ex)
117 {
118 throw new ApplicationRuntimeException(AssetMessages.unableToReadResource(
119 resourcePath,
120 ex));
121 }
122 catch (Exception ex)
123 {
124 throw new ApplicationRuntimeException(ex);
125 }
126 finally
127 {
128 IOUtils.close(stream);
129 }
130 }
131
132 private void digestStream(MessageDigest digest, InputStream stream) throws IOException
133 {
134 byte[] buffer = new byte[BUFFER_SIZE];
135
136 while (true)
137 {
138 int length = stream.read(buffer);
139
140 if (length < 0)
141 return;
142
143 digest.update(buffer, 0, length);
144 }
145 }
146
147 public void setClassResolver(ClassResolver classResolver)
148 {
149 _classResolver = classResolver;
150 }
151
152 public void setServiceId(String serviceId)
153 {
154 _serviceId = serviceId;
155 }
156 }