1
2
3
4
5
6 package gov.nist.secauto.metaschema.maven.plugin;
7
8 import gov.nist.secauto.metaschema.core.model.IConstraintLoader;
9 import gov.nist.secauto.metaschema.core.model.MetaschemaException;
10 import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet;
11 import gov.nist.secauto.metaschema.core.model.xml.ExternalConstraintsModulePostProcessor;
12 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
13 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
14 import gov.nist.secauto.metaschema.databind.IBindingContext;
15 import gov.nist.secauto.metaschema.databind.model.metaschema.BindingConstraintLoader;
16 import gov.nist.secauto.metaschema.databind.model.metaschema.BindingModuleLoader;
17
18 import org.apache.maven.plugin.AbstractMojo;
19 import org.apache.maven.plugin.MojoExecution;
20 import org.apache.maven.plugin.MojoExecutionException;
21 import org.apache.maven.plugins.annotations.Component;
22 import org.apache.maven.plugins.annotations.Parameter;
23 import org.apache.maven.project.MavenProject;
24 import org.codehaus.plexus.util.DirectoryScanner;
25 import org.sonatype.plexus.build.incremental.BuildContext;
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.net.URI;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.Objects;
33 import java.util.stream.Collectors;
34 import java.util.stream.Stream;
35
36 import edu.umd.cs.findbugs.annotations.NonNull;
37 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
38
39 public abstract class AbstractMetaschemaMojo
40 extends AbstractMojo {
41 private static final String SYSTEM_FILE_ENCODING_PROPERTY = "file.encoding";
42 private static final String[] DEFAULT_INCLUDES = { "**/*.xml" };
43
44
45
46
47
48
49
50
51 @Parameter(defaultValue = "${project}", required = true, readonly = true)
52 MavenProject mavenProject;
53
54
55
56
57
58
59 @Parameter(defaultValue = "${mojoExecution}", readonly = true)
60 private MojoExecution mojoExecution;
61
62 @Component
63 private BuildContext buildContext;
64
65
66
67
68
69
70
71
72
73
74
75
76
77 @Parameter(defaultValue = "${project.build.directory}/metaschema", readonly = true, required = true)
78 protected File staleFileDirectory;
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 @Parameter(defaultValue = "${project.build.sourceEncoding}")
101 private String encoding;
102
103
104
105
106 @Parameter(defaultValue = "${project.build.directory}/generated-sources/metaschema", required = true)
107 private File outputDirectory;
108
109
110
111
112 @Parameter(defaultValue = "${basedir}/src/main/metaschema")
113 private File metaschemaDir;
114
115
116
117
118 @Parameter(property = "constraints")
119 private File[] constraints;
120
121
122
123
124
125
126 @Parameter
127 protected String[] includes;
128
129
130
131
132
133 @Parameter
134 protected String[] excludes;
135
136
137
138
139 @Parameter(property = "metaschema.skip", defaultValue = "false")
140 private boolean skip;
141
142
143
144
145
146
147
148
149 protected final BuildContext getBuildContext() {
150 return buildContext;
151 }
152
153
154
155
156
157
158 protected final MavenProject getMavenProject() {
159 return mavenProject;
160 }
161
162
163
164
165
166
167 @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "this is a data holder")
168 public MojoExecution getMojoExecution() {
169 return mojoExecution;
170 }
171
172
173
174
175
176
177 protected File getOutputDirectory() {
178 return outputDirectory;
179 }
180
181
182
183
184
185
186
187 protected void setOutputDirectory(File outputDirectory) {
188 Objects.requireNonNull(outputDirectory, "outputDirectory");
189 this.outputDirectory = outputDirectory;
190 }
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209 protected final String getEncoding() {
210 String encoding;
211 if (this.encoding != null) {
212
213 encoding = this.encoding;
214 if (getLog().isDebugEnabled()) {
215 getLog().debug(String.format("Using configured encoding [%s].", encoding));
216 }
217 } else {
218 encoding = System.getProperty(SYSTEM_FILE_ENCODING_PROPERTY);
219 if (getLog().isWarnEnabled()) {
220 getLog().warn(String.format("Using system encoding [%s]. This build is platform dependent!", encoding));
221 }
222 }
223 return encoding;
224 }
225
226
227
228
229
230
231 protected Stream<File> getModuleSources() {
232 DirectoryScanner ds = new DirectoryScanner();
233 ds.setBasedir(metaschemaDir);
234 ds.setIncludes(includes != null && includes.length > 0 ? includes : DEFAULT_INCLUDES);
235 ds.setExcludes(excludes != null && excludes.length > 0 ? excludes : null);
236 ds.addDefaultExcludes();
237 ds.setCaseSensitive(true);
238 ds.setFollowSymlinks(false);
239 ds.scan();
240 return Stream.of(ds.getIncludedFiles()).map(filename -> new File(metaschemaDir, filename)).distinct();
241 }
242
243
244
245
246
247
248
249
250
251
252
253
254 protected List<IConstraintSet> getConstraints(@NonNull IBindingContext bindingContext)
255 throws MetaschemaException, IOException {
256 IConstraintLoader loader = new BindingConstraintLoader(bindingContext);
257 List<IConstraintSet> constraintSets = new ArrayList<>(constraints.length);
258 for (File constraint : this.constraints) {
259 constraintSets.addAll(loader.load(ObjectUtils.notNull(constraint)));
260 }
261 return CollectionUtil.unmodifiableList(constraintSets);
262 }
263
264
265
266
267
268
269
270 protected boolean shouldExecutionBeSkipped() {
271 return skip;
272 }
273
274
275
276
277
278
279 protected abstract String getStaleFileName();
280
281
282
283
284
285
286 protected final File getStaleFile() {
287 StringBuilder builder = new StringBuilder();
288 if (getMojoExecution() != null) {
289 builder.append(getMojoExecution().getExecutionId()).append('-');
290 }
291 builder.append(getStaleFileName());
292 return new File(staleFileDirectory, builder.toString());
293 }
294
295
296
297
298
299
300
301
302
303 protected boolean isGenerationRequired() {
304 final File staleFile = getStaleFile();
305 boolean generate = !staleFile.exists();
306 if (generate) {
307 if (getLog().isInfoEnabled()) {
308 getLog().info(String.format("Stale file '%s' doesn't exist! Generating source files.", staleFile.getPath()));
309 }
310 generate = true;
311 } else {
312 generate = false;
313
314 long staleLastModified = staleFile.lastModified();
315
316 BuildContext buildContext = getBuildContext();
317 URI metaschemaDirRelative = getMavenProject().getBasedir().toURI().relativize(metaschemaDir.toURI());
318
319 if (buildContext.isIncremental() && buildContext.hasDelta(metaschemaDirRelative.toString())) {
320 if (getLog().isInfoEnabled()) {
321 getLog().info("metaschemaDirRelative: " + metaschemaDirRelative.toString());
322 }
323 generate = true;
324 }
325
326 if (!generate) {
327 for (File sourceFile : getModuleSources().collect(Collectors.toList())) {
328 if (getLog().isInfoEnabled()) {
329 getLog().info("Source file: " + sourceFile.getPath());
330 }
331 if (sourceFile.lastModified() > staleLastModified) {
332 generate = true;
333 }
334 }
335 }
336 }
337 return generate;
338 }
339
340
341
342
343
344
345
346
347 @NonNull
348 protected BindingModuleLoader newModuleLoader() throws MojoExecutionException {
349 IBindingContext bindingContext = IBindingContext.instance();
350
351 List<IConstraintSet> constraints;
352 try {
353 constraints = getConstraints(bindingContext);
354 } catch (MetaschemaException | IOException ex) {
355 throw new MojoExecutionException("Unable to load external constraints.", ex);
356 }
357
358
359 BindingModuleLoader loader = constraints.isEmpty()
360 ? new BindingModuleLoader(bindingContext)
361 : new BindingModuleLoader(
362 bindingContext,
363 CollectionUtil.singletonList(new ExternalConstraintsModulePostProcessor(constraints)));
364 loader.allowEntityResolution();
365 return loader;
366 }
367 }