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

org.nuiton.eugene.plugin.SmartGenerateMojo Maven / Gradle / Ivy

There is a newer version: 2.4.2
Show newest version
/*
 * *##% 
 * 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 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 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