All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.samaxes.maven.minify.plugin.ProcessFilesTask Maven / Gradle / Ivy
Go to download
Combine and minimize JavaScript and CSS files for faster page loading.
/*
* Minify Maven Plugin
* https://github.com/samaxes/minify-maven-plugin
*
* Copyright (c) 2009 samaxes.com
*
* 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 com.samaxes.maven.minify.plugin;
import com.samaxes.maven.minify.common.SourceFilesEnumeration;
import com.samaxes.maven.minify.common.YuiConfig;
import com.samaxes.maven.minify.plugin.MinifyMojo.Engine;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.zip.GZIPOutputStream;
/**
* Abstract class for merging and compressing a files list.
*/
public abstract class ProcessFilesTask implements Callable {
public static final String TEMP_SUFFIX = ".tmp";
protected final Log log;
protected final boolean verbose;
protected final Integer bufferSize;
protected final Charset charset;
protected final String suffix;
protected final boolean nosuffix;
protected final boolean skipMerge;
protected final boolean skipMinify;
protected final Engine engine;
protected final YuiConfig yuiConfig;
private final File sourceDir;
private final File targetDir;
private final String mergedFilename;
private final List files = new ArrayList<>();
private final boolean sourceFilesEmpty;
private final boolean sourceIncludesEmpty;
/**
* Task constructor.
*
* @param log Maven plugin log
* @param verbose display additional info
* @param bufferSize size of the buffer used to read source files
* @param charset if a character set is specified, a byte-to-char variant allows the encoding to be selected.
* Otherwise, only byte-to-byte operations are used
* @param suffix final file name suffix
* @param nosuffix whether to use a suffix for the minified file name or not
* @param skipMerge whether to skip the merge step or not
* @param skipMinify whether to skip the minify step or not
* @param webappSourceDir web resources source directory
* @param webappTargetDir web resources target directory
* @param inputDir directory containing source files
* @param sourceFiles list of source files to include
* @param sourceIncludes list of source files to include
* @param sourceExcludes list of source files to exclude
* @param outputDir directory to write the final file
* @param outputFilename the output file name
* @param engine minify processor engine selected
* @param yuiConfig YUI Compressor configuration
* @throws FileNotFoundException when the given source file does not exist
*/
public ProcessFilesTask(Log log, boolean verbose, Integer bufferSize, Charset charset, String suffix,
boolean nosuffix, boolean skipMerge, boolean skipMinify, String webappSourceDir,
String webappTargetDir, String inputDir, List sourceFiles,
List sourceIncludes, List sourceExcludes, String outputDir,
String outputFilename, Engine engine, YuiConfig yuiConfig) throws FileNotFoundException {
this.log = log;
this.verbose = verbose;
this.bufferSize = bufferSize;
this.charset = charset;
this.suffix = suffix;
this.nosuffix = nosuffix;
this.skipMerge = skipMerge;
this.skipMinify = skipMinify;
this.engine = engine;
this.yuiConfig = yuiConfig;
this.sourceDir = new File(webappSourceDir + File.separator + inputDir);
this.targetDir = new File(webappTargetDir + File.separator + outputDir);
this.mergedFilename = outputFilename;
for (String sourceFilename : sourceFiles) {
addNewSourceFile(mergedFilename, sourceFilename);
}
for (File sourceInclude : getFilesToInclude(sourceIncludes, sourceExcludes)) {
if (!files.contains(sourceInclude)) {
addNewSourceFile(mergedFilename, sourceInclude);
}
}
this.sourceFilesEmpty = sourceFiles.isEmpty();
this.sourceIncludesEmpty = sourceIncludes.isEmpty();
}
/**
* Method executed by the thread.
*
* @throws IOException when the merge or minify steps fail
*/
@Override
public Object call() throws IOException {
synchronized (log) {
String fileType = (this instanceof ProcessCSSFilesTask) ? "CSS" : "JavaScript";
log.info("Starting " + fileType + " task:");
if (!targetDir.exists() && !targetDir.mkdirs()) {
throw new RuntimeException("Unable to create target directory for: " + targetDir);
}
if (!files.isEmpty()) {
if (skipMerge) {
log.info("Skipping the merge step...");
String sourceBasePath = sourceDir.getAbsolutePath();
for (File mergedFile : files) {
// Create folders to preserve sub-directory structure when only minifying
String originalPath = mergedFile.getAbsolutePath();
String subPath = originalPath.substring(sourceBasePath.length(),
originalPath.lastIndexOf(File.separator));
File targetPath = new File(targetDir.getAbsolutePath() + subPath);
if (!targetPath.exists() && !targetPath.mkdirs()) {
throw new RuntimeException("Unable to create target directory for: " + targetPath);
}
File minifiedFile = new File(targetPath, (nosuffix) ? mergedFile.getName()
: FileUtils.removeExtension(mergedFile.getName()) + suffix + "." + FileUtils.extension(mergedFile.getName()));
minify(mergedFile, minifiedFile);
}
} else if (skipMinify) {
File mergedFile = new File(targetDir, mergedFilename);
merge(mergedFile);
log.info("Skipping the minify step...");
} else {
File mergedFile = new File(targetDir, (nosuffix) ? mergedFilename + TEMP_SUFFIX : mergedFilename);
merge(mergedFile);
File minifiedFile = new File(targetDir, (nosuffix) ? mergedFilename
: FileUtils.removeExtension(mergedFilename) + suffix + "." + FileUtils.extension(mergedFilename));
minify(mergedFile, minifiedFile);
if (nosuffix) {
if (!mergedFile.delete()) {
mergedFile.deleteOnExit();
}
}
}
log.info("");
} else if (!sourceFilesEmpty || !sourceIncludesEmpty) {
// 'files' list will be empty if source file paths or names added to the project's POM are invalid.
log.error("No valid " + fileType + " source files found to process.");
}
}
return null;
}
/**
* Merges a list of source files. Create missing parent directories if needed.
*
* @param mergedFile output file resulting from the merged step
* @throws IOException when the merge step fails
*/
protected void merge(File mergedFile) throws IOException {
if (!mergedFile.getParentFile().exists() && !mergedFile.getParentFile().mkdirs()) {
throw new RuntimeException("Unable to create target directory for: " + mergedFile.getParentFile());
}
try (InputStream sequence = new SequenceInputStream(new SourceFilesEnumeration(log, files, verbose));
OutputStream out = new FileOutputStream(mergedFile);
InputStreamReader sequenceReader = new InputStreamReader(sequence, charset);
OutputStreamWriter outWriter = new OutputStreamWriter(out, charset)) {
log.info("Creating the merged file [" + (verbose ? mergedFile.getPath() : mergedFile.getName()) + "].");
IOUtil.copy(sequenceReader, outWriter, bufferSize);
} catch (IOException e) {
log.error("Failed to concatenate files.", e);
throw e;
}
}
/**
* Minifies a source file. Create missing parent directories if needed.
*
* @param mergedFile input file resulting from the merged step
* @param minifiedFile output file resulting from the minify step
* @throws IOException when the minify step fails
*/
abstract void minify(File mergedFile, File minifiedFile) throws IOException;
/**
* Logs compression gains.
*
* @param mergedFile input file resulting from the merged step
* @param minifiedFile output file resulting from the minify step
*/
void logCompressionGains(File mergedFile, File minifiedFile) {
try {
File temp = File.createTempFile(minifiedFile.getName(), ".gz");
try (InputStream in = new FileInputStream(minifiedFile);
OutputStream out = new FileOutputStream(temp);
GZIPOutputStream outGZIP = new GZIPOutputStream(out)) {
IOUtil.copy(in, outGZIP, bufferSize);
}
log.info("Uncompressed size: " + mergedFile.length() + " bytes.");
log.info("Compressed size: " + minifiedFile.length() + " bytes minified (" + temp.length()
+ " bytes gzipped).");
temp.deleteOnExit();
} catch (IOException e) {
log.debug("Failed to calculate the gzipped file size.", e);
}
}
/**
* Logs an addition of a new source file.
*
* @param finalFilename the final file name
* @param sourceFilename the source file name
* @throws FileNotFoundException when the given source file does not exist
*/
private void addNewSourceFile(String finalFilename, String sourceFilename) throws FileNotFoundException {
File sourceFile = new File(sourceDir, sourceFilename);
addNewSourceFile(finalFilename, sourceFile);
}
/**
* Logs an addition of a new source file.
*
* @param finalFilename the final file name
* @param sourceFile the source file
* @throws FileNotFoundException when the given source file does not exist
*/
private void addNewSourceFile(String finalFilename, File sourceFile) throws FileNotFoundException {
if (sourceFile.exists()) {
if (finalFilename.equalsIgnoreCase(sourceFile.getName())) {
log.warn("The source file [" + (verbose ? sourceFile.getPath() : sourceFile.getName())
+ "] has the same name as the final file.");
}
log.debug("Adding source file [" + (verbose ? sourceFile.getPath() : sourceFile.getName()) + "].");
files.add(sourceFile);
} else {
throw new FileNotFoundException("The source file ["
+ (verbose ? sourceFile.getPath() : sourceFile.getName()) + "] does not exist.");
}
}
/**
* Returns the files to copy. Default exclusions are used when the excludes list is empty.
*
* @param includes list of source files to include
* @param excludes list of source files to exclude
* @return the files to copy
*/
private List getFilesToInclude(List includes, List excludes) {
List includedFiles = new ArrayList<>();
if (includes != null && !includes.isEmpty()) {
DirectoryScanner scanner = new DirectoryScanner();
scanner.setIncludes(includes.toArray(new String[includes.size()]));
scanner.setExcludes(excludes.toArray(new String[excludes.size()]));
scanner.addDefaultExcludes();
scanner.setBasedir(sourceDir);
scanner.scan();
for (String includedFilename : scanner.getIncludedFiles()) {
includedFiles.add(new File(sourceDir, includedFilename));
}
Collections.sort(includedFiles, new Comparator() {
@Override
public int compare(File o1, File o2) {
return o1.getName().compareToIgnoreCase(o2.getName());
}
});
}
return includedFiles;
}
}