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.ContentType; 016import org.apache.tapestry5.internal.TapestryInternalUtils; 017import org.apache.tapestry5.ioc.Resource; 018import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 019import org.apache.tapestry5.services.assets.*; 020 021import java.io.ByteArrayOutputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.util.Map; 025import java.util.Set; 026 027public class StreamableResourceSourceImpl implements StreamableResourceSource 028{ 029 private final Map<String, ResourceTransformer> configuration; 030 031 private final ContentTypeAnalyzer contentTypeAnalyzer; 032 033 private final CompressionAnalyzer compressionAnalyzer; 034 035 private final ResourceChangeTracker resourceChangeTracker; 036 037 private final AssetChecksumGenerator checksumGenerator; 038 039 public StreamableResourceSourceImpl(Map<String, ResourceTransformer> configuration, 040 ContentTypeAnalyzer contentTypeAnalyzer, CompressionAnalyzer compressionAnalyzer, 041 ResourceChangeTracker resourceChangeTracker, AssetChecksumGenerator checksumGenerator) 042 { 043 this.configuration = configuration; 044 this.contentTypeAnalyzer = contentTypeAnalyzer; 045 this.compressionAnalyzer = compressionAnalyzer; 046 this.resourceChangeTracker = resourceChangeTracker; 047 this.checksumGenerator = checksumGenerator; 048 } 049 050 public Set<String> fileExtensionsForContentType(ContentType contentType) 051 { 052 Set<String> result = CollectionFactory.newSet(); 053 054 for (Map.Entry<String, ResourceTransformer> me : configuration.entrySet()) 055 { 056 if (me.getValue().getTransformedContentType().equals(contentType)) 057 { 058 result.add(me.getKey()); 059 } 060 } 061 062 return result; 063 } 064 065 public StreamableResource getStreamableResource(Resource baseResource, StreamableResourceProcessing processing, ResourceDependencies dependencies) 066 throws IOException 067 { 068 assert baseResource != null; 069 070 if (!baseResource.exists()) 071 { 072 throw new IOException(String.format("Resource %s does not exist.", baseResource)); 073 } 074 075 String fileSuffix = TapestryInternalUtils.toFileSuffix(baseResource.getFile()); 076 077 // Optionally, transform the resource. The main driver for this is to allow 078 // for libraries like LessJS (http://lesscss.org/) or 079 // http://jashkenas.github.com/coffee-script/ 080 ResourceTransformer rt = configuration.get(fileSuffix); 081 082 InputStream transformed = rt == null ? baseResource.openStream() : rt.transform(baseResource, dependencies); 083 084 assert transformed != null; 085 086 BytestreamCache bytestreamCache = readStream(transformed); 087 088 transformed.close(); 089 090 ContentType contentType = rt == null 091 ? new ContentType(contentTypeAnalyzer.getContentType(baseResource)) 092 : rt.getTransformedContentType(); 093 094 boolean compressable = compressionAnalyzer.isCompressable(contentType.getMimeType()); 095 096 long lastModified = resourceChangeTracker.trackResource(baseResource); 097 098 return new StreamableResourceImpl(baseResource.toString(), contentType, compressable ? CompressionStatus.COMPRESSABLE 099 : CompressionStatus.NOT_COMPRESSABLE, lastModified, bytestreamCache, checksumGenerator, null); 100 } 101 102 private BytestreamCache readStream(InputStream stream) throws IOException 103 { 104 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 105 106 TapestryInternalUtils.copy(stream, bos); 107 108 stream.close(); 109 110 return new BytestreamCache(bos); 111 } 112 113}