View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.metaschema.maven.plugin;
28  
29  import gov.nist.secauto.metaschema.core.model.IModule;
30  import gov.nist.secauto.metaschema.core.model.MetaschemaException;
31  import gov.nist.secauto.metaschema.core.model.xml.ModuleLoader;
32  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
33  import gov.nist.secauto.metaschema.databind.codegen.JavaGenerator;
34  import gov.nist.secauto.metaschema.databind.codegen.config.DefaultBindingConfiguration;
35  
36  import org.apache.maven.plugin.MojoExecutionException;
37  import org.apache.maven.plugins.annotations.LifecyclePhase;
38  import org.apache.maven.plugins.annotations.Mojo;
39  import org.apache.maven.plugins.annotations.Parameter;
40  
41  import java.io.File;
42  import java.io.IOException;
43  import java.io.OutputStream;
44  import java.nio.file.Files;
45  import java.nio.file.StandardOpenOption;
46  import java.util.Arrays;
47  import java.util.Collections;
48  import java.util.HashSet;
49  import java.util.List;
50  import java.util.Set;
51  import java.util.stream.Collectors;
52  
53  import edu.umd.cs.findbugs.annotations.NonNull;
54  
55  /**
56   * Goal which generates Java source files for a given set of Module definitions.
57   */
58  @Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
59  public class GenerateSourcesMojo
60      extends AbstractMetaschemaMojo {
61    private static final String STALE_FILE_NAME = "generateSourcesStaleFile";
62  
63    /**
64     * A set of binding configurations.
65     */
66    @Parameter
67    protected File[] configs;
68  
69    /**
70     * <p>
71     * Gets the last part of the stale filename.
72     * </p>
73     * <p>
74     * The full stale filename will be generated by pre-pending
75     * {@code "." + getExecution().getExecutionId()} to this staleFileName.
76     *
77     * @return the stale filename postfix
78     */
79    @Override
80    protected String getStaleFileName() {
81      return STALE_FILE_NAME;
82    }
83  
84    /**
85     * Retrieve a list of binding configurations.
86     *
87     * @return the collection of binding configurations
88     */
89    protected List<File> getConfigs() {
90      List<File> retval;
91      if (configs == null) {
92        retval = Collections.emptyList();
93      } else {
94        retval = Arrays.asList(configs);
95      }
96      return retval;
97    }
98  
99    /**
100    * Generate the Java source files for the provided Metaschemas.
101    *
102    * @param modules
103    *          the collection of Metaschema modules to generate sources for
104    * @throws MojoExecutionException
105    *           if an error occurred while generating sources
106    */
107   protected void generate(@NonNull Set<IModule> modules) throws MojoExecutionException {
108     DefaultBindingConfiguration bindingConfiguration = new DefaultBindingConfiguration();
109     for (File config : getConfigs()) {
110       try {
111         getLog().info("Loading binding configuration: " + config.getPath());
112         bindingConfiguration.load(config);
113       } catch (IOException ex) {
114         throw new MojoExecutionException(
115             String.format("Unable to load binding configuration from '%s'.", config.getPath()), ex);
116       }
117     }
118 
119     try {
120       getLog().info("Generating Java classes in: " + getOutputDirectory().getPath());
121       JavaGenerator.generate(modules, ObjectUtils.notNull(getOutputDirectory().toPath()),
122           bindingConfiguration);
123     } catch (IOException ex) {
124       throw new MojoExecutionException("Creation of Java classes failed.", ex);
125     }
126   }
127 
128   @Override
129   public void execute() throws MojoExecutionException {
130     File staleFile = getStaleFile();
131     try {
132       staleFile = staleFile.getCanonicalFile();
133     } catch (IOException ex) {
134       getLog().warn("Unable to resolve canonical path to stale file. Treating it as not existing.", ex);
135     }
136 
137     boolean generate;
138     if (shouldExecutionBeSkipped()) {
139       getLog().debug(String.format("Source file generation is configured to be skipped. Skipping."));
140       generate = false;
141     } else if (!staleFile.exists()) {
142       getLog().info(String.format("Stale file '%s' doesn't exist! Generating source files.", staleFile.getPath()));
143       generate = true;
144     } else {
145       generate = isGenerationRequired();
146     }
147 
148     if (generate) {
149 
150       File outputDir = getOutputDirectory();
151       getLog().debug(String.format("Using outputDirectory: %s", outputDir.getPath()));
152 
153       if (!outputDir.exists()) {
154         if (!outputDir.mkdirs()) {
155           throw new MojoExecutionException("Unable to create output directory: " + outputDir);
156         }
157       }
158 
159       // generate Java sources based on provided metaschema sources
160       final ModuleLoader loader = new ModuleLoader();
161       loader.allowEntityResolution();
162       final Set<IModule> modules = new HashSet<>();
163       for (File source : getSources().collect(Collectors.toList())) {
164         getLog().info("Using metaschema source: " + source.getPath());
165         IModule module;
166         try {
167           module = loader.load(source);
168         } catch (MetaschemaException | IOException ex) {
169           throw new MojoExecutionException("Loading of metaschema failed", ex);
170         }
171         modules.add(module);
172       }
173 
174       generate(modules);
175 
176       // create the stale file
177       if (!staleFileDirectory.exists()) {
178         if (!staleFileDirectory.mkdirs()) {
179           throw new MojoExecutionException("Unable to create output directory: " + staleFileDirectory);
180         }
181       }
182       try (OutputStream os
183           = Files.newOutputStream(staleFile.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE,
184               StandardOpenOption.TRUNCATE_EXISTING)) {
185         os.close();
186         getLog().info("Created stale file: " + staleFile);
187       } catch (IOException ex) {
188         throw new MojoExecutionException("Failed to write stale file: " + staleFile.getPath(), ex);
189       }
190 
191       // for m2e
192       getBuildContext().refresh(getOutputDirectory());
193     }
194 
195     // add generated sources to Maven
196     try {
197       getMavenProject().addCompileSourceRoot(getOutputDirectory().getCanonicalFile().getPath());
198     } catch (IOException ex) {
199       throw new MojoExecutionException("Unable to add output directory to maven sources.", ex);
200     }
201   }
202 }