blob: dd19e87ca2856406dad7fb2208c52df9225c6eaa [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.myfaces.buildtools.maven2.plugin.javascript.jmt;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.myfaces.buildtools.maven2.plugin.javascript.jmt.compress.CompressionException;
import org.apache.myfaces.buildtools.maven2.plugin.javascript.jmt.compress.IsolatedClassLoader;
import org.apache.myfaces.buildtools.maven2.plugin.javascript.jmt.compress.JSCompressor;
import org.apache.myfaces.buildtools.maven2.plugin.javascript.jmt.compress.JSCompressorProxy;
import org.apache.myfaces.buildtools.maven2.plugin.javascript.jmt.compress.JSMinCompressor;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
/**
* Abstact mojo for compressing JavaScripts.
*
* @author <a href="mailto:nicolas@apache.org">nicolas De Loof</a>
*/
public abstract class AbstractCompressMojo
extends AbstractMojo
{
/**
* Resolves the artifacts and dependencies.
*
* @component
*/
private ArtifactResolver artifactResolver;
/**
* Create Artifact references
*
* @component
*/
private ArtifactFactory artifactFactory;
/**
* The local repository
*
* @parameter expression="${localRepository}"
* @required
*/
private ArtifactRepository localRepository;
/**
* For retrieval of artifact's metadata.
*
* @component
*/
private ArtifactMetadataSource metadataSource;
/**
* The remote repositories declared in the pom.
*
* @parameter expression="${project.pluginArtifactRepositories}"
*/
private List remoteRepositories;
/**
*
*/
private static final NumberFormat INTEGER = NumberFormat.getIntegerInstance();
private static final String HR = StringUtils.rightPad( "", 78, "-" );
/**
* The maven project we are working on.
*
* @parameter default-value="${project}"
* @required
* @readonly
*/
MavenProject project;
/**
* The available compressors
*/
private Map compressors = new HashMap();
{
compressors.put( "jsmin", new JSMinCompressor() );
}
/**
* Optimization level, from 0 to 9
*
* @parameter default-value="9"
*/
private int optimizationLevel;
/**
* JS Language version (130 for JS 1.3)
*
* @parameter default-value="130"
*/
private int languageVersion;
/**
* The compressor to used. Either "shrinksafe", "yahooui" or "jsmin" for default compressor,
* or a custom one provided as an artifact in repo org.codehaus.mojo.javascript:<xxx>-compressor.
*
* @parameter default-value="jsmin"
*/
private String compressor;
/**
* Don't display compression stats
*
* @parameter
*/
private boolean skipStats;
private static final String[] DEFAULT_INCLUDES = new String[] { "**/*.js" };
/**
* Inclusion patterns
*
* @parameter
*/
private String[] includes;
/**
* Exclusion patterns
*
* @parameter
*/
private String[] excludes;
/**
* A special token to recognize lines to be removed from scripts (debugging
* code).
*
* @parameter
*/
private String strip;
/**
* {@inheritDoc}
*
* @see org.apache.maven.plugin.AbstractMojo#execute()
*/
public void execute()
throws MojoExecutionException
{
DirectoryScanner scanner = new DirectoryScanner();
scanner.setBasedir( getSourceDirectory() );
if ( includes == null )
{
includes = DEFAULT_INCLUDES;
}
scanner.setIncludes( includes );
scanner.addDefaultExcludes();
if ( excludes != null )
{
scanner.setExcludes( excludes );
}
scanner.scan();
String[] files = scanner.getIncludedFiles();
// if ( !Context.isValidOptimizationLevel( optimizationLevel ) )
// {
// throw new MojoExecutionException( "optimizationLevel is invalid" );
// }
// if ( !Context.isValidLanguageVersion( languageVersion ) )
// {
// throw new MojoExecutionException( "languageVersion is invalid" );
// }
JSCompressor jscompressor = getCompressor();
logStats( HR );
getOutputDirectory().mkdirs();
long saved = 0;
for ( int i = 0; i < files.length; i++ )
{
String file = files[i];
saved = compress( jscompressor, file );
}
logStats( HR );
logStats( "compression saved " + INTEGER.format( saved ) + " bytes" );
}
private File stripDebugs( File file )
throws MojoExecutionException
{
if ( strip == null )
{
return file;
}
try
{
File stripped = File.createTempFile( "stripped", ".js" );
BufferedReader reader = new BufferedReader( new FileReader( file ) );
PrintWriter writer = new PrintWriter( stripped );
String line;
while ( ( line = reader.readLine() ) != null )
{
if ( !line.trim().startsWith( strip ) )
{
writer.println( line );
}
}
IOUtil.close( reader );
IOUtil.close( writer );
FileUtils.copyFile( stripped, file );
stripped.delete();
}
catch ( IOException e )
{
throw new MojoExecutionException( "Failed to strip debug code in " + file, e );
}
return file;
}
private JSCompressor getCompressor()
throws MojoExecutionException
{
if ( compressors.containsKey( compressor ) )
{
return (JSCompressor) compressors.get( compressor );
}
// Inspired by the surefire plugin
// allows to use multiple compressor that rely on modifier Rhino engines
// without dependencies/classpath conflicts
//String id = compressor.toLowerCase() + "-compressor";
/*
String id = "myfaces-javascript-plugin";
// TODO don't have version hardcoded
Artifact compressorArtifact =
artifactFactory.createDependencyArtifact( "org.apache.myfaces.buildtools", id,
VersionRange.createFromVersion( "1.0.1-SNAPSHOT" ), "jar", null,
Artifact.SCOPE_RUNTIME );
Artifact originatingArtifact =
artifactFactory.createBuildArtifact( "dummy", "dummy", "1.0", "jar" );
ArtifactResolutionResult dependencies;
try
{
dependencies =
artifactResolver.resolveTransitively( Collections.singleton( compressorArtifact ),
originatingArtifact, localRepository, remoteRepositories, metadataSource, null );
}
catch ( Exception e )
{
getLog().info( "Failed to load compressor artifact " + compressorArtifact.toString() );
throw new MojoExecutionException( "Failed to load compressor artifact"
+ compressorArtifact.toString(), e );
}
IsolatedClassLoader classLoader = new IsolatedClassLoader( dependencies.getArtifacts() );
*/
compressor = StringUtils.capitalize( compressor );
String compressorClassName =
"org.apache.myfaces.buildtools.maven2.plugin.javascript.jmt.compress." + compressor + "Compressor";
Class compressorClass;
try
{
compressorClass = getClass().getClassLoader().loadClass( compressorClassName );
//compressorClass = classLoader.loadClass( compressorClassName );
}
catch ( ClassNotFoundException e )
{
getLog().info( "Failed to load compressor class " + compressorClassName );
throw new MojoExecutionException( "Failed to load compressor class"
+ compressorClassName, e );
}
JSCompressor jscompressor;
try
{
jscompressor = new JSCompressorProxy( compressorClass.newInstance() );
}
catch ( Exception e )
{
getLog().info(
"Failed to create a isolated-classloader proxy for " + compressorClassName );
throw new MojoExecutionException( "Failed to create a isolated-classloader proxy for "
+ compressorClassName, e );
}
getLog().info( "Compressing javascript using " + compressor );
compressors.put( compressor, jscompressor );
return jscompressor;
}
private long compress( JSCompressor jscompressor, String file )
throws MojoExecutionException
{
String name = file;
if ( getExtension() != null )
{
int ext = file.lastIndexOf( '.' );
name = file.substring( 0, ext ) + "-" + getExtension() + file.substring( ext );
}
File compressed = new File( getOutputDirectory(), name );
compressed.getParentFile().mkdirs();
File in = new File( getSourceDirectory(), file );
if ( in.equals( compressed ) )
{
try
{
File temp = File.createTempFile( "compress", ".js" );
long size = compress( in, temp, jscompressor );
FileUtils.copyFile( temp, compressed );
temp.delete();
return size;
}
catch ( IOException e )
{
throw new MojoExecutionException( "Error creating temp file for compression", e );
}
}
else
{
return compress( in, compressed, jscompressor );
}
}
private long compress( File in, File compressed, JSCompressor jscompressor )
throws MojoExecutionException
{
if ( in.length() > 0 )
{
File stripped = stripDebugs( in );
try
{
jscompressor.compress( stripped, compressed, optimizationLevel, languageVersion );
}
catch ( CompressionException e )
{
throw new MojoExecutionException( "Failed to compress Javascript file "
+ e.getScript(), e );
}
String describe = in.getName() + " (" + INTEGER.format( in.length() ) + " bytes) ";
String title = StringUtils.rightPad( describe, 60, "." );
logStats( title + " compressed at " + ratio( compressed, in ) + "%" );
return in.length() - compressed.length();
}
else
{
try
{
compressed.createNewFile();
getLog().info( in.getName() + " was zero length; not compressed." );
return 0;
}
catch ( IOException e )
{
throw new MojoExecutionException( "Error handling zero length file.", e );
}
}
}
private long ratio( File compressed, File in )
{
long length = in.length();
if ( length == 0 )
{
return 0;
}
return ( ( ( length - compressed.length() ) * 100 ) / length );
}
private void logStats( String line )
{
if ( skipStats )
{
return;
}
getLog().info( line );
}
/**
* @return the extension to append to compressed scripts.
*/
public abstract String getExtension();
/**
* @return the outputDirectory
*/
protected abstract File getOutputDirectory();
/**
* @return the sourceDirectory
*/
protected abstract File getSourceDirectory();
protected void setLocalRepository( ArtifactRepository localRepository )
{
this.localRepository = localRepository;
}
}