Java tutorial
package cn.wanghaomiao.maven.plugin.seimi.packaging; /* * 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. */ import java.io.File; import java.io.IOException; import cn.wanghaomiao.maven.plugin.seimi.util.PathSet; import org.apache.commons.io.input.XmlStreamReader; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.MojoExecutionException; import cn.wanghaomiao.maven.plugin.seimi.util.WebappStructure; import org.apache.maven.shared.filtering.MavenFilteringException; import org.apache.maven.shared.mapping.MappingUtils; import org.codehaus.plexus.archiver.ArchiverException; import org.codehaus.plexus.archiver.UnArchiver; import org.codehaus.plexus.archiver.jar.JarArchiver; import org.codehaus.plexus.archiver.manager.NoSuchArchiverException; import org.codehaus.plexus.interpolation.InterpolationException; import org.codehaus.plexus.util.DirectoryScanner; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.IOUtil; /** * @author Stephane Nicoll * @version $Id: AbstractWarPackagingTask.java 1637197 2014-11-06 19:46:57Z khmarbaise $ */ public abstract class AbstractWarPackagingTask implements WarPackagingTask { /** * The default list of includes. */ public static final String[] DEFAULT_INCLUDES = { "**/**" }; /** * The {@code WEB-INF} path. */ public static final String WEB_INF_PATH = "seimi"; /** * The {@code META-INF} path. */ public static final String META_INF_PATH = "seimi"; /** * The {@code classes} path. */ public static final String CLASSES_PATH = "seimi/classes/"; /** * The {@code lib} path. */ public static final String LIB_PATH = "seimi/lib/"; /** * Copies the files if possible with an optional target prefix. * <p/> * Copy uses a first-win strategy: files that have already been copied by previous tasks are ignored. This method * makes sure to update the list of protected files which gives the list of files that have already been copied. * <p/> * If the structure of the source directory is not the same as the root of the webapp, use the <tt>targetPrefix</tt> * parameter to specify in which particular directory the files should be copied. Use <tt>null</tt> to copy the * files with the same structure * * @param sourceId the source id * @param context the context to use * @param sourceBaseDir the base directory from which the <tt>sourceFilesSet</tt> will be copied * @param sourceFilesSet the files to be copied * @param targetPrefix the prefix to add to the target file name * @param filtered filter or not. * @throws IOException if an error occurred while copying the files * @throws MojoExecutionException if an error occurs. */ protected void copyFiles(String sourceId, WarPackagingContext context, File sourceBaseDir, PathSet sourceFilesSet, String targetPrefix, boolean filtered) throws IOException, MojoExecutionException { for (String fileToCopyName : sourceFilesSet.paths()) { final File sourceFile = new File(sourceBaseDir, fileToCopyName); String destinationFileName; if (targetPrefix == null) { destinationFileName = fileToCopyName; } else { destinationFileName = targetPrefix + fileToCopyName; } if (filtered && !context.isNonFilteredExtension(sourceFile.getName())) { copyFilteredFile(sourceId, context, sourceFile, destinationFileName); } else { copyFile(sourceId, context, sourceFile, destinationFileName); } } } /** * Copies the files if possible as is. * <p/> * Copy uses a first-win strategy: files that have already been copied by previous tasks are ignored. This method * makes sure to update the list of protected files which gives the list of files that have already been copied. * * @param sourceId the source id * @param context the context to use * @param sourceBaseDir the base directory from which the <tt>sourceFilesSet</tt> will be copied * @param sourceFilesSet the files to be copied * @param filtered filter or not. * @throws IOException if an error occurred while copying the files * @throws MojoExecutionException break the build. */ protected void copyFiles(String sourceId, WarPackagingContext context, File sourceBaseDir, PathSet sourceFilesSet, boolean filtered) throws IOException, MojoExecutionException { copyFiles(sourceId, context, sourceBaseDir, sourceFilesSet, null, filtered); } /** * Copy the specified file if the target location has not yet already been used. * <p/> * The <tt>targetFileName</tt> is the relative path according to the root of the generated web application. * * @param sourceId the source id * @param context the context to use * @param file the file to copy * @param targetFilename the relative path according to the root of the webapp * @throws IOException if an error occurred while copying */ // CHECKSTYLE_OFF: LineLength protected void copyFile(String sourceId, final WarPackagingContext context, final File file, String targetFilename) throws IOException // CHECKSTYLE_ON: LineLength { final File targetFile = new File(context.getWebappDirectory(), targetFilename); if (file.isFile()) { context.getWebappStructure().registerFile(sourceId, targetFilename, new WebappStructure.RegistrationCallback() { public void registered(String ownerId, String targetFilename) throws IOException { copyFile(context, file, targetFile, targetFilename, false); } public void alreadyRegistered(String ownerId, String targetFilename) throws IOException { copyFile(context, file, targetFile, targetFilename, true); } public void refused(String ownerId, String targetFilename, String actualOwnerId) throws IOException { context.getLog().debug(" - " + targetFilename + " wasn't copied because it has " + "already been packaged for overlay [" + actualOwnerId + "]."); } public void superseded(String ownerId, String targetFilename, String deprecatedOwnerId) throws IOException { context.getLog().info("File [" + targetFilename + "] belonged to overlay [" + deprecatedOwnerId + "] so it will be overwritten."); copyFile(context, file, targetFile, targetFilename, false); } public void supersededUnknownOwner(String ownerId, String targetFilename, String unknownOwnerId) throws IOException { // CHECKSTYLE_OFF: LineLength context.getLog().warn("File [" + targetFilename + "] belonged to overlay [" + unknownOwnerId + "] which does not exist anymore in the current project. It is recommended to invoke " + "clean if the dependencies of the project changed."); // CHECKSTYLE_ON: LineLength copyFile(context, file, targetFile, targetFilename, false); } }); } else if (!targetFile.exists() && !targetFile.mkdirs()) { context.getLog().info("Failed to create directory " + targetFile.getAbsolutePath()); } } /** * Copy the specified file if the target location has not yet already been used and filter its content with the * configured filter properties. * <p/> * The <tt>targetFileName</tt> is the relative path according to the root of the generated web application. * * @param sourceId the source id * @param context the context to use * @param file the file to copy * @param targetFilename the relative path according to the root of the webapp * @return true if the file has been copied, false otherwise * @throws IOException if an error occurred while copying * @throws MojoExecutionException if an error occurred while retrieving the filter properties */ protected boolean copyFilteredFile(String sourceId, final WarPackagingContext context, File file, String targetFilename) throws IOException, MojoExecutionException { if (context.getWebappStructure().registerFile(sourceId, targetFilename)) { final File targetFile = new File(context.getWebappDirectory(), targetFilename); final String encoding; try { if (isXmlFile(file)) { // For xml-files we extract the encoding from the files encoding = getEncoding(file); } else { // For all others we use the configured encoding encoding = context.getResourceEncoding(); } // fix for MWAR-36, ensures that the parent dir are created first targetFile.getParentFile().mkdirs(); context.getMavenFileFilter().copyFile(file, targetFile, true, context.getFilterWrappers(), encoding); } catch (MavenFilteringException e) { throw new MojoExecutionException(e.getMessage(), e); } // CHECKSTYLE_OFF: LineLength // Add the file to the protected list context.getLog() .debug(" + " + targetFilename + " has been copied (filtered encoding='" + encoding + "')."); // CHECKSTYLE_ON: LineLength return true; } else { context.getLog().debug( " - " + targetFilename + " wasn't copied because it has already been packaged (filtered)."); return false; } } /** * Unpacks the specified file to the specified directory. * * @param context the packaging context * @param file the file to unpack * @param unpackDirectory the directory to use for th unpacked file * @throws MojoExecutionException if an error occurred while unpacking the file */ protected void doUnpack(WarPackagingContext context, File file, File unpackDirectory) throws MojoExecutionException { String archiveExt = FileUtils.getExtension(file.getAbsolutePath()).toLowerCase(); try { UnArchiver unArchiver = context.getArchiverManager().getUnArchiver(archiveExt); unArchiver.setSourceFile(file); unArchiver.setUseJvmChmod(context.isUseJvmChmod()); unArchiver.setDestDirectory(unpackDirectory); unArchiver.setOverwrite(true); unArchiver.extract(); } catch (ArchiverException e) { throw new MojoExecutionException("Error unpacking file [" + file.getAbsolutePath() + "]" + "to [" + unpackDirectory.getAbsolutePath() + "]", e); } catch (NoSuchArchiverException e) { context.getLog().warn("Skip unpacking dependency file [" + file.getAbsolutePath() + " with unknown extension [" + archiveExt + "]"); } } /** * Copy file from source to destination. The directories up to <code>destination</code> will be created if they * don't already exist. if the <code>onlyIfModified</code> flag is <tt>false</tt>, <code>destination</code> will be * overwritten if it already exists. If the flag is <tt>true</tt> destination will be overwritten if it's not up to * date. * <p/> * * @param context the packaging context * @param source an existing non-directory <code>File</code> to copy bytes from * @param destination a non-directory <code>File</code> to write bytes to (possibly overwriting). * @param targetFilename the relative path of the file from the webapp root directory * @param onlyIfModified if true, copy the file only if the source has changed, always copy otherwise * @return true if the file has been copied/updated, false otherwise * @throws IOException if <code>source</code> does not exist, <code>destination</code> cannot be written to, or an * IO error occurs during copying */ protected boolean copyFile(WarPackagingContext context, File source, File destination, String targetFilename, boolean onlyIfModified) throws IOException { if (onlyIfModified && destination.lastModified() >= source.lastModified()) { context.getLog().debug(" * " + targetFilename + " is up to date."); return false; } else { if (source.isDirectory()) { context.getLog().warn(" + " + targetFilename + " is packaged from the source folder"); try { JarArchiver archiver = context.getJarArchiver(); archiver.addDirectory(source); archiver.setDestFile(destination); archiver.createArchive(); } catch (ArchiverException e) { String msg = "Failed to create " + targetFilename; context.getLog().error(msg, e); IOException ioe = new IOException(msg); ioe.initCause(e); throw ioe; } } else { FileUtils.copyFile(source.getCanonicalFile(), destination); // preserve timestamp destination.setLastModified(source.lastModified()); context.getLog().debug(" + " + targetFilename + " has been copied."); } return true; } } /** * Get the encoding from an XML-file. * * @param webXml the XML-file * @return The encoding of the XML-file, or UTF-8 if it's not specified in the file * @throws java.io.IOException if an error occurred while reading the file */ protected String getEncoding(File webXml) throws IOException { XmlStreamReader xmlReader = new XmlStreamReader(webXml); try { return xmlReader.getEncoding(); } finally { IOUtil.close(xmlReader); } } /** * Returns the file to copy. If the includes are <tt>null</tt> or empty, the default includes are used. * * @param baseDir the base directory to start from * @param includes the includes * @param excludes the excludes * @return the files to copy */ protected PathSet getFilesToIncludes(File baseDir, String[] includes, String[] excludes) { return getFilesToIncludes(baseDir, includes, excludes, false); } /** * Returns the file to copy. If the includes are <tt>null</tt> or empty, the default includes are used. * * @param baseDir the base directory to start from * @param includes the includes * @param excludes the excludes * @param includeDirectories include directories yes or not. * @return the files to copy */ // CHECKSTYLE_OFF: LineLength protected PathSet getFilesToIncludes(File baseDir, String[] includes, String[] excludes, boolean includeDirectories) // CHECKSTYLE_ON: LineLength { final DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir(baseDir); if (excludes != null) { scanner.setExcludes(excludes); } scanner.addDefaultExcludes(); if (includes != null && includes.length > 0) { scanner.setIncludes(includes); } else { scanner.setIncludes(DEFAULT_INCLUDES); } scanner.scan(); PathSet pathSet = new PathSet(scanner.getIncludedFiles()); if (includeDirectories) { pathSet.addAll(scanner.getIncludedDirectories()); } return pathSet; } /** * Returns the final name of the specified artifact. * <p/> * If the <tt>outputFileNameMapping</tt> is set, it is used, otherwise the standard naming scheme is used. * * @param context the packaging context * @param artifact the artifact * @return the converted filename of the artifact * @throws InterpolationException in case of interpolation problem. */ protected String getArtifactFinalName(WarPackagingContext context, Artifact artifact) throws InterpolationException { if (context.getOutputFileNameMapping() != null) { return MappingUtils.evaluateFileNameMapping(context.getOutputFileNameMapping(), artifact); } String classifier = artifact.getClassifier(); if ((classifier != null) && !("".equals(classifier.trim()))) { return MappingUtils.evaluateFileNameMapping(MappingUtils.DEFAULT_FILE_NAME_MAPPING_CLASSIFIER, artifact); } else { return MappingUtils.evaluateFileNameMapping(MappingUtils.DEFAULT_FILE_NAME_MAPPING, artifact); } } /** * Returns <code>true</code> if the <code>File</code>-object is a file (not a directory) that is not * <code>null</code> and has a file name that ends in ".xml". * * @param file The file to check * @return <code>true</code> if the file is an xml-file, otherwise <code>false</code> * @since 2.3 */ private boolean isXmlFile(File file) { return file != null && file.isFile() && file.getName().endsWith(".xml"); } }