001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.maven.plugin; 028 029import gov.nist.secauto.metaschema.core.model.IModule; 030import gov.nist.secauto.metaschema.core.model.MetaschemaException; 031import gov.nist.secauto.metaschema.core.model.xml.ModuleLoader; 032import gov.nist.secauto.metaschema.core.util.ObjectUtils; 033import gov.nist.secauto.metaschema.databind.codegen.JavaGenerator; 034import gov.nist.secauto.metaschema.databind.codegen.config.DefaultBindingConfiguration; 035 036import org.apache.maven.plugin.MojoExecutionException; 037import org.apache.maven.plugins.annotations.LifecyclePhase; 038import org.apache.maven.plugins.annotations.Mojo; 039import org.apache.maven.plugins.annotations.Parameter; 040 041import java.io.File; 042import java.io.IOException; 043import java.io.OutputStream; 044import java.nio.file.Files; 045import java.nio.file.StandardOpenOption; 046import java.util.Arrays; 047import java.util.Collections; 048import java.util.HashSet; 049import java.util.List; 050import java.util.Set; 051import java.util.stream.Collectors; 052 053import edu.umd.cs.findbugs.annotations.NonNull; 054 055/** 056 * Goal which generates Java source files for a given set of Module definitions. 057 */ 058@Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES) 059public class GenerateSourcesMojo 060 extends AbstractMetaschemaMojo { 061 private static final String STALE_FILE_NAME = "generateSourcesStaleFile"; 062 063 /** 064 * A set of binding configurations. 065 */ 066 @Parameter 067 protected File[] configs; 068 069 /** 070 * <p> 071 * Gets the last part of the stale filename. 072 * </p> 073 * <p> 074 * The full stale filename will be generated by pre-pending 075 * {@code "." + getExecution().getExecutionId()} to this staleFileName. 076 * 077 * @return the stale filename postfix 078 */ 079 @Override 080 protected String getStaleFileName() { 081 return STALE_FILE_NAME; 082 } 083 084 /** 085 * Retrieve a list of binding configurations. 086 * 087 * @return the collection of binding configurations 088 */ 089 protected List<File> getConfigs() { 090 List<File> retval; 091 if (configs == null) { 092 retval = Collections.emptyList(); 093 } else { 094 retval = Arrays.asList(configs); 095 } 096 return retval; 097 } 098 099 /** 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}