package com.anjlab.tapestry5;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;
import java.util.zip.GZIPInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.tapestry5.internal.services.ResourceStreamerImpl;
import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker;
import org.apache.tapestry5.ioc.OperationTracker;
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.Response;
import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
import org.apache.tapestry5.services.assets.CompressionStatus;
import org.apache.tapestry5.services.assets.StreamableResource;
import org.apache.tapestry5.services.assets.StreamableResourceSource;
public class StaticAssetResourceStreamer extends ResourceStreamerImpl {
private final Request request;
public StaticAssetResourceStreamer(Request request, Response response,
StreamableResourceSource streamableResourceSource,
ResponseCompressionAnalyzer analyzer, OperationTracker tracker,
boolean productionMode, ResourceChangeTracker resourceChangeTracker) {
super(request, response, streamableResourceSource, analyzer, tracker,
productionMode, resourceChangeTracker);
this.request = request;
}
public static final String ASSETS_STATIC_PROPERTIES = "assets/static.properties";
@Override
public void streamResource(StreamableResource resource) throws IOException {
String header = request.getHeader(StaticAssetPathConverter.ASSETS_PRECOMPILATION);
if (header != null) {
// Only create static assets during precompilation
String path = request.getPath();
if (path.startsWith("/assets")) {
try {
createStaticAsset(path, (resource.getCompression() == CompressionStatus.COMPRESSED)
? uncompress(resource.openStream()) : resource.openStream());
} catch (Exception e) {
throw new RuntimeException("Try running application with system property -D--enable_all_permissions=true", e);
}
}
}
super.streamResource(resource);
}
private InputStream uncompress(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
GZIPInputStream gzip = new GZIPInputStream(input);
IOUtils.copy(gzip, output);
gzip.close();
return new ByteArrayInputStream(output.toByteArray());
}
@Override
public void streamResource(Resource resource) throws IOException {
super.streamResource(resource);
}
private synchronized void createStaticAsset(String assetPathVersioned, InputStream input)
throws IOException, ClientProtocolException, NoSuchAlgorithmException, FileNotFoundException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(input, baos);
byte[] responseBytes = baos.toByteArray();
File staticPropertiesFile = new File(ASSETS_STATIC_PROPERTIES);
Properties properties = loadStaticProperties(staticPropertiesFile);
copyFile(responseBytes, assetPathVersioned.substring(1));
String fileHash = calculateDigest(inputStream(responseBytes));
properties.put(assetPathVersioned, fileHash);
FileOutputStream outputStream = new FileOutputStream(staticPropertiesFile);
properties.store(outputStream, "Static Assets For Tapestry5 Application");
outputStream.close();
}
public Properties loadStaticProperties(File staticPropertiesFile) throws IOException
{
Properties properties = new Properties();
if (staticPropertiesFile.exists()) {
FileInputStream inputStream = new FileInputStream(staticPropertiesFile);
properties.load(inputStream);
inputStream.close();
}
return properties;
}
private ByteArrayInputStream inputStream(byte[] responseBytes) {
return new ByteArrayInputStream(responseBytes);
}
private void copyFile(byte[] responseBytes, String targetPath) throws IOException
{
File targetFile = new File(targetPath);
if (!targetFile.getParentFile().exists()) {
targetFile.getParentFile().mkdirs();
}
if (!targetFile.exists()) {
FileOutputStream outputStream = new FileOutputStream(targetFile);
IOUtils.copy(inputStream(responseBytes), outputStream);
outputStream.close();
}
}
private String calculateDigest(InputStream input) throws NoSuchAlgorithmException, FileNotFoundException, IOException {
MessageDigest digest = MessageDigest.getInstance("SHA1");
digest.reset();
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = input.read(buf)) > 0) {
digest.update(buf, 0, bytesRead);
}
return new BigInteger(1, digest.digest()).toString(16);
}
}