All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.plugin.AbstractPlugin Maven / Gradle / Ivy

There is a newer version: 2.4.1
Show newest version
package org.nuiton.plugin;

/*
 * #%L
 * Helper Maven Plugin :: API
 * $Id: AbstractPlugin.java 906 2013-08-08 12:30:53Z tchemit $
 * $HeadURL: http://svn.nuiton.org/svn/maven-helper-plugin/tags/maven-helper-plugin-2.1/helper-maven-plugin-api/src/main/java/org/nuiton/plugin/AbstractPlugin.java $
 * %%
 * Copyright (C) 2009 - 2012 Codelutin, Tony Chemit
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */

import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.DirectoryScanner;
import org.nuiton.io.MirroredFileUpdater;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * Base mojo with usefull methods and implementing {@link Plugin} contract.
 *
 * @author tchemit 
 */
public abstract class AbstractPlugin extends AbstractMojo implements Plugin {

    /**
     * Method to initialize the mojo before doing any concrete actions.
     * 

* Note: The method is invoked before the {@link #doAction()} method. *

*

* * @throws Exception if any */ protected abstract void init() throws Exception; /** * Do plugin action. *

* The method {@link #execute()} invoke this method only and only if : *

    *
  • {@link #checkPackaging()} returns {@code true}.
  • *
  • method {@link #init()} returns {@code true}.
  • *
* * @throws Exception if any */ protected abstract void doAction() throws Exception; @Override public final void execute() throws MojoExecutionException, MojoFailureException { try { if (getLog().isDebugEnabled()) { // always be verbose in debug mode setVerbose(true); } // check if project packaging is compatible with the mojo boolean canContinue = checkPackaging(); if (!canContinue) { getLog().warn("The goal is skip due to packaging '" + getProject().getPackaging() + "'"); return; } // init the mojo try { checkEncoding(); init(); } catch (MojoFailureException e) { throw e; } catch (MojoExecutionException e) { throw e; } catch (Exception e) { throw new MojoExecutionException( "could not init goal " + getClass().getSimpleName() + " for reason : " + e.getMessage(), e); } // check if mojo can be skipped canContinue = checkSkip(); if (!canContinue) { if (isVerbose() || getLog().isDebugEnabled()) { getLog().info("Goal will not be executed."); } return; } // can really execute the mojo try { doAction(); } catch (MojoFailureException e) { throw e; } catch (MojoExecutionException e) { throw e; } catch (Exception e) { throw new MojoExecutionException( "could not execute goal " + getClass().getSimpleName() + " for reason : " + e.getMessage(), e); } } finally { afterExecute(); } } /** A call back to execute after the {@link #execute()} is done */ protected void afterExecute() { // by default do nothing } /** * Check if the project packaging is acceptable for the mojo. *

* By default, accept all packaging types. *

* Note: This method is the first instruction to be executed in * the {@link #execute()}. *

* Tip: There is two method to simplify the packaging check : *

* {@link #acceptPackaging(Plugin.Packaging...)} *

* and *

* {@link #rejectPackaging(Plugin.Packaging...)} * * @return {@code true} if can execute the goal for the packaging of the * project, {@code false} otherwise. */ protected boolean checkPackaging() { // by default, accept every type of packaging return true; } /** * Checks if the mojo execution should be skipped. * * @return {@code false} if the mojo should not be executed. */ protected boolean checkSkip() { // by default, never skip goal return true; } /** * Accept the project's packaging between some given. * * @param packages the accepted packaging * @return {@code true} if the project's packaging is one of the given ones. */ protected boolean acceptPackaging(Packaging... packages) { String projectPackaging = getProject().getPackaging(); for (Packaging p : packages) { if (p.name().equals(projectPackaging)) { // accept packaging return true; } } // reject packaging return false; } /** * Accept the project's packaging if not in given one. * * @param packages the rejecting packagings * @return {@code true} if the project's packaging is not in the given ones. */ protected boolean rejectPackaging(Packaging... packages) { String projectPackaging = getProject().getPackaging(); for (Packaging p : packages) { if (p.name().equals(projectPackaging)) { // reject this packaging return false; } } // accept packaging return true; } /** @return {@code true} if project is the root execution project */ protected boolean isExecutionRoot() { return getProject().isExecutionRoot(); } /** * Check if an execution was already done according to the given * parameters based on a timestamp. * * @param runOnce the flag * @param onlyForRoot a flag to * @param buildStartTime the build statrt time (if none means must do it) * @param newStartTime the current build start time * @return {@code true} if the goal was already invoked, {@code false} * otherwise (means should run it now!). * @deprecated since 2.1, prefer use now {@link #needInvoke(boolean, boolean, Date, Date)} */ @Deprecated protected boolean checkRunOnceDone(boolean runOnce, boolean onlyForRoot, Date buildStartTime, Date newStartTime) { if (!runOnce) { // will run each time return false; } if (onlyForRoot && !isExecutionRoot()) { // never do it for a child return true; } if (buildStartTime == null) { // no build start time, so must run return false; } if (newStartTime == null) { // must run return false; } if (isVerbose()) { getLog().info("build timestamp : " + buildStartTime); getLog().info("plugin timestamp : " + newStartTime); } if (newStartTime.getTime() > buildStartTime.getTime()) { // was already generated return true; } // must run return false; } /** * Check if an execution should be done or not according to the * given parameters. * * @param runOnce the flag * @param onlyForRoot a flag to * @param buildStartTime the build statrt time (if none means must do it) * @param newStartTime the current build start time * {@code true} if the goal should be invoked, {@code false} * otherwise. * @since 2.1 */ protected boolean needInvoke(boolean runOnce, boolean onlyForRoot, Date buildStartTime, Date newStartTime) { if (!runOnce) { // will run each time return true; } if (onlyForRoot && !isExecutionRoot()) { // never do it for a child return false; } if (buildStartTime == null) { // no build start time, so must run return true; } if (newStartTime == null) { // must run return true; } if (isVerbose()) { getLog().info("build timestamp : " + buildStartTime); getLog().info("plugin timestamp : " + newStartTime); } if (newStartTime.getTime() > buildStartTime.getTime()) { // was already generated return false; } // must run return true; } /** * Check if an execution should be done or not according to the * given parameters. *

* Will search if the given {@code key} is found in project, if not then * means that the plugin was not invoked. * * @param runOnce always return true if not set * @param onlyForRoot flag to only accept root project * @param key key to check over project (first time will put it in project properties) * @return {@code true} if the goal should be invoked, {@code false} * otherwise. * @since 2.1 */ protected boolean needInvoke(boolean runOnce, boolean onlyForRoot, String key) { if (!runOnce) { // will run each time return true; } if (onlyForRoot && !isExecutionRoot()) { // never do it for a child return false; } if (isVerbose()) { getLog().info("check if already done for key : " + key); } Object value = getProject().getProperties().get(key); if (value != null) { // ok was already done if (isVerbose()) { getLog().info("Goal was already executed (but asked to be run only once), will skip goal."); } return false; } long timestamp = System.nanoTime(); getProject().getProperties().put(key, timestamp + ""); if (isVerbose()) { getLog().info("Adding cache key " + key + " with timestamp " + timestamp); } return true; } /** * Recupere le fichier donnée à partir de son chemin relatif sur le basedir * du projet maven. * * @param paths les paths pour atteindre le fichier ou le répertoire * @return le fichier de la destination * @since 1.1.0 */ public File getFileFromBasedir(String... paths) { File result = getProject().getBasedir(); for (String path : paths) { result = new File(result, path); } return result; } /** * Copy a file to a given locationand logging. * * @param srcFile represents the file to copy. * @param destFile file name of destination file. * @throws IOException if could not copy file. */ public void copyFile(File srcFile, File destFile) throws IOException { getLog().info("Copying " + srcFile.getName() + " to " + destFile); PluginHelper.copy(srcFile, destFile); } /** * Write a {@code content} into the given destination file for the given * {@code encoding}. * * @param destFile location where to write the content * @param content content ot write in the file * @param encoding the enconding of the file * @throws IOException if any pb while writing the content into the file */ public void writeFile(File destFile, String content, String encoding) throws IOException { PluginHelper.writeString(destFile, content, encoding); } /** * Test if a file exists and is newer than the pom file. * * @param f the file to test * @return {@code true} if file exists and is newer than the pom file, * {@code false} otherwise. */ protected boolean isFileNewerThanPomFile(File f) { File pomFile = getProject().getFile(); return f.exists() && f.lastModified() > pomFile.lastModified(); } /** * Collects some file. * * @param includes includes * @param excludes excludes * @param roots root directories to treate * @param files cache of file detected indexed by their root directory * @param updater the updater used to detect changes (if none, will get all * the files) */ protected void getFilesToTreateForRoots( String[] includes, String[] excludes, List roots, Map files, MirroredFileUpdater updater) { DirectoryScanner ds = new DirectoryScanner(); ds.setIncludes(includes); if (excludes != null) { ds.setExcludes(excludes); } for (String src : roots) { File f = new File(src); if (!f.exists()) { // do nothing on a non-existent continue; } if (getLog().isDebugEnabled()) { getLog().debug("discovering source files in " + src); } ds.setBasedir(f); // scan ds.scan(); // get files String[] tmp = ds.getIncludedFiles(); if (tmp.length < 1) { // no files found continue; } List toTreate = new ArrayList(); if (updater != null) { updater.setSourceDirectory(f); } for (String filePath : tmp) { File srcFile = new File(f, filePath); // check file is up-to-date if (updater == null || !updater.isFileUpToDate(srcFile)) { toTreate.add(filePath); } } if (toTreate.isEmpty()) { // no file or all are up-to-date continue; } // register files files.put(f, toTreate.toArray(new String[toTreate.size()])); } } /** * Collect to some files with a mirror. * * @param includes includes * @param excludes excludes * @param srcDir the directory to treate * @param updater an updater (will give the mirrored files) * @return the map of mirrored files associated to their files in srcDir */ protected Map getFilesToTreate( String[] includes, String[] excludes, File srcDir, MirroredFileUpdater updater) { Map result = new TreeMap(); DirectoryScanner ds = new DirectoryScanner(); ds.setIncludes(includes); if (excludes != null) { ds.setExcludes(excludes); } if (!srcDir.exists()) { // do nothing on a non-existent return result; } if (isVerbose()) { getLog().info("discovering files for " + srcDir); } ds.setBasedir(srcDir); // scan ds.scan(); // get files String[] tmp = ds.getIncludedFiles(); if (tmp.length < 1) { // no files found return result; } List toTreate = new ArrayList(); if (updater != null) { updater.setSourceDirectory(srcDir); } for (String filePath : tmp) { File srcFile = new File(srcDir, filePath); File mirrorFile = updater.getMirrorFile(srcFile); // check file is up-to-date if (updater == null || !updater.isFileUpToDate(srcFile)) { result.put(filePath, mirrorFile.getAbsolutePath()); toTreate.add(filePath); } } if (toTreate.isEmpty()) { // no file or all are up-to-date return result; } // register files return result; } /** * Add a given directory in maven project's compile source roots (if not * already present). * * @param srcDir the location to include in compile source roots */ protected void addCompileSourceRoots(File srcDir) { if (!getProject().getCompileSourceRoots().contains(srcDir.getPath())) { if (isVerbose()) { getLog().info("adding source roots : " + srcDir.getPath()); } getProject().addCompileSourceRoot(srcDir.getPath()); } } /** * Remove a given directory in maven project's compile source roots (if * present). * * @param srcDir the location to remove from compile source roots */ protected void removeCompileSourceRoots(File srcDir) { if (getProject().getCompileSourceRoots().contains(srcDir.getPath())) { if (isVerbose()) { getLog().info("removing source roots : " + srcDir.getPath()); } getProject().getCompileSourceRoots().remove(srcDir.getPath()); } } /** * Add a given directory in maven project's test compile source roots * (if not already present). * * @param srcDir the location to include in test compile source roots */ protected void addTestCompileSourceRoots(File srcDir) { if (!getProject().getTestCompileSourceRoots().contains( srcDir.getPath())) { if (isVerbose()) { getLog().info("adding test source roots : " + srcDir.getPath()); } getProject().addTestCompileSourceRoot(srcDir.getPath()); } } /** * Remove a given directory in maven project's test compile source roots * (if present). * * @param srcDir the location to remove from test compile source roots */ protected void removeTestCompileSourceRoots(File srcDir) { if (getProject().getTestCompileSourceRoots().contains( srcDir.getPath())) { if (isVerbose()) { getLog().info("removing test source roots : " + srcDir.getPath()); } getProject().getTestCompileSourceRoots().remove(srcDir.getPath()); } } /** * Add a new resource location to the maven project * (in not already present). * * @param dir the new resource location to add * @param includes files to include * @since 1.1.1 */ protected void addResourceDir(File dir, String... includes) { boolean added = PluginHelper.addResourceDir(dir, getProject(), includes); if (added && isVerbose()) { getLog().info("add resource " + dir + " with includes " + Arrays.toString(includes)); } } /** * Add a new test resource location to the maven project * (in not already present). * * @param dir the new resource location to add * @param includes files to include * @since 1.1.1 */ protected void addTestResourceDir(File dir, String... includes) { boolean added = PluginHelper.addTestResourceDir( dir, getProject(), includes); if (added && isVerbose()) { getLog().info("add test resource " + dir + " with includes " + Arrays.toString(includes)); } } /** * Create the directory if necessary. * * @param dir the directory to create if not already existing * @throws IOException if could not create the directory * @since 1.1.1 */ protected void createDirectoryIfNecessary(File dir) throws IOException { boolean b = PluginHelper.createDirectoryIfNecessary(dir); if (b && isVerbose()) { getLog().info("mkdir " + dir); } } /** * Create a new file. * * @param file the file to create * @throws IOException if could not create the file * @since 1.1.1 */ protected void createNewFile(File file) throws IOException { PluginHelper.createNewFile(file); } /** * Delete the given file. * * @param file the file to delete * @throws IOException if could not delete the file * @since 1.1.1 */ protected void deleteFile(File file) throws IOException { PluginHelper.deleteFile(file); } /** * Rename the given file to the destination one. * * @param file the file to rename * @param destination the destination of the file * @throws IOException if could not delete the file * @since 1.2.0 */ protected void renameFile(File file, File destination) throws IOException { PluginHelper.renameFile(file, destination); } /** * @param file the source file * @return the backup file */ public File getBackupFile(File file) { return new File(file.getAbsolutePath() + "~"); } /** * Backups the given file using the {@link #getBackupFile(File)} as * destination file. * * @param f the file to backup * @throws IOException if any pb while copying the file */ protected void backupFile(File f) throws IOException { File dst = getBackupFile(f); copyFile(f, dst); } /** * Init mojo classLoader. * * @param project the maven project * @param src the source directory * @param addSourcesToClassPath a flag to a maven sources to classLoader * @param testPhase a flag to specify if we are in a * test phase (changes the classLoader) * @param addResourcesToClassPath flag to add maven's resources to * classLoader * @param addCompileClassPath flag to add maven's project compile * classPath to classLoader * @param addProjectClassPath flag to add maven'es project dependecies * to classLoader * @return the new classLoader * @throws MalformedURLException if an url was not correct */ @SuppressWarnings({"unchecked"}) protected URLClassLoader initClassLoader( MavenProject project, File src, boolean addSourcesToClassPath, boolean testPhase, boolean addResourcesToClassPath, boolean addCompileClassPath, boolean addProjectClassPath) throws MalformedURLException { URLClassLoader loader; if (project != null) { URLClassLoader result; try { Set dones = new HashSet(); List lUrls = new ArrayList(); List sources = project.getCompileSourceRoots(); if (addSourcesToClassPath) { for (String source : sources) { addDirectoryToUrlsList(new File(source), lUrls, dones); } if (testPhase) { for (Object source : project.getTestCompileSourceRoots()) { addDirectoryToUrlsList( new File(source.toString()), lUrls, dones); } } } if (addResourcesToClassPath) { for (Object source : project.getResources()) { Resource r = (Resource) source; addDirectoryToUrlsList( new File(r.getDirectory()), lUrls, dones); } } if (testPhase && addCompileClassPath) { addDirectoryToUrlsList( new File(project.getBuild().getOutputDirectory()), lUrls, dones); } if (src != null) { addDirectoryToUrlsList(src, lUrls, dones); } if (addProjectClassPath) { getLog().info("use project compile scope class-path"); // add also all dependencies of the project in compile // scope Set artifacts = project.getArtifacts(); for (Object o : artifacts) { Artifact a = (Artifact) o; addDirectoryToUrlsList(a.getFile(), lUrls, dones); } } result = new URLClassLoader( lUrls.toArray(new URL[lUrls.size()]), getClass().getClassLoader() ); } catch (IOException e) { throw new RuntimeException( "Can't create ClassLoader for reason " + e.getMessage(), e); } loader = result; } else { List lUrls = new ArrayList(); if (addSourcesToClassPath) { lUrls.add(src.toURI().toURL()); } loader = new URLClassLoader(lUrls.toArray(new URL[lUrls.size()]), getClass().getClassLoader()); } if (isVerbose()) { for (URL entry : loader.getURLs()) { getLog().info("classpath : " + entry); } } return loader; } /** * Add the given {@code directory} in {@code urls} if not already included. *

* Note: We use a extra list to store file string representation, * since we do NOT want any url resolution and the * {@link URL#equals(Object)} is doing some... * * @param directory the directory to insert in {@code urls} * @param urls list of urls * @param done list of string representation of urls * @throws MalformedURLException if pb while converting file to url * @since 1.1.0 */ protected void addDirectoryToUrlsList( File directory, List urls, Set done) throws MalformedURLException { // do the comparaison on a String to avoid url to be resolved // (in URL.equals method) addUrlToUrlsList(directory.toURI().toURL(), urls, done); } /** * Add the given {@code url} in {@code urls} if not already included. *

* Note: We use a extra list to store file string representation, * since we do NOT want any url resolution and the * {@link URL#equals(Object)} is doing some.. * * @param url the url to insert in {@code urls} * @param urls list of urls * @param done list of string representation of urls * @since 1.1.0 */ protected void addUrlToUrlsList(URL url, List urls, Set done) { // do the comparaison on a String to avoid url to be resolved // (in URL.equals method) String u = url.toString(); if (!done.contains(u)) { done.add(u); urls.add(url); } } /** * Obtain the url of a file, if file does not exist, try in the classPath. * * @param f the required resource file. * @return the url of the resource * @throws IOException for any error while looking up for the url of * the resources */ protected URL getTemplate(File f) throws IOException { URL r; if (f.exists()) { r = f.toURI().toURL(); } else { r = getClass().getResource(f.toString()); } return r; } /** * Check that the given resource exists in a simple fs file or in the * classPath. * * @param f the required resource file. * @throws IOException for any error while looking up for the resources * content. */ protected void checkResource(File f) throws IOException { if (!f.exists()) { // test in classPath InputStream r = null; try { r = getClass().getResourceAsStream(f.toString()); if (r == null) { throw new IOException("could not find ressource " + f); } } finally { if (r != null) { r.close(); } } } } protected MavenProject getRootProject(MavenProject project, List reactors) { if (project.isExecutionRoot() || reactors.size() == 1 || reactors.get(0).equals(project)) { getLog().debug("project " + project + " is root execution :)"); return project; } MavenProject root = (MavenProject) reactors.get(0); getLog().debug("first project in reactor : " + root); return root; } /** * Method to be invoked in init phase to check sanity of {@link PluginWithEncoding#getEncoding()}. *

* If no encoding was filled, then use the default for system * (via {@code file.encoding} environement property). *

* Note: If mojo is not implementing {@link PluginWithEncoding}, * nothing is done. * * @since 1.26 */ protected void checkEncoding() { if (PluginWithEncoding.class.isAssignableFrom(getClass())) { if (isVerbose()) { getLog().info("Will check encoding : " + ((PluginWithEncoding) this).getEncoding()); } PluginHelper.checkEncoding((PluginWithEncoding) this); } } /** * @return {@code true} if project is not a pom, {@code false} otherwise. * @since 1.2.6 */ protected boolean hasClassPath() { return rejectPackaging(Packaging.pom); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy