blob: 4475bec412c7c00e7bb77c9ecf6c68c22ded801b [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry5.internal.webresources;
import org.apache.tapestry5.internal.TapestryInternalUtils;
import org.apache.tapestry5.internal.services.assets.BytestreamCache;
import org.apache.tapestry5.internal.services.assets.StreamableResourceImpl;
import org.apache.tapestry5.ioc.IOOperation;
import org.apache.tapestry5.ioc.OperationTracker;
import org.apache.tapestry5.services.assets.AssetChecksumGenerator;
import org.apache.tapestry5.services.assets.CompressionStatus;
import org.apache.tapestry5.services.assets.ResourceMinimizer;
import org.apache.tapestry5.services.assets.StreamableResource;
import org.slf4j.Logger;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Base class for resource minimizers.
*
* @since 5.3
*/
public abstract class AbstractMinimizer implements ResourceMinimizer
{
private static final double NANOS_TO_MILLIS = 1.0d / 1000000.0d;
protected final Logger logger;
protected final OperationTracker tracker;
private final AssetChecksumGenerator checksumGenerator;
private final String resourceType;
public AbstractMinimizer(Logger logger, OperationTracker tracker, AssetChecksumGenerator checksumGenerator, String resourceType)
{
this.logger = logger;
this.tracker = tracker;
this.resourceType = resourceType;
this.checksumGenerator = checksumGenerator;
}
@Override
public StreamableResource minimize(final StreamableResource input) throws IOException
{
if (!isEnabled(input))
{
return input;
}
long startNanos = System.nanoTime();
final ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
tracker.perform("Minimizing " + input, new IOOperation<Void>()
{
@Override
public Void perform() throws IOException
{
InputStream in = doMinimize(input);
TapestryInternalUtils.copy(in, bos);
in.close();
return null;
}
});
// The content is minimized, but can still be (GZip) compressed.
StreamableResource output = new StreamableResourceImpl("minimized " + input.getDescription(),
input.getContentType(), CompressionStatus.COMPRESSABLE,
input.getLastModified(), new BytestreamCache(bos), checksumGenerator, input.getResponseCustomizer());
if (logger.isInfoEnabled())
{
long elapsedNanos = System.nanoTime() - startNanos;
int inputSize = input.getSize();
int outputSize = output.getSize();
double elapsedMillis = ((double) elapsedNanos) * NANOS_TO_MILLIS;
// e.g., reducing 100 bytes to 25 would be a (100-25)/100 reduction, or 75%
double reduction = 100d * ((double) (inputSize - outputSize)) / ((double) inputSize);
logger.info(String.format("Minimized %s (%,d input bytes of %s to %,d output bytes in %.2f ms, %.2f%% reduction)",
input.getDescription(), inputSize, resourceType, outputSize, elapsedMillis, reduction));
}
return output;
}
/**
* Implemented in subclasses to do the actual work.
*
* @param resource
* content to minimize
* @return stream of minimized content
*/
protected abstract InputStream doMinimize(StreamableResource resource) throws IOException;
/**
* Determines if the resource can be minimized.
*
* @return true, subclasses may override
*/
protected boolean isEnabled(StreamableResource resource)
{
return true;
}
}