001 // Copyright 2011 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 private final Logger logger;
041
042 private 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 long elapsedNanos = System.nanoTime() - startNanos;
086
087 if (logger.isDebugEnabled())
088 {
089 double elapsedMillis = ((double) elapsedNanos) * NANOS_TO_MILLIS;
090
091 logger.debug(String.format("Minimized %s (%,d input bytes of %s to %,d output bytes in %.2f ms)",
092 input.getDescription(), input.getSize(), resourceType, output.getSize(), elapsedMillis));
093 }
094
095 return output;
096 }
097
098 protected Reader toReader(StreamableResource input) throws IOException
099 {
100 InputStream is = input.openStream();
101
102 return new InputStreamReader(is, "UTF-8");
103 }
104
105 /**
106 * Implemented in subclasses to do the actual work.
107 *
108 * @param resource content to minimize
109 * @param output writer for minimized version of input
110 */
111 protected abstract void doMinimize(StreamableResource resource, Writer output) throws IOException;
112 }