1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.maven.plugin;
7   
8   import gov.nist.secauto.metaschema.core.model.IModule;
9   import gov.nist.secauto.metaschema.core.model.MetaschemaException;
10  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
11  import gov.nist.secauto.metaschema.databind.IBindingContext;
12  import gov.nist.secauto.metaschema.databind.codegen.JavaGenerator;
13  import gov.nist.secauto.metaschema.databind.codegen.config.DefaultBindingConfiguration;
14  
15  import org.apache.maven.plugin.MojoExecutionException;
16  import org.apache.maven.plugins.annotations.LifecyclePhase;
17  import org.apache.maven.plugins.annotations.Mojo;
18  import org.apache.maven.plugins.annotations.Parameter;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.Set;
26  
27  import edu.umd.cs.findbugs.annotations.NonNull;
28  
29  /**
30   * Goal which generates Java source files for a given set of Metaschema modules.
31   */
32  @Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
33  public class GenerateSourcesMojo
34      extends AbstractMetaschemaMojo {
35    private static final String STALE_FILE_NAME = "generateSourcesStaleFile";
36  
37    /**
38     * A set of binding configurations.
39     */
40    @Parameter
41    protected File[] configs;
42  
43    /**
44     * <p>
45     * Gets the last part of the stale filename.
46     * </p>
47     * <p>
48     * The full stale filename will be generated by pre-pending
49     * {@code "." + getExecution().getExecutionId()} to this staleFileName.
50     *
51     * @return the stale filename postfix
52     */
53    @Override
54    protected String getStaleFileName() {
55      return STALE_FILE_NAME;
56    }
57  
58    /**
59     * Retrieve a list of binding configurations.
60     *
61     * @return the collection of binding configurations
62     */
63    protected List<File> getConfigs() {
64      List<File> retval;
65      if (configs == null) {
66        retval = Collections.emptyList();
67      } else {
68        retval = Arrays.asList(configs);
69      }
70      return retval;
71    }
72  
73    /**
74     * Generate the Java source files for the provided Metaschemas.
75     *
76     * @param modules
77     *          the collection of Metaschema modules to generate sources for
78     * @throws MojoExecutionException
79     *           if an error occurred while generating sources
80     */
81    protected void generate(@NonNull Set<IModule> modules) throws MojoExecutionException {
82      DefaultBindingConfiguration bindingConfiguration = new DefaultBindingConfiguration();
83      for (File config : getConfigs()) {
84        try {
85          if (getLog().isInfoEnabled()) {
86            getLog().info("Loading binding configuration: " + config.getPath());
87          }
88          bindingConfiguration.load(config);
89        } catch (IOException ex) {
90          throw new MojoExecutionException(
91              String.format("Unable to load binding configuration from '%s'.", config.getPath()), ex);
92        }
93      }
94  
95      try {
96        if (getLog().isInfoEnabled()) {
97          getLog().info("Generating Java classes in: " + getOutputDirectory().getPath());
98        }
99        JavaGenerator.generate(modules, ObjectUtils.notNull(getOutputDirectory().toPath()),
100           bindingConfiguration);
101     } catch (IOException ex) {
102       throw new MojoExecutionException("Creation of Java classes failed.", ex);
103     }
104   }
105 
106   @SuppressWarnings("PMD.AvoidCatchingGenericException")
107   @Override
108   public void execute() throws MojoExecutionException {
109     File staleFile = getStaleFile();
110     try {
111       staleFile = ObjectUtils.notNull(staleFile.getCanonicalFile());
112     } catch (IOException ex) {
113       if (getLog().isWarnEnabled()) {
114         getLog().warn("Unable to resolve canonical path to stale file. Treating it as not existing.", ex);
115       }
116     }
117 
118     boolean generate;
119     if (shouldExecutionBeSkipped()) {
120       if (getLog().isDebugEnabled()) {
121         getLog().debug(String.format("Source file generation is configured to be skipped. Skipping."));
122       }
123       generate = false;
124     } else if (staleFile.exists()) {
125       generate = isGenerationRequired();
126     } else {
127       if (getLog().isInfoEnabled()) {
128         getLog().info(String.format("Stale file '%s' doesn't exist! Generating source files.", staleFile.getPath()));
129       }
130       generate = true;
131     }
132 
133     if (generate) {
134       performGeneration();
135       createStaleFile(staleFile);
136 
137       // for m2e
138       getBuildContext().refresh(getOutputDirectory());
139     }
140 
141     // add generated sources to Maven
142     try {
143       getMavenProject().addCompileSourceRoot(getOutputDirectory().getCanonicalFile().getPath());
144     } catch (IOException ex) {
145       throw new MojoExecutionException("Unable to add output directory to maven sources.", ex);
146     }
147   }
148 
149   @SuppressWarnings("PMD.AvoidCatchingGenericException")
150   private void performGeneration() throws MojoExecutionException {
151     File outputDir = getOutputDirectory();
152     if (getLog().isDebugEnabled()) {
153       getLog().debug(String.format("Using outputDirectory: %s", outputDir.getPath()));
154     }
155 
156     if (!outputDir.exists() && !outputDir.mkdirs()) {
157       throw new MojoExecutionException("Unable to create output directory: " + outputDir);
158     }
159 
160     IBindingContext bindingContext;
161     try {
162       bindingContext = newBindingContext();
163     } catch (MetaschemaException | IOException ex) {
164       throw new MojoExecutionException("Failed to create the binding context", ex);
165     }
166 
167     // generate Java sources based on provided metaschema sources
168     Set<IModule> modules;
169     try {
170       modules = getModulesToGenerateFor(bindingContext);
171     } catch (Exception ex) {
172       throw new MojoExecutionException("Loading of metaschema modules failed", ex);
173     }
174 
175     generate(modules);
176   }
177 }