001// Licensed under the Apache License, Version 2.0 (the "License"); 002// you may not use this file except in compliance with the License. 003// You may obtain a copy of the License at 004// 005// http://www.apache.org/licenses/LICENSE-2.0 006// 007// Unless required by applicable law or agreed to in writing, software 008// distributed under the License is distributed on an "AS IS" BASIS, 009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 010// See the License for the specific language governing permissions and 011// limitations under the License. 012 013package org.apache.tapestry5.internal.services.assets; 014 015import org.apache.tapestry5.commons.Resource; 016import org.apache.tapestry5.commons.util.CollectionFactory; 017import org.apache.tapestry5.http.ContentType; 018import org.apache.tapestry5.http.services.CompressionAnalyzer; 019import org.apache.tapestry5.internal.TapestryInternalUtils; 020import org.apache.tapestry5.services.assets.*; 021 022import java.io.ByteArrayOutputStream; 023import java.io.IOException; 024import java.io.InputStream; 025import java.util.Map; 026import java.util.Set; 027 028public class StreamableResourceSourceImpl implements StreamableResourceSource 029{ 030 private final Map<String, ResourceTransformer> configuration; 031 032 private final ContentTypeAnalyzer contentTypeAnalyzer; 033 034 private final CompressionAnalyzer compressionAnalyzer; 035 036 private final ResourceChangeTracker resourceChangeTracker; 037 038 private final AssetChecksumGenerator checksumGenerator; 039 040 public StreamableResourceSourceImpl(Map<String, ResourceTransformer> configuration, 041 ContentTypeAnalyzer contentTypeAnalyzer, CompressionAnalyzer compressionAnalyzer, 042 ResourceChangeTracker resourceChangeTracker, AssetChecksumGenerator checksumGenerator) 043 { 044 this.configuration = configuration; 045 this.contentTypeAnalyzer = contentTypeAnalyzer; 046 this.compressionAnalyzer = compressionAnalyzer; 047 this.resourceChangeTracker = resourceChangeTracker; 048 this.checksumGenerator = checksumGenerator; 049 } 050 051 public Set<String> fileExtensionsForContentType(ContentType contentType) 052 { 053 Set<String> result = CollectionFactory.newSet(); 054 055 for (Map.Entry<String, ResourceTransformer> me : configuration.entrySet()) 056 { 057 if (me.getValue().getTransformedContentType().equals(contentType)) 058 { 059 result.add(me.getKey()); 060 } 061 } 062 063 return result; 064 } 065 066 public StreamableResource getStreamableResource(Resource baseResource, StreamableResourceProcessing processing, ResourceDependencies dependencies) 067 throws IOException 068 { 069 assert baseResource != null; 070 071 if (!baseResource.exists()) 072 { 073 throw new IOException(String.format("Resource %s does not exist.", baseResource)); 074 } 075 076 String fileSuffix = TapestryInternalUtils.toFileSuffix(baseResource.getFile()); 077 078 // Optionally, transform the resource. The main driver for this is to allow 079 // for libraries like LessJS (http://lesscss.org/) or 080 // http://jashkenas.github.com/coffee-script/ 081 ResourceTransformer rt = configuration.get(fileSuffix); 082 083 InputStream transformed = rt == null ? baseResource.openStream() : rt.transform(baseResource, dependencies); 084 085 assert transformed != null; 086 087 BytestreamCache bytestreamCache = readStream(transformed); 088 089 transformed.close(); 090 091 ContentType contentType = rt == null 092 ? new ContentType(contentTypeAnalyzer.getContentType(baseResource)) 093 : rt.getTransformedContentType(); 094 095 boolean compressable = compressionAnalyzer.isCompressable(contentType.getMimeType()); 096 097 long lastModified = resourceChangeTracker.trackResource(baseResource); 098 099 return new StreamableResourceImpl(baseResource.toString(), contentType, compressable ? CompressionStatus.COMPRESSABLE 100 : CompressionStatus.NOT_COMPRESSABLE, lastModified, bytestreamCache, checksumGenerator, null); 101 } 102 103 private BytestreamCache readStream(InputStream stream) throws IOException 104 { 105 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 106 107 TapestryInternalUtils.copy(stream, bos); 108 109 stream.close(); 110 111 return new BytestreamCache(bos); 112 } 113 114}