org.nuiton.eugene.plugin.SmartGenerateMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of maven-eugene-plugin Show documentation
Show all versions of maven-eugene-plugin Show documentation
maven plugin to use the eugene library
/*
* *##%
* EUGene :: Maven plugin
* Copyright (C) 2006 - 2009 CodeLutin
*
* 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
* .
* ##%*
*/
package org.nuiton.eugene.plugin;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Settings;
import org.nuiton.eugene.ModelReader;
import org.nuiton.eugene.Template;
import org.nuiton.eugene.models.Model;
import org.nuiton.eugene.plugin.writer.BaseChainedFileWriter;
import org.nuiton.eugene.plugin.writer.ModelChainedFileWriter;
import org.nuiton.eugene.plugin.writer.XmiChainedFileWriter;
import org.nuiton.eugene.writer.ChainedFileWriter;
import org.nuiton.eugene.writer.ChainedFileWriterConfiguration;
import org.nuiton.eugene.writer.ChainedFileWriterEntry;
import org.nuiton.eugene.writer.ChainedWriterEngine;
import org.nuiton.plugin.AbstractPlugin;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
/**
* Smart file generator.
*
* Fill inputs and mojo will chained needed writer.
*
* User: chemit
* Date: 24 nov. 2009
* Time: 00:22:37
*
* @goal smart-generate
* @requiresProject true
* @requiresDependencyResolution compile
*/
public class SmartGenerateMojo extends AbstractPlugin implements ChainedFileWriterConfiguration {
/**
* Inputs files to used to generate the required model files.
*
* An include has the following pattern :
*
* writer:
*
* when you want to use a specific writer with his default io values.
*
* Can also write :
*
* [writer:]directory:includes
*
* where {@code includes} is the pattern to find files from the directory given and must be terminated by the extension
* of files.
*
* Specifying the {@code writer} can be usefull when you want to use a writer for an unknown extension
* by any writer.
*
* Example :
*
* <inputs>
* <input>zargo:</input>
* <input>src/main/xmi2:**\/*.zargo</input>
* <input>zargo:src/main/xmi:**\/*.zargo2</input>
* </inputs>
*
*
* Note: If your using a single input, you can just write :
*
* <inputs>zargo</inputs>
*
*
* @parameter expression="${eugene.inputs}"
* @required
* @since 2.0.0
*/
protected String[] inputs;
/**
* List of input (protocol) not to treate separated by comma.
*
* Example :
*
* <skipInputs>xmi</skipInputs>
* <skipInputs>xmi,model</skipInputs>
*
*
* @parameter expression="${eugene.skipInputs}"
* @since 2.0.0
*/
protected String skipInputs;
/**
* Where to generate files.
*
* @parameter expression="${eugene.outputDirectory}" default-value="target/generated-sources"
* @required
* @since 2.0.0
*/
protected File outputDirectory;
/**
* Ecrase les fichiers générés.
*
* @parameter expression="${eugene.overwrite}" default-value="false"
* @since 2.0.0
*/
protected boolean overwrite;
/**
* Pour activer le mode verbeux.
*
* @parameter expression="${eugene.verbose}" default-value="${maven.verbose}"
* @since 2.0.0
*/
protected boolean verbose;
/**
* Encoding to be used for generation of files.
*
* @parameter expression="${eugene.encoding}" default-value="${project.build.sourceEncoding}"
* @since 2.0.0
*/
protected String encoding;
/**
* A flag to mark the mojo to be used in a test phase. This will permits to add generated sources in test compile roots.
*
* @parameter expression="${eugene.testPhase}" default-value="false"
* @since 2.0.0
*/
protected boolean testPhase;
/**
* The type of model to be used.
*
* By default, use an {@code objectmodel}.
*
* @parameter expression="${eugene.modelType}" default-value="objectmodel"
* @required
* @since 2.0.0
*/
protected String modelType;
// /**
// * An extra directory to be added to the classpath.
// *
// * @parameter expression="${eugene.extraClassPathDirectory}"
// * @since 2.0.0
// */
// protected File extraClassPathDirectory;
/**
* Properties to pass to writer.
*
* @parameter
* @since 2.0.0
*/
protected Map properties;
/**
* Ne génère rien, analyse juste la configuration.
*
* @parameter expression="${eugene.dryRun}" default-value="false"
* @since 2.0.0
*/
protected boolean dryRun;
/**
* Nom du paquetage pour les fichiers générés (xmi input sepcific).
*
* @parameter expression="${generator.fullPackagePath}" default-value="${project.groupId}.${project.artifactId}"
* @since 2.0.0
*/
protected String fullPackagePath;
// /**
// * Nom du paquetage à générer (xmi input sepcific).
// *
// * @parameter expression="${generator.extractedPackages}" default-value="${project.groupId}.${project.artifactId}"
// * @since 2.0.0
// */
// protected String extractedPackages;
/**
* Nom du resolver a utiliser pour les transformations xmi vers model (xmi input sepcific).
*
* @parameter expression="${generator.resolver}" default-value="org.nuiton.util.ResourceResolver"
* @since 2.0.0
*/
protected String resolver;
/**
* Templates à utiliser, séparés par des virgules pour les transformations depuis les models (model input sepcific).
*
* @parameter expression="${eugene.templates}"
* @since 0.50
*/
protected String templates;
/**
* Templates à ne pas utiliser lors de la transformations des models (model input sepcific).
*
* @parameter expression="${eugene.excludeTemplates}"
* @since 0.63
*/
protected String[] excludeTemplates;
/**
* Nom par défaut du paquetage généré (model input sepcific).
*
* @parameter expression="${eugene.defaultPackage}" default-value="${project.groupId}.${project.artifactId}"
* @since 0.50
*/
protected String defaultPackage;
/**
* List of packages to generate (comma separated). (model input sepcific).
*
* If the parameter is not filled, will generate all packages.
*
* @parameter expression="${eugene.generatedPackages}"
* @since 1.0.0-rc-8
*/
protected String generatedPackages;
/**
* Maven project.
*
* @parameter default-value="${project}"
* @readonly
* @since 2.0.0
*/
protected MavenProject project;
/**
* Le settings (pour obtenir le mode offline).
*
* @parameter default-value="${settings}"
* @readonly
* @since 2.0.0
*/
protected Settings settings;
/**
* All available models (obtain by plexus, keys are plexus roles, values are a
* instance of corresponding model).
*
* @component role="org.nuiton.eugene.models.Model"
*/
protected Map _models;
/**
* All available writers introspects via plexus
*
* @component role="org.nuiton.eugene.writer.ChainedFileWriter"
*/
protected Map writers;
/**
* All available writers introspects via plexus
*
* @component role="org.nuiton.eugene.ModelReader"
*/
protected Map> modelReaders;
/**
* All available templates introspects via plexus
*
* @component role="org.nuiton.eugene.Template"
*/
protected Map> modelTemplates;
/**
* The engine to compute {@link org.nuiton.eugene.writer.ChainedFileWriter} from inputs entries.
*
* @component role="org.nuiton.eugene.writer.ChainedWriterEngine"
*/
protected ChainedWriterEngine engine;
// /**
// * class instance of the given {@link #modelType}
// */
// protected Class extends Model> modelClass;
/**
* fixed classloader
*/
protected ClassLoader fixedClassLoader;
@Override
protected boolean init() throws Exception {
modelType = modelType.trim().toLowerCase();
// Check model type is accepted
// pouvoir associé un nom à un type de service).
Model model = _models.get(modelType);
if (model == null) {
throw new MojoExecutionException("No modelType named '" + modelType + "', use one of " + _models.keySet());
}
// modelClass = model.getClass();
if (inputs.length == 0) {
throw new MojoExecutionException("Must specify something to include using the includes property");
}
//FIXME-TC20091217 use a configurator in plexus ?
// Actually we obtain a different instance of the mojo conflit with mojo and plexus :)
engine.setConfiguration(this);
Set availableWriters = engine.getAvailableWriters();
if (availableWriters.isEmpty()) {
throw new MojoExecutionException("Could not find any writer in class-path.");
}
for (ChainedFileWriter writer : availableWriters) {
if (writer instanceof BaseChainedFileWriter) {
// add log support
((BaseChainedFileWriter) writer).setLog(getLog());
}
}
// detect top level writers
for (String include : inputs) {
getLog().info("Register include : " + include);
engine.registerInclude(include);
}
if (engine.getSelectedWriters().isEmpty()) {
getLog().warn("No phase was detected, skip the goal.");
return false;
}
if (properties == null) {
properties = new LinkedHashMap();
}
if (engine.containsWriter("xmi")) {
// add xmi writer support
properties.put(XmiChainedFileWriter.PROP_FULL_PACKAGE_PATH, fullPackagePath);
//properties.put("extractedPackages", extractedPackages);
properties.put(XmiChainedFileWriter.PROP_RESOLVER, resolver);
}
if (engine.containsWriter("model")) {
// add model writer support
properties.put(ModelChainedFileWriter.PROP_DEFAULT_PACKAGE, defaultPackage);
properties.put(ModelChainedFileWriter.PROP_GENERATED_PACKAGES, generatedPackages);
properties.put(ModelChainedFileWriter.PROP_TEMPLATES, templates);
properties.put(ModelChainedFileWriter.PROP_EXCLUDE_TEMPLATES, getExcludeTemplatesAsString());
}
return true;
}
@Override
protected void doAction() throws Exception {
if (dryRun) {
getLog().warn("dryRun property is set, no file will be generated.");
}
if (isVerbose()) {
if (isTestPhase()) {
getLog().info(" using testPhase");
}
}
try {
List skipInputList = new ArrayList();
if (!StringUtils.isEmpty(skipInputs)) {
for (String s : skipInputs.split(",")) {
skipInputList.add(s.trim());
}
}
// launch writers in incoming order of dicovering of them
for (ChainedFileWriter writer : engine.getSelectedWriters()) {
if (skipInputList.contains(writer.getInputProtocol())) {
getLog().info("Skip phase [" + writer.getInputProtocol() + "] as required in skipInputs configuration.");
continue;
}
int size = writer.getEntries().size();
if (size == 1) {
getLog().info("Process phase [" + writer.getInputProtocol() + "] for one entry.");
} else {
getLog().info("Process phase [" + writer.getInputProtocol() + "] for " + size + " entries.");
}
if (dryRun || isVerbose()) {
for (ChainedFileWriterEntry entry : writer.getEntries()) {
getLog().info(" entry : " + entry.getInputDirectory() + " - " + entry.getIncludePattern());
}
if (dryRun) {
continue;
}
}
if (getLog().isDebugEnabled()) {
getLog().debug("Generating files and copying resources...");
}
try {
writer.generate(this);
} catch (Exception e) {
throw new MojoExecutionException("could not generate for writer " + writer.getInputProtocol(), e);
}
if ("model".equals(writer.getInputProtocol())) {
// must fix source compile roots
File outputDir = writer.getOutputDirectory(getOutputDirectory(), isTestPhase());
fixCompileSourceRoots(outputDir);
}
}
} finally {
// always clear everything to avoid side-effects in goal is invoked more than once
properties.clear();
engine.clear();
}
}
/**
* Add a single input to the {@link #inputs} property.
*
* Note: This is a convinient way to allow in pom to write
*
* <inputs>zargo</inputs>
*
* in stead of array notation :
*
* <inputs>
* <input>zargo:</input>
* </inputs>
*
*
* @param inputs unique input to add
*/
public void setInputs(String inputs) {
this.inputs = new String[]{inputs};
}
@Override
public File getOutputDirectory() {
return outputDirectory;
}
@Override
public Map getProperties() {
return properties;
}
@Override
public ClassLoader getClassLoader() {
try {
return getFixedClassLoader();
} catch (MojoExecutionException e) {
throw new IllegalStateException("could not obtain classLoader",e);
}
}
@Override
public MavenProject getProject() {
return project;
}
@Override
public void setProject(MavenProject project) {
this.project = project;
}
@Override
public boolean isVerbose() {
return verbose;
}
@Override
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
@Override
public String getEncoding() {
return encoding;
}
@Override
public boolean isOverwrite() {
return overwrite;
}
@Override
public boolean isOffline() {
return settings.isOffline();
}
@Override
public boolean isTestPhase() {
return testPhase;
}
@Override
public String getModelType() {
return modelType;
}
// @Override
// public Class extends Model> getModelClass() {
// return modelClass;
// }
@Override
public Map getWriters() {
return writers;
}
@Override
public Map> getModelReaders() {
return modelReaders;
}
@Override
public Map> getModelTemplates() {
return modelTemplates;
}
@Override
public File getBasedir() {
return getProject().getBasedir();
}
/**
* @return the string representation of excludesTemplates (separated by comma)
*/
protected String getExcludeTemplatesAsString() {
String result = "";
for (int i = 0; i < excludeTemplates.length; i++) {
result += excludeTemplates[i];
if (i != excludeTemplates.length - 1) {
result += ",";
}
}
return result;
}
/**
* Prepare le classLoader a utiliser dans le generateur.
*
* Si le mojo est en phase de test {@link #testPhase} a été renseigné, target/classes est rajouté.
*
* Si des références à des sibling modules, ils seront rajoutés aussi.
*
* @return le class loader modifie
* @throws MojoExecutionException if any pb
*/
public ClassLoader getFixedClassLoader() throws MojoExecutionException {
if (fixedClassLoader == null) {
Set urlsAsString = new HashSet();
List urls = new ArrayList();
try {
ClassLoader loader;
if (testPhase) {
File extraClassPathDirectory = new File(getProject().getBuild().getOutputDirectory());
// if (verbose) {
getLog().info("Add in generator's classLoader : " + extraClassPathDirectory);
// }
addDirectoryToUrlsList(extraClassPathDirectory, urls, urlsAsString);
}
if (project.getProjectReferences() != null) {
// this case is for multi-module when calling from a parent module
for (Object o : project.getProjectReferences().entrySet()) {
Map.Entry, ?> entry = (Map.Entry, ?>) o;
MavenProject relatedProject = (MavenProject) entry.getValue();
if (verbose) {
getLog().info("Add project reference in generator's classLoader : '" + relatedProject.getArtifact() + "'");
}
//TODO il faudrait peut-etre aussi ajouter les dependances ?
addDirectoryToUrlsList(relatedProject.getArtifact().getFile(), urls, urlsAsString);
}
}
if (!project.getArtifacts().isEmpty()) {
// this is a special case when artifacts were resolved (for example in site phase)
if (verbose) {
getLog().info("Use resolved artifacts to build class-path");
}
for (Object o : project.getArtifacts()) {
Artifact a = (Artifact) o;
if (!a.getScope().equals("provided")) {
addDirectoryToUrlsList(a.getFile(), urls, urlsAsString);
}
}
}
// we ask to add the directory in classloader
loader = getClass().getClassLoader();
if (getLog().isDebugEnabled()) {
getLog().info("original classloader " + loader);
}
if (loader instanceof URLClassLoader) {
// on reinjecte les urls de loader de base
// car sinon on risque de ne pas retrouver les resources...
for (URL u : ((URLClassLoader) loader).getURLs()) {
addUrlToUrlsList(u, urls, urlsAsString);
if (getLog().isDebugEnabled()) {
getLog().debug("original cp entry: " + u);
}
}
// et on force l'utilisation du classloader parent
// s'il existe
if (loader.getParent() != null) {
loader = loader.getParent();
}
}
if (!urls.isEmpty()) {
loader = new URLClassLoader(urls.toArray(new URL[urls.size()]),
loader);
}
if (getLog().isDebugEnabled()) {
for (URL u : urls) {
getLog().debug("cp entry: " + u);
}
}
fixedClassLoader = loader;
} catch (MalformedURLException e) {
throw new MojoExecutionException(e.getMessage());
} finally {
urls.clear();
urlsAsString.clear();
}
}
return fixedClassLoader;
}
/**
* permet d'ajout le répertoire de génération des fichiers java dans les répertoires
* de compilation du projet Maven.
*
* @param destDirGen le repertoire a traiter
*/
protected void fixCompileSourceRoots(File destDirGen) {
//FIXME-TC20091215 : should never have a null project, this is not normal
if (project == null) {
// no project defined, can not fix anything
// this case could appears if we wanted to do some tests of the plugin
return;
}
//TODO-TC20091016 should use AbstractPlugin api
if (isTestPhase()) {
if (!project.getTestCompileSourceRoots().contains(destDirGen.getPath())) {
getLog().info("Add test compile source root : " + destDirGen);
project.addTestCompileSourceRoot(destDirGen.getPath());
Resource resources = new Resource();
resources.setDirectory(destDirGen.getAbsolutePath());
resources.setExcludes(Arrays.asList("**/*.java"));
getLog().info("Add test resource root :" + resources);
project.addTestResource(resources);
}
} else {
if (!project.getCompileSourceRoots().contains(destDirGen.getPath())) {
getLog().info("Add compile source root : " + destDirGen);
project.addCompileSourceRoot(destDirGen.getPath());
Resource resources = new Resource();
resources.setDirectory(destDirGen.getAbsolutePath());
resources.setExcludes(Arrays.asList("**/*.java"));
getLog().info("Add resource root :" + resources);
project.addResource(resources);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy