001 // Copyright 2011-2012 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.tapestry5.internal.yuicompressor; 016 017 import org.apache.tapestry5.internal.IOOperation; 018 import org.apache.tapestry5.internal.TapestryInternalUtils; 019 import org.apache.tapestry5.internal.services.assets.BytestreamCache; 020 import org.apache.tapestry5.internal.services.assets.StreamableResourceImpl; 021 import org.apache.tapestry5.ioc.OperationTracker; 022 import org.apache.tapestry5.ioc.internal.util.InternalUtils; 023 import org.apache.tapestry5.services.assets.CompressionStatus; 024 import org.apache.tapestry5.services.assets.ResourceMinimizer; 025 import org.apache.tapestry5.services.assets.StreamableResource; 026 import org.slf4j.Logger; 027 028 import javax.management.RuntimeErrorException; 029 import java.io.*; 030 031 /** 032 * Base class for resource minimizers. 033 * 034 * @since 5.3 035 */ 036 public abstract class AbstractMinimizer implements ResourceMinimizer 037 { 038 private static final double NANOS_TO_MILLIS = 1.0d / 1000000.0d; 039 040 protected final Logger logger; 041 042 protected final OperationTracker tracker; 043 044 private final String resourceType; 045 046 public AbstractMinimizer(Logger logger, OperationTracker tracker, String resourceType) 047 { 048 this.logger = logger; 049 this.tracker = tracker; 050 this.resourceType = resourceType; 051 } 052 053 public StreamableResource minimize(final StreamableResource input) throws IOException 054 { 055 long startNanos = System.nanoTime(); 056 057 ByteArrayOutputStream bos = new ByteArrayOutputStream(1000); 058 059 final Writer writer = new OutputStreamWriter(bos); 060 061 TapestryInternalUtils.performIO(tracker, "Minimizing " + resourceType, new IOOperation() 062 { 063 public void perform() throws IOException 064 { 065 try 066 { 067 doMinimize(input, writer); 068 } catch (RuntimeErrorException ex) 069 { 070 throw new RuntimeException(String.format("Unable to minimize %s: %s", resourceType, 071 InternalUtils.toMessage(ex)), ex); 072 } 073 074 } 075 }); 076 077 writer.close(); 078 079 // The content is minimized, but can still be (GZip) compressed. 080 081 StreamableResource output = new StreamableResourceImpl("minimized " + input.getDescription(), 082 input.getContentType(), CompressionStatus.COMPRESSABLE, 083 input.getLastModified(), new BytestreamCache(bos)); 084 085 if (logger.isInfoEnabled()) 086 { 087 long elapsedNanos = System.nanoTime() - startNanos; 088 089 int inputSize = input.getSize(); 090 int outputSize = output.getSize(); 091 092 double elapsedMillis = ((double) elapsedNanos) * NANOS_TO_MILLIS; 093 // e.g., reducing 100 bytes to 25 would be a (100-25)/100 reduction, or 75% 094 double reduction = 100d * ((double) (inputSize - outputSize)) / ((double) inputSize); 095 096 logger.info(String.format("Minimized %s (%,d input bytes of %s to %,d output bytes in %.2f ms, %.2f%% reduction)", 097 input.getDescription(), inputSize, resourceType, outputSize, elapsedMillis, reduction)); 098 } 099 100 return output; 101 } 102 103 protected Reader toReader(StreamableResource input) throws IOException 104 { 105 InputStream is = input.openStream(); 106 107 return new InputStreamReader(is, "UTF-8"); 108 } 109 110 /** 111 * Implemented in subclasses to do the actual work. 112 * 113 * @param resource 114 * content to minimize 115 * @param output 116 * writer for minimized version of input 117 */ 118 protected abstract void doMinimize(StreamableResource resource, Writer output) throws IOException; 119 }