001/* 002 * SPDX-FileCopyrightText: none 003 * SPDX-License-Identifier: CC0-1.0 004 */ 005 006package gov.nist.secauto.metaschema.maven.plugin; 007 008import gov.nist.secauto.metaschema.core.model.IModule; 009import gov.nist.secauto.metaschema.core.model.MetaschemaException; 010import gov.nist.secauto.metaschema.core.util.ObjectUtils; 011import gov.nist.secauto.metaschema.databind.codegen.JavaGenerator; 012import gov.nist.secauto.metaschema.databind.codegen.config.DefaultBindingConfiguration; 013import gov.nist.secauto.metaschema.databind.model.metaschema.BindingModuleLoader; 014import gov.nist.secauto.metaschema.databind.model.metaschema.IBindingMetaschemaModule; 015 016import org.apache.maven.plugin.MojoExecutionException; 017import org.apache.maven.plugins.annotations.LifecyclePhase; 018import org.apache.maven.plugins.annotations.Mojo; 019import org.apache.maven.plugins.annotations.Parameter; 020 021import java.io.File; 022import java.io.IOException; 023import java.io.OutputStream; 024import java.nio.file.Files; 025import java.nio.file.StandardOpenOption; 026import java.util.Arrays; 027import java.util.Collections; 028import java.util.HashSet; 029import java.util.List; 030import java.util.Set; 031import java.util.stream.Collectors; 032 033import edu.umd.cs.findbugs.annotations.NonNull; 034 035/** 036 * Goal which generates Java source files for a given set of Metaschema modules. 037 */ 038@Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES) 039public class GenerateSourcesMojo 040 extends AbstractMetaschemaMojo { 041 private static final String STALE_FILE_NAME = "generateSourcesStaleFile"; 042 043 /** 044 * A set of binding configurations. 045 */ 046 @Parameter 047 protected File[] configs; 048 049 /** 050 * <p> 051 * Gets the last part of the stale filename. 052 * </p> 053 * <p> 054 * The full stale filename will be generated by pre-pending 055 * {@code "." + getExecution().getExecutionId()} to this staleFileName. 056 * 057 * @return the stale filename postfix 058 */ 059 @Override 060 protected String getStaleFileName() { 061 return STALE_FILE_NAME; 062 } 063 064 /** 065 * Retrieve a list of binding configurations. 066 * 067 * @return the collection of binding configurations 068 */ 069 protected List<File> getConfigs() { 070 List<File> retval; 071 if (configs == null) { 072 retval = Collections.emptyList(); 073 } else { 074 retval = Arrays.asList(configs); 075 } 076 return retval; 077 } 078 079 /** 080 * Generate the Java source files for the provided Metaschemas. 081 * 082 * @param modules 083 * the collection of Metaschema modules to generate sources for 084 * @throws MojoExecutionException 085 * if an error occurred while generating sources 086 */ 087 protected void generate(@NonNull Set<IModule> modules) throws MojoExecutionException { 088 DefaultBindingConfiguration bindingConfiguration = new DefaultBindingConfiguration(); 089 for (File config : getConfigs()) { 090 try { 091 if (getLog().isInfoEnabled()) { 092 getLog().info("Loading binding configuration: " + config.getPath()); 093 } 094 bindingConfiguration.load(config); 095 } catch (IOException ex) { 096 throw new MojoExecutionException( 097 String.format("Unable to load binding configuration from '%s'.", config.getPath()), ex); 098 } 099 } 100 101 try { 102 if (getLog().isInfoEnabled()) { 103 getLog().info("Generating Java classes in: " + getOutputDirectory().getPath()); 104 } 105 JavaGenerator.generate(modules, ObjectUtils.notNull(getOutputDirectory().toPath()), 106 bindingConfiguration); 107 } catch (IOException ex) { 108 throw new MojoExecutionException("Creation of Java classes failed.", ex); 109 } 110 } 111 112 @Override 113 public void execute() throws MojoExecutionException { 114 File staleFile = getStaleFile(); 115 try { 116 staleFile = staleFile.getCanonicalFile(); 117 } catch (IOException ex) { 118 if (getLog().isWarnEnabled()) { 119 getLog().warn("Unable to resolve canonical path to stale file. Treating it as not existing.", ex); 120 } 121 } 122 123 boolean generate; 124 if (shouldExecutionBeSkipped()) { 125 if (getLog().isDebugEnabled()) { 126 getLog().debug(String.format("Source file generation is configured to be skipped. Skipping.")); 127 } 128 generate = false; 129 } else if (!staleFile.exists()) { 130 if (getLog().isInfoEnabled()) { 131 getLog().info(String.format("Stale file '%s' doesn't exist! Generating source files.", staleFile.getPath())); 132 } 133 generate = true; 134 } else { 135 generate = isGenerationRequired(); 136 } 137 138 if (generate) { 139 140 File outputDir = getOutputDirectory(); 141 if (getLog().isDebugEnabled()) { 142 getLog().debug(String.format("Using outputDirectory: %s", outputDir.getPath())); 143 } 144 145 if (!outputDir.exists() && !outputDir.mkdirs()) { 146 throw new MojoExecutionException("Unable to create output directory: " + outputDir); 147 } 148 149 BindingModuleLoader loader = newModuleLoader(); 150 151 // generate Java sources based on provided metaschema sources 152 final Set<IModule> modules = new HashSet<>(); 153 for (File source : getModuleSources().collect(Collectors.toList())) { 154 assert source != null; 155 if (getLog().isInfoEnabled()) { 156 getLog().info("Using metaschema source: " + source.getPath()); 157 } 158 IBindingMetaschemaModule module; 159 try { 160 module = loader.load(source); 161 } catch (MetaschemaException | IOException ex) { 162 throw new MojoExecutionException("Loading of metaschema failed", ex); 163 } 164 modules.add(module); 165 } 166 167 generate(modules); 168 169 // create the stale file 170 if (!staleFileDirectory.exists() && !staleFileDirectory.mkdirs()) { 171 throw new MojoExecutionException("Unable to create output directory: " + staleFileDirectory); 172 } 173 try (OutputStream os 174 = Files.newOutputStream(staleFile.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE, 175 StandardOpenOption.TRUNCATE_EXISTING)) { 176 os.close(); 177 if (getLog().isInfoEnabled()) { 178 getLog().info("Created stale file: " + staleFile); 179 } 180 } catch (IOException ex) { 181 throw new MojoExecutionException("Failed to write stale file: " + staleFile.getPath(), ex); 182 } 183 184 // for m2e 185 getBuildContext().refresh(getOutputDirectory()); 186 } 187 188 // add generated sources to Maven 189 try { 190 getMavenProject().addCompileSourceRoot(getOutputDirectory().getCanonicalFile().getPath()); 191 } catch (IOException ex) { 192 throw new MojoExecutionException("Unable to add output directory to maven sources.", ex); 193 } 194 } 195}