org.nuiton.plugin.AbstractPlugin Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of helper-maven-plugin-api Show documentation
Show all versions of helper-maven-plugin-api Show documentation
Simple maven mojo api for our projects
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