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 }