From 2f1a93c6fe539c9203ad6335ebd79a414640ae1b Mon Sep 17 00:00:00 2001 From: David Waltermire Date: Fri, 1 Nov 2024 23:14:35 -0400 Subject: [PATCH] Refactored code to reduce warnings. --- cli-processor/pom.xml | 4 - .../cli/processor/CLIProcessor.java | 37 +-- .../command/AbstractTerminalCommand.java | 6 +- .../command/CommandExecutionException.java | 99 ++++++ .../core/util/DeleteOnShutdown.java | 90 ++++++ .../core/metapath/cst/path/StepTest.java | 80 ++--- databind-metaschema/pom.xml | 5 - .../modules/sarif/SarifValidationHandler.java | 57 +++- .../sarif/SarifValidationHandlerTest.java | 3 +- metaschema-cli/pom.xml | 13 +- .../gov/nist/secauto/metaschema/cli/CLI.java | 3 + .../commands/AbstractConvertSubcommand.java | 8 +- .../AbstractValidateContentCommand.java | 119 ++++++-- .../ConvertContentUsingModuleCommand.java | 8 +- .../cli/commands/GenerateDiagramCommand.java | 14 +- .../cli/commands/GenerateSchemaCommand.java | 86 ++---- .../cli/commands/MetaschemaCommands.java | 289 ++++++++++++++---- .../ValidateContentUsingModuleCommand.java | 8 +- .../cli/commands/ValidateModuleCommand.java | 11 +- .../metapath/EvaluateMetapathCommand.java | 42 ++- .../metapath/ListFunctionsSubcommand.java | 9 +- .../commands/metapath/MetapathCommand.java | 4 + .../cli/util/LoggingValidationHandler.java | 4 + .../nist/secauto/metaschema/cli/CLITest.java | 28 +- .../EvaluateMetapathSubCommandTest.java | 6 +- metaschema-maven-plugin/pom.xml | 43 ++- .../maven/plugin/AbstractMetaschemaMojo.java | 2 +- pom.xml | 20 -- schemagen/pom.xml | 5 - 29 files changed, 761 insertions(+), 342 deletions(-) create mode 100644 cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/CommandExecutionException.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/util/DeleteOnShutdown.java diff --git a/cli-processor/pom.xml b/cli-processor/pom.xml index 8cf166e9d..0fe90e8ad 100644 --- a/cli-processor/pom.xml +++ b/cli-processor/pom.xml @@ -52,10 +52,6 @@ nl.talsmasoftware lazy4j - - com.google.auto.service - auto-service-annotations - org.apache.logging.log4j log4j-core diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/CLIProcessor.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/CLIProcessor.java index 17c015a00..104fb9018 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/CLIProcessor.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/CLIProcessor.java @@ -12,6 +12,7 @@ import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument; import gov.nist.secauto.metaschema.cli.processor.command.ICommand; import gov.nist.secauto.metaschema.cli.processor.command.ICommandExecutor; +import gov.nist.secauto.metaschema.core.util.AutoCloser; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.IVersionInfo; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -624,31 +625,33 @@ protected String buildHelpCliSyntax() { return retval; } + /** + * Output the help text to the console. + */ public void showHelp() { HelpFormatter formatter = new HelpFormatter(); formatter.setLongOptSeparator("="); + @SuppressWarnings("resource") AnsiPrintStream out = AnsiConsole.out(); - int terminalWidth = Math.max(out.getTerminalWidth(), 40); - @SuppressWarnings("resource") - PrintWriter writer = new PrintWriter( // NOPMD not owned - out, + try (PrintWriter writer = new PrintWriter( // NOPMD not owned + AutoCloser.preventClose(out), true, - StandardCharsets.UTF_8); - formatter.printHelp( - writer, - terminalWidth, - buildHelpCliSyntax(), - buildHelpHeader(), - toOptions(), - HelpFormatter.DEFAULT_LEFT_PAD, - HelpFormatter.DEFAULT_DESC_PAD, - buildHelpFooter(), - false); - writer.flush(); + StandardCharsets.UTF_8)) { + formatter.printHelp( + writer, + Math.max(out.getTerminalWidth(), 50), + buildHelpCliSyntax(), + buildHelpHeader(), + toOptions(), + HelpFormatter.DEFAULT_LEFT_PAD, + HelpFormatter.DEFAULT_DESC_PAD, + buildHelpFooter(), + false); + writer.flush(); + } } } - } diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractTerminalCommand.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractTerminalCommand.java index 17a1eab90..65da54530 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractTerminalCommand.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractTerminalCommand.java @@ -39,16 +39,16 @@ protected static Path getCurrentWorkingDirectory() { @NonNull protected static Path resolveAgainstCWD(@NonNull Path path) { - return getCurrentWorkingDirectory().resolve(path).normalize(); + return ObjectUtils.notNull(getCurrentWorkingDirectory().resolve(path).normalize()); } @NonNull protected static URI resolveAgainstCWD(@NonNull URI uri) { - return getCurrentWorkingDirectory().toUri().resolve(uri.normalize()); + return ObjectUtils.notNull(getCurrentWorkingDirectory().toUri().resolve(uri.normalize())); } @NonNull protected static URI resolveAgainstCWD(@NonNull String uri) throws URISyntaxException { - return UriUtils.toUri(uri, getCurrentWorkingDirectory().toUri()); + return UriUtils.toUri(uri, ObjectUtils.notNull(getCurrentWorkingDirectory().toUri())); } } diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/CommandExecutionException.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/CommandExecutionException.java new file mode 100644 index 000000000..78997116f --- /dev/null +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/CommandExecutionException.java @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.cli.processor.command; + +import gov.nist.secauto.metaschema.cli.processor.ExitCode; +import gov.nist.secauto.metaschema.cli.processor.ExitStatus; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * For use in commands to short-circut command execution. + */ +public class CommandExecutionException + extends Exception { + private final ExitCode exitCode; + + /** + * the serial version UID. + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with the provided {@code code}, and no message or + * cause. + * + * @param code + * the exit code associated with this error + */ + public CommandExecutionException(@NonNull ExitCode code) { + this.exitCode = code; + } + + /** + * Constructs a new exception with the provided {@code code}, {@code message}, + * and no cause. + * + * @param code + * the exit code associated with this error + * @param message + * the exception message + */ + public CommandExecutionException(@NonNull ExitCode code, String message) { + super(message); + this.exitCode = code; + } + + /** + * Constructs a new exception with the no message and provided the {@code code} + * and {@code cause}. + * + * @param code + * the exit code associated with this error + * @param cause + * the original exception cause + */ + public CommandExecutionException(@NonNull ExitCode code, Throwable cause) { + super(cause); + this.exitCode = code; + } + + /** + * Constructs a new exception with the provided {@code code}, {@code message}, + * and {@code cause}. + * + * @param code + * the exit code associated with this error + * @param message + * the exception message + * @param cause + * the original exception cause + */ + public CommandExecutionException(@NonNull ExitCode code, String message, Throwable cause) { + super(message, cause); + this.exitCode = code; + } + + /** + * Generate an {@link ExitStatus} based on this exception. + * + * @return the exit status + */ + @NonNull + public ExitStatus toExitStatus() { + String message = getLocalizedMessage(); + + ExitStatus retval = message == null + ? exitCode.exit() + : exitCode.exitMessage(message); + + Throwable cause = getCause(); + if (cause != null) { + retval.withThrowable(cause); + } + return retval; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/util/DeleteOnShutdown.java b/core/src/main/java/gov/nist/secauto/metaschema/core/util/DeleteOnShutdown.java new file mode 100644 index 000000000..662a97670 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/util/DeleteOnShutdown.java @@ -0,0 +1,90 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.util; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Used to perform cleanup on shutdown. + */ +@SuppressWarnings("PMD.DoNotUseThreads") +public final class DeleteOnShutdown { + private static Set paths = new LinkedHashSet<>(); + private static final Lock LOCK = new ReentrantLock(); + + static { + Runtime.getRuntime().addShutdownHook( + new Thread(DeleteOnShutdown::shutdownHook)); + } + + @SuppressWarnings("PMD.NullAssignment") + private static void shutdownHook() { + LOCK.lock(); + try { + Set localSet = new LinkedHashSet<>(paths); + paths = null; + localSet.forEach(path -> { + try { + Files.walkFileTree(path, + new SimpleFileVisitor<>() { + @Override + public FileVisitResult postVisitDirectory( + Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile( + Path file, BasicFileAttributes attrs) + throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException ex) { + // this is a best effort + } + }); + } finally { + LOCK.unlock(); + } + } + + /** + * Register a new path to be deleted on JVM termination. + *

+ * If the path is a directory, then its contents will also be deleted. + * + * @param path + * the path to delete + */ + public static void register(Path path) { + LOCK.lock(); + try { + if (paths == null) { + throw new IllegalStateException("ShutdownHook already in progress."); + } + paths.add(path); + } finally { + LOCK.unlock(); + } + } + + private DeleteOnShutdown() { + // disable construction + } + +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/path/StepTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/path/StepTest.java index f5af2bb6e..3c0f7b99a 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/path/StepTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/cst/path/StepTest.java @@ -398,27 +398,27 @@ void testDescendantAxis() { = MetapathExpression.compile("descendant::*", dynamicContext.getStaticContext()) .evaluate(node2, dynamicContext); - IModelNodeItem a + IModelNodeItem nodeA = ObjectUtils.requireNonNull(node2.getModelItemsByName(new QName(NS, "a"))).iterator().next(); - IModelNodeItem b + IModelNodeItem nodeB = ObjectUtils.requireNonNull(node2.getModelItemsByName(new QName(NS, "b"))).iterator().next(); - IModelNodeItem c + IModelNodeItem nodeC = ObjectUtils.requireNonNull(node2.getModelItemsByName(new QName(NS, "c"))).iterator().next(); Assertions.assertThat(actual.getValue()).isEqualTo( List.of( - a, - ObjectUtils.requireNonNull(a.getModelItemsByName(new QName(NS, "x"))).iterator().next(), - ObjectUtils.requireNonNull(a.getModelItemsByName(new QName(NS, "y"))).iterator().next(), - ObjectUtils.requireNonNull(a.getModelItemsByName(new QName(NS, "z"))).iterator().next(), - b, - ObjectUtils.requireNonNull(b.getModelItemsByName(new QName(NS, "x"))).iterator().next(), - ObjectUtils.requireNonNull(b.getModelItemsByName(new QName(NS, "y"))).iterator().next(), - ObjectUtils.requireNonNull(b.getModelItemsByName(new QName(NS, "z"))).iterator().next(), - c, - ObjectUtils.requireNonNull(c.getModelItemsByName(new QName(NS, "x"))).iterator().next(), - ObjectUtils.requireNonNull(c.getModelItemsByName(new QName(NS, "y"))).iterator().next(), - ObjectUtils.requireNonNull(c.getModelItemsByName(new QName(NS, "z"))).iterator().next())); + nodeA, + ObjectUtils.requireNonNull(nodeA.getModelItemsByName(new QName(NS, "x"))).iterator().next(), + ObjectUtils.requireNonNull(nodeA.getModelItemsByName(new QName(NS, "y"))).iterator().next(), + ObjectUtils.requireNonNull(nodeA.getModelItemsByName(new QName(NS, "z"))).iterator().next(), + nodeB, + ObjectUtils.requireNonNull(nodeB.getModelItemsByName(new QName(NS, "x"))).iterator().next(), + ObjectUtils.requireNonNull(nodeB.getModelItemsByName(new QName(NS, "y"))).iterator().next(), + ObjectUtils.requireNonNull(nodeB.getModelItemsByName(new QName(NS, "z"))).iterator().next(), + nodeC, + ObjectUtils.requireNonNull(nodeC.getModelItemsByName(new QName(NS, "x"))).iterator().next(), + ObjectUtils.requireNonNull(nodeC.getModelItemsByName(new QName(NS, "y"))).iterator().next(), + ObjectUtils.requireNonNull(nodeC.getModelItemsByName(new QName(NS, "z"))).iterator().next())); } @Test @@ -435,28 +435,28 @@ void testDescendantOrSelfAxis() { = MetapathExpression.compile("descendant-or-self::*", dynamicContext.getStaticContext()) .evaluate(node2, dynamicContext); - IModelNodeItem a + IModelNodeItem nodeA = ObjectUtils.requireNonNull(node2.getModelItemsByName(new QName(NS, "a"))).iterator().next(); - IModelNodeItem b + IModelNodeItem nodeB = ObjectUtils.requireNonNull(node2.getModelItemsByName(new QName(NS, "b"))).iterator().next(); - IModelNodeItem c + IModelNodeItem nodeC = ObjectUtils.requireNonNull(node2.getModelItemsByName(new QName(NS, "c"))).iterator().next(); Assertions.assertThat(actual.getValue()).isEqualTo( List.of( node2, - a, - ObjectUtils.requireNonNull(a.getModelItemsByName(new QName(NS, "x"))).iterator().next(), - ObjectUtils.requireNonNull(a.getModelItemsByName(new QName(NS, "y"))).iterator().next(), - ObjectUtils.requireNonNull(a.getModelItemsByName(new QName(NS, "z"))).iterator().next(), - b, - ObjectUtils.requireNonNull(b.getModelItemsByName(new QName(NS, "x"))).iterator().next(), - ObjectUtils.requireNonNull(b.getModelItemsByName(new QName(NS, "y"))).iterator().next(), - ObjectUtils.requireNonNull(b.getModelItemsByName(new QName(NS, "z"))).iterator().next(), - c, - ObjectUtils.requireNonNull(c.getModelItemsByName(new QName(NS, "x"))).iterator().next(), - ObjectUtils.requireNonNull(c.getModelItemsByName(new QName(NS, "y"))).iterator().next(), - ObjectUtils.requireNonNull(c.getModelItemsByName(new QName(NS, "z"))).iterator().next())); + nodeA, + ObjectUtils.requireNonNull(nodeA.getModelItemsByName(new QName(NS, "x"))).iterator().next(), + ObjectUtils.requireNonNull(nodeA.getModelItemsByName(new QName(NS, "y"))).iterator().next(), + ObjectUtils.requireNonNull(nodeA.getModelItemsByName(new QName(NS, "z"))).iterator().next(), + nodeB, + ObjectUtils.requireNonNull(nodeB.getModelItemsByName(new QName(NS, "x"))).iterator().next(), + ObjectUtils.requireNonNull(nodeB.getModelItemsByName(new QName(NS, "y"))).iterator().next(), + ObjectUtils.requireNonNull(nodeB.getModelItemsByName(new QName(NS, "z"))).iterator().next(), + nodeC, + ObjectUtils.requireNonNull(nodeC.getModelItemsByName(new QName(NS, "x"))).iterator().next(), + ObjectUtils.requireNonNull(nodeC.getModelItemsByName(new QName(NS, "y"))).iterator().next(), + ObjectUtils.requireNonNull(nodeC.getModelItemsByName(new QName(NS, "z"))).iterator().next())); } @Test @@ -521,15 +521,15 @@ void testFollowingAxis() { .getModelItemsByName(new QName(NS, "node-2")) .iterator().next(); - IModelNodeItem c + IModelNodeItem nodeC = ObjectUtils.requireNonNull(node2.getModelItemsByName(new QName(NS, "c"))).iterator().next(); Assertions.assertThat(actual.getValue()).isEqualTo( List.of( - c, - ObjectUtils.requireNonNull(c.getModelItemsByName(new QName(NS, "x"))).iterator().next(), - ObjectUtils.requireNonNull(c.getModelItemsByName(new QName(NS, "y"))).iterator().next(), - ObjectUtils.requireNonNull(c.getModelItemsByName(new QName(NS, "z"))).iterator().next())); + nodeC, + ObjectUtils.requireNonNull(nodeC.getModelItemsByName(new QName(NS, "x"))).iterator().next(), + ObjectUtils.requireNonNull(nodeC.getModelItemsByName(new QName(NS, "y"))).iterator().next(), + ObjectUtils.requireNonNull(nodeC.getModelItemsByName(new QName(NS, "z"))).iterator().next())); } @Test @@ -550,14 +550,14 @@ void testPrecedingAxis() { .getModelItemsByName(new QName(NS, "node-2")) .iterator().next(); - IModelNodeItem a + IModelNodeItem nodeA = ObjectUtils.requireNonNull(node2.getModelItemsByName(new QName(NS, "a"))).iterator().next(); Assertions.assertThat(actual.getValue()).isEqualTo( List.of( - a, - ObjectUtils.requireNonNull(a.getModelItemsByName(new QName(NS, "x"))).iterator().next(), - ObjectUtils.requireNonNull(a.getModelItemsByName(new QName(NS, "y"))).iterator().next(), - ObjectUtils.requireNonNull(a.getModelItemsByName(new QName(NS, "z"))).iterator().next())); + nodeA, + ObjectUtils.requireNonNull(nodeA.getModelItemsByName(new QName(NS, "x"))).iterator().next(), + ObjectUtils.requireNonNull(nodeA.getModelItemsByName(new QName(NS, "y"))).iterator().next(), + ObjectUtils.requireNonNull(nodeA.getModelItemsByName(new QName(NS, "z"))).iterator().next())); } } diff --git a/databind-metaschema/pom.xml b/databind-metaschema/pom.xml index efffba434..ee9b3ab7c 100644 --- a/databind-metaschema/pom.xml +++ b/databind-metaschema/pom.xml @@ -38,11 +38,6 @@ metaschema-databind - - com.google.auto.service - auto-service-annotations - - com.github.spotbugs spotbugs-annotations diff --git a/databind-metaschema/src/main/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandler.java b/databind-metaschema/src/main/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandler.java index 3211cf8e7..31b4e779b 100644 --- a/databind-metaschema/src/main/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandler.java +++ b/databind-metaschema/src/main/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandler.java @@ -45,6 +45,7 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -56,6 +57,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +/** + * Supports building a Static Analysis Results Interchange Format (SARIF) + * document based on a set of validation findings. + */ @SuppressWarnings("PMD.CouplingBetweenObjects") public final class SarifValidationHandler { private enum Kind { @@ -99,15 +104,15 @@ public String getLabel() { } @NonNull - public static final String SARIF_NS = "https://docs.oasis-open.org/sarif/sarif/v2.1.0"; + static final String SARIF_NS = "https://docs.oasis-open.org/sarif/sarif/v2.1.0"; @NonNull - public static final IAttributable.Key SARIF_HELP_URL_KEY + static final IAttributable.Key SARIF_HELP_URL_KEY = IAttributable.key("help-url", SARIF_NS); @NonNull - public static final IAttributable.Key SARIF_HELP_TEXT_KEY + static final IAttributable.Key SARIF_HELP_TEXT_KEY = IAttributable.key("help-text", SARIF_NS); @NonNull - public static final IAttributable.Key SARIF_HELP_MARKDOWN_KEY + static final IAttributable.Key SARIF_HELP_MARKDOWN_KEY = IAttributable.key("help-markdown", SARIF_NS); @NonNull @@ -131,6 +136,15 @@ public String getLabel() { private final SchemaRuleRecord schemaRule = new SchemaRuleRecord(); private boolean schemaValid = true; + /** + * Construct a new validation handler. + * + * @param source + * the URI of the content that was validated + * @param toolVersion + * the version information for the tool producing the validation + * results + */ public SarifValidationHandler( @NonNull URI source, @Nullable IVersionInfo toolVersion) { @@ -147,17 +161,29 @@ private URI getSource() { return source; } - public IVersionInfo getToolVersion() { + private IVersionInfo getToolVersion() { return toolVersion; } - public void addFindings(@NonNull List findings) { + /** + * Register a collection of validation finding. + * + * @param findings + * the findings to register + */ + public void addFindings(@NonNull Collection findings) { for (IValidationFinding finding : findings) { assert finding != null; addFinding(finding); } } + /** + * Register a validation finding. + * + * @param finding + * the finding to register + */ public void addFinding(@NonNull IValidationFinding finding) { if (finding instanceof JsonValidationFinding) { addJsonValidationFinding((JsonValidationFinding) finding); @@ -207,7 +233,20 @@ private void addConstraintValidationFinding(@NonNull ConstraintValidationFinding results.add(new ConstraintResult(finding)); } - public void write(@NonNull Path outputFile) throws IOException { + /** + * Write the collection of findings to the provided output file. + * + * @param outputFile + * the path to the output file to write to + * @param bindingContext + * the context used to access Metaschema module information based on + * Java class bindings + * @throws IOException + * if an error occurred while writing the SARIF file + */ + public void write( + @NonNull Path outputFile, + @NonNull IBindingContext bindingContext) throws IOException { URI output = ObjectUtils.notNull(outputFile.toUri()); @@ -246,7 +285,6 @@ public void write(@NonNull Path outputFile) throws IOException { run.setTool(tool); } - IBindingContext bindingContext = IBindingContext.newInstance(); bindingContext.registerModule(SarifModule.class); bindingContext.newSerializer(Format.JSON, Sarif.class) .disableFeature(SerializationFeature.SERIALIZE_ROOT) @@ -428,7 +466,8 @@ public List generateResults(@NonNull URI output) throws IOException { assert constraint != null; ConstraintRuleRecord rule = getRuleRecord(constraint); - @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") Result result = new Result(); + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + Result result = new Result(); String id = constraint.getId(); if (id != null) { diff --git a/databind-metaschema/src/test/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandlerTest.java b/databind-metaschema/src/test/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandlerTest.java index 9dce9a8d1..76b3488ac 100644 --- a/databind-metaschema/src/test/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandlerTest.java +++ b/databind-metaschema/src/test/java/gov/nist/secauto/metaschema/modules/sarif/SarifValidationHandlerTest.java @@ -15,6 +15,7 @@ import gov.nist.secauto.metaschema.core.model.validation.IValidationFinding; import gov.nist.secauto.metaschema.core.util.IVersionInfo; import gov.nist.secauto.metaschema.core.util.ObjectUtils; +import gov.nist.secauto.metaschema.databind.IBindingContext; import org.jmock.Expectations; import org.jmock.junit5.JUnit5Mockery; @@ -110,7 +111,7 @@ void testValid() throws IOException { // no need to cleanup this file, since it is created in the target directory Path sarifFile = ObjectUtils.requireNonNull(Paths.get("target/test.sarif")); - handler.write(sarifFile); + handler.write(sarifFile, IBindingContext.newInstance()); Path sarifSchema = Paths.get("modules/sarif/sarif-schema-2.1.0.json"); diff --git a/metaschema-cli/pom.xml b/metaschema-cli/pom.xml index 2c7a9286c..cd71d3fcc 100644 --- a/metaschema-cli/pom.xml +++ b/metaschema-cli/pom.xml @@ -44,16 +44,6 @@ ${project.groupId} metaschema-databind-modules - - - com.github.spotbugs - spotbugs-annotations - org.apache.logging.log4j @@ -66,8 +56,7 @@ io.github.hakky54 - consolecaptor - 1.0.3 + logcaptor test diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/CLI.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/CLI.java index 19b96471e..542b654d5 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/CLI.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/CLI.java @@ -19,6 +19,9 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * The main entry point for the CLI application. + */ @SuppressWarnings("PMD.ShortClassName") public final class CLI { /** diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java index e2ae2c789..5921f4d74 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java @@ -12,7 +12,6 @@ import gov.nist.secauto.metaschema.cli.processor.command.CommandExecutionException; import gov.nist.secauto.metaschema.cli.processor.command.DefaultExtraArgument; import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument; -import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.core.util.AutoCloser; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.IBindingContext; @@ -39,7 +38,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; /** - * Used by implementing classes to declare a content conversion command. + * Used by implementing classes to provide a content conversion command. */ public abstract class AbstractConvertSubcommand extends AbstractTerminalCommand { @@ -94,11 +93,8 @@ protected AbstractConversionCommandExecutor( * Get the binding context to use for data processing. * * @return the context - * @throws MetaschemaException - * if a Metaschema error occurred - * @throws IOException - * if an error occurred while reading data * @throws CommandExecutionException + * if an error occurred getting the binding context */ @NonNull protected abstract IBindingContext getBindingContext() throws CommandExecutionException; diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java index 782891066..f854b42d6 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java @@ -18,7 +18,6 @@ import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration; import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.model.IModule; -import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet; import gov.nist.secauto.metaschema.core.model.constraint.ValidationFeature; import gov.nist.secauto.metaschema.core.model.validation.AggregateValidationResult; @@ -47,7 +46,11 @@ import java.util.Set; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +/** + * Used by implementing classes to provide a content validation command. + */ public abstract class AbstractValidateContentCommand extends AbstractTerminalCommand { private static final Logger LOGGER = LogManager.getLogger(AbstractValidateContentCommand.class); @@ -113,6 +116,9 @@ public List getExtraArguments() { return EXTRA_ARGUMENTS; } + /** + * Drives the validation execution. + */ protected abstract class AbstractValidationCommandExecutor extends AbstractCommandExecutor { @@ -136,27 +142,59 @@ public AbstractValidationCommandExecutor( * @param constraintSets * the constraints to configure in the resulting binding context * @return the context - * @throws MetaschemaException - * if a Metaschema error occurred - * @throws IOException - * if an error occurred while reading data + * @throws CommandExecutionException + * if a error occurred while getting the binding context */ @NonNull protected abstract IBindingContext getBindingContext(@NonNull Set constraintSets) throws CommandExecutionException; + /** + * Get the module to use for validation. + *

+ * This module is used to generate schemas and as a source of built-in + * constraints. + * + * @param commandLine + * the provided command line argument information + * @param bindingContext + * the context used to access Metaschema module information based on + * Java class bindings + * @return the loaded Metaschema module + * @throws CommandExecutionException + * if an error occurred while loading the module + */ @NonNull protected abstract IModule getModule( @NonNull CommandLine commandLine, @NonNull IBindingContext bindingContext) throws CommandExecutionException; + /** + * Get the schema validation implementation requested based on the provided + * command line arguments. + *

+ * It is typical for this call to result in the dynamic generation of a schema + * to use for validation. + * + * @param module + * the Metaschema module to generate the schema from + * @param commandLine + * the provided command line argument information + * @param bindingContext + * the context used to access Metaschema module information based on + * Java class bindings + * @return the provider + */ @NonNull protected abstract ISchemaValidationProvider getSchemaValidationProvider( @NonNull IModule module, @NonNull CommandLine commandLine, @NonNull IBindingContext bindingContext); + /** + * Execute the validation operation. + */ @SuppressWarnings("PMD.OnlyOneReturn") // readability @Override public void execute() throws CommandExecutionException { @@ -168,40 +206,63 @@ public void execute() throws CommandExecutionException { CONSTRAINTS_OPTION, currentWorkingDirectory); - IBindingContext bindingContext = getBindingContext(constraintSets); - IBoundLoader loader = bindingContext.newBoundLoader(); - List extraArgs = cmdLine.getArgList(); URI source = MetaschemaCommands.handleSource( ObjectUtils.requireNonNull(extraArgs.get(0)), currentWorkingDirectory); + IBindingContext bindingContext = getBindingContext(constraintSets); + IBoundLoader loader = bindingContext.newBoundLoader(); Format asFormat = MetaschemaCommands.determineSourceFormat( cmdLine, MetaschemaCommands.AS_FORMAT_OPTION, loader, source); - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Validating '{}' as {}.", source, asFormat.name()); + IValidationResult validationResult = validate(source, asFormat, cmdLine, bindingContext); + handleOutput(source, validationResult, cmdLine, bindingContext); + + if (validationResult == null || validationResult.isPassing()) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("The file '{}' is valid.", source); + } + } else if (LOGGER.isErrorEnabled()) { + LOGGER.error("The file '{}' is invalid.", source); } - IMutableConfiguration> configuration = new DefaultConfiguration<>(); - if (cmdLine.hasOption(SARIF_OUTPUT_FILE_OPTION) && cmdLine.hasOption(SARIF_INCLUDE_PASS_OPTION)) { - configuration.enableFeature(ValidationFeature.VALIDATE_GENERATE_PASS_FINDINGS); + if (validationResult != null && !validationResult.isPassing()) { + throw new CommandExecutionException(ExitCode.FAIL); + } + } + + @SuppressWarnings("PMD.CyclomaticComplexity") + @Nullable + private IValidationResult validate( + @NonNull URI source, + @NonNull Format asFormat, + @NonNull CommandLine commandLine, + @NonNull IBindingContext bindingContext) throws CommandExecutionException { + + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Validating '{}' as {}.", source, asFormat.name()); } IValidationResult validationResult = null; try { - IModule module = bindingContext.registerModule(getModule(getCommandLine(), bindingContext)); - if (!cmdLine.hasOption(NO_SCHEMA_VALIDATION_OPTION)) { + IModule module = bindingContext.registerModule(getModule(commandLine, bindingContext)); + if (!commandLine.hasOption(NO_SCHEMA_VALIDATION_OPTION)) { // perform schema validation - validationResult = getSchemaValidationProvider(module, getCommandLine(), bindingContext) + validationResult = getSchemaValidationProvider(module, commandLine, bindingContext) .validateWithSchema(source, asFormat, bindingContext); } - if (!cmdLine.hasOption(NO_CONSTRAINT_VALIDATION_OPTION)) { + if (!commandLine.hasOption(NO_CONSTRAINT_VALIDATION_OPTION)) { + IMutableConfiguration> configuration = new DefaultConfiguration<>(); + if (commandLine.hasOption(SARIF_OUTPUT_FILE_OPTION) && commandLine.hasOption(SARIF_INCLUDE_PASS_OPTION)) { + configuration.enableFeature(ValidationFeature.VALIDATE_GENERATE_PASS_FINDINGS); + } + // perform constraint validation IValidationResult constraintValidationResult = bindingContext.validateWithConstraints(source, configuration); validationResult = validationResult == null @@ -223,9 +284,16 @@ public void execute() throws CommandExecutionException { } catch (MetapathException ex) { throw new CommandExecutionException(ExitCode.PROCESSING_ERROR, ex.getLocalizedMessage(), ex); } + return validationResult; + } - if (cmdLine.hasOption(SARIF_OUTPUT_FILE_OPTION) && LOGGER.isInfoEnabled()) { - Path sarifFile = ObjectUtils.notNull(Paths.get(cmdLine.getOptionValue(SARIF_OUTPUT_FILE_OPTION))); + private void handleOutput( + @NonNull URI source, + @Nullable IValidationResult validationResult, + @NonNull CommandLine commandLine, + @NonNull IBindingContext bindingContext) throws CommandExecutionException { + if (commandLine.hasOption(SARIF_OUTPUT_FILE_OPTION) && LOGGER.isInfoEnabled()) { + Path sarifFile = ObjectUtils.notNull(Paths.get(commandLine.getOptionValue(SARIF_OUTPUT_FILE_OPTION))); IVersionInfo version = getCallingContext().getCLIProcessor().getVersionInfos().get(CLIProcessor.COMMAND_VERSION); @@ -235,7 +303,7 @@ public void execute() throws CommandExecutionException { if (validationResult != null) { sarifHandler.addFindings(validationResult.getFindings()); } - sarifHandler.write(sarifFile); + sarifHandler.write(sarifFile, bindingContext); } catch (IOException ex) { throw new CommandExecutionException(ExitCode.IO_ERROR, ex.getLocalizedMessage(), ex); } @@ -244,17 +312,6 @@ public void execute() throws CommandExecutionException { LoggingValidationHandler.instance().handleResults(validationResult); } - if (validationResult == null || validationResult.isPassing()) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("The file '{}' is valid.", source); - } - } else if (LOGGER.isErrorEnabled()) { - LOGGER.error("The file '{}' is invalid.", source); - } - - if (validationResult != null && !validationResult.isPassing()) { - throw new CommandExecutionException(ExitCode.FAIL); - } } } } diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ConvertContentUsingModuleCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ConvertContentUsingModuleCommand.java index 632d393d2..4b6bdc298 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ConvertContentUsingModuleCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ConvertContentUsingModuleCommand.java @@ -35,7 +35,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; -public class ConvertContentUsingModuleCommand +/** + * This command implementation supports the conversion of a content instance + * between supported formats based on a provided Metaschema module. + */ +class ConvertContentUsingModuleCommand extends AbstractConvertSubcommand { @NonNull private static final String COMMAND = "convert"; @@ -80,7 +84,7 @@ private CommandExecutor( protected IBindingContext getBindingContext() throws CommandExecutionException { IBindingContext retval = MetaschemaCommands.newBindingContextWithDynamicCompilation(); - IModule module = MetaschemaCommands.handleModule( + IModule module = MetaschemaCommands.loadModule( getCommandLine(), MetaschemaCommands.METASCHEMA_REQUIRED_OPTION, ObjectUtils.notNull(getCurrentWorkingDirectory().toUri()), diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateDiagramCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateDiagramCommand.java index 267a413f5..513265be8 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateDiagramCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateDiagramCommand.java @@ -38,7 +38,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -public class GenerateDiagramCommand +/** + * This command implementation supports generation of a diagram depicting the + * objects and relationships within a provided Metaschema module. + */ +class GenerateDiagramCommand extends AbstractTerminalCommand { private static final Logger LOGGER = LogManager.getLogger(GenerateDiagramCommand.class); @@ -66,8 +70,7 @@ public String getDescription() { @SuppressWarnings("null") @Override public Collection gatherOptions() { - return List.of( - MetaschemaCommands.OVERWRITE_OPTION); + return List.of(MetaschemaCommands.OVERWRITE_OPTION); } @Override @@ -87,7 +90,8 @@ public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine c * information about the calling context * @param cmdLine * the parsed command line details - * @return the execution result + * @throws CommandExecutionException + * if an error occurred while executing the command */ @SuppressWarnings({ "PMD.OnlyOneReturn", // readability @@ -119,7 +123,7 @@ protected void executeCommand( ex.getLocalizedMessage()), ex); } - IModule module = MetaschemaCommands.handleModule(moduleUri, bindingContext); + IModule module = MetaschemaCommands.loadModule(moduleUri, bindingContext); if (destination == null) { Writer stringWriter = new StringWriter(); diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateSchemaCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateSchemaCommand.java index d05e780cd..ba91e4b69 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateSchemaCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateSchemaCommand.java @@ -7,9 +7,6 @@ import gov.nist.secauto.metaschema.cli.processor.CLIProcessor.CallingContext; import gov.nist.secauto.metaschema.cli.processor.ExitCode; -import gov.nist.secauto.metaschema.cli.processor.ExitStatus; -import gov.nist.secauto.metaschema.cli.processor.InvalidArgumentException; -import gov.nist.secauto.metaschema.cli.processor.OptionUtils; import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand; import gov.nist.secauto.metaschema.cli.processor.command.CommandExecutionException; import gov.nist.secauto.metaschema.cli.processor.command.DefaultExtraArgument; @@ -34,22 +31,26 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Collection; import java.util.List; import edu.umd.cs.findbugs.annotations.NonNull; -public class GenerateSchemaCommand +/** + * This command implementation supports generation of schemas in a variety of + * formats based on a provided Metaschema module. + */ +class GenerateSchemaCommand extends AbstractTerminalCommand { private static final Logger LOGGER = LogManager.getLogger(GenerateSchemaCommand.class); @NonNull private static final String COMMAND = "generate-schema"; @NonNull - private static final List EXTRA_ARGUMENTS; + private static final List EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of( + new DefaultExtraArgument("metaschema-module-file-or-URL", true), + new DefaultExtraArgument("destination-schema-file", false))); private static final Option INLINE_TYPES_OPTION = ObjectUtils.notNull( Option.builder() @@ -57,12 +58,6 @@ public class GenerateSchemaCommand .desc("definitions declared inline will be generated as inline types") .build()); - static { - EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of( - new DefaultExtraArgument("metaschema-module-file-or-URL", true), - new DefaultExtraArgument("destination-schema-file", false))); - } - @Override public String getName() { return COMMAND; @@ -87,75 +82,37 @@ public List getExtraArguments() { return EXTRA_ARGUMENTS; } - @SuppressWarnings("PMD.PreserveStackTrace") // intended - @Override - public void validateOptions(CallingContext callingContext, CommandLine cmdLine) throws InvalidArgumentException { - List extraArgs = cmdLine.getArgList(); - if (extraArgs.isEmpty() || extraArgs.size() > 2) { - throw new InvalidArgumentException("Illegal number of arguments."); - } - } - @Override public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine cmdLine) { return ICommandExecutor.using(callingContext, cmdLine, this::executeCommand); } /** - * Called to execute the schema generation. + * Execute the schema generation operation. * * @param callingContext * the context information for the execution * @param cmdLine * the parsed command line details - * @return the execution result * @throws CommandExecutionException + * if an error occurred while determining the source format */ @SuppressWarnings({ - "PMD.OnlyOneReturn" // readability + "PMD.OnlyOneReturn", // readability + "PMD.CyclomaticComplexity" }) - protected ExitStatus executeCommand( + protected void executeCommand( @NonNull CallingContext callingContext, @NonNull CommandLine cmdLine) throws CommandExecutionException { List extraArgs = cmdLine.getArgList(); - Path destination = null; - if (extraArgs.size() > 1) { - destination = Paths.get(extraArgs.get(1)).toAbsolutePath(); - } + Path destination = extraArgs.size() > 1 + ? MetaschemaCommands.handleDestination( + ObjectUtils.requireNonNull(extraArgs.get(1)), + cmdLine) + : null; - if (destination != null) { - if (Files.exists(destination)) { - if (!cmdLine.hasOption(MetaschemaCommands.OVERWRITE_OPTION)) { - return ExitCode.INVALID_ARGUMENTS.exitMessage( // NOPMD readability - String.format("The provided destination '%s' already exists and the '%s' option was not provided.", - destination, - OptionUtils.toArgument(MetaschemaCommands.OVERWRITE_OPTION))); - } - if (!Files.isWritable(destination)) { - return ExitCode.IO_ERROR.exitMessage( // NOPMD readability - "The provided destination '" + destination + "' is not writable."); - } - } else { - Path parent = destination.getParent(); - if (parent != null) { - try { - Files.createDirectories(parent); - } catch (IOException ex) { - return ExitCode.INVALID_TARGET.exit().withThrowable(ex); // NOPMD readability - } - } - } - } - - SchemaFormat asFormat; - try { - asFormat = MetaschemaCommands.getSchemaFormat(cmdLine, MetaschemaCommands.AS_SCHEMA_FORMAT_OPTION); - } catch (InvalidArgumentException ex) { - return ExitCode.INVALID_ARGUMENTS - .exitMessage(ex.getLocalizedMessage()) - .withThrowable(ex); - } + SchemaFormat asFormat = MetaschemaCommands.getSchemaFormat(cmdLine, MetaschemaCommands.AS_SCHEMA_FORMAT_OPTION); IMutableConfiguration> configuration = new DefaultConfiguration<>(); if (cmdLine.hasOption(INLINE_TYPES_OPTION)) { @@ -168,7 +125,7 @@ protected ExitStatus executeCommand( } IBindingContext bindingContext = MetaschemaCommands.newBindingContextWithDynamicCompilation(); - IModule module = MetaschemaCommands.handleModule( + IModule module = MetaschemaCommands.loadModule( ObjectUtils.requireNonNull(extraArgs.get(0)), ObjectUtils.notNull(getCurrentWorkingDirectory().toUri()), bindingContext); @@ -191,11 +148,10 @@ protected ExitStatus executeCommand( ISchemaGenerator.generateSchema(module, destination, asFormat, configuration); } } catch (IOException ex) { - return ExitCode.PROCESSING_ERROR.exit().withThrowable(ex); // NOPMD readability + throw new CommandExecutionException(ExitCode.PROCESSING_ERROR, ex); } if (destination != null && LOGGER.isInfoEnabled()) { LOGGER.info("Generated {} schema file: {}", asFormat.toString(), destination); } - return ExitCode.OK.exit(); } } diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/MetaschemaCommands.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/MetaschemaCommands.java index 88fd6c59f..f009ecf59 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/MetaschemaCommands.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/MetaschemaCommands.java @@ -7,7 +7,6 @@ import gov.nist.secauto.metaschema.cli.commands.metapath.MetapathCommand; import gov.nist.secauto.metaschema.cli.processor.ExitCode; -import gov.nist.secauto.metaschema.cli.processor.InvalidArgumentException; import gov.nist.secauto.metaschema.cli.processor.OptionUtils; import gov.nist.secauto.metaschema.cli.processor.command.CommandExecutionException; import gov.nist.secauto.metaschema.cli.processor.command.ICommand; @@ -18,6 +17,7 @@ import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.CustomCollectors; +import gov.nist.secauto.metaschema.core.util.DeleteOnShutdown; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.core.util.UriUtils; import gov.nist.secauto.metaschema.databind.IBindingContext; @@ -44,7 +44,21 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * This class provides a variety of utility methods for processing + * Metaschema-related commands. + *

+ * These methods handle the errors produced using the + * {@link CommandExecutionException}, which will return an exceptional result to + * the command line interface (CLI) processor. This approach keeps the command + * implementations fairly clean and simple. + */ +@SuppressWarnings("PMD.GodClass") public final class MetaschemaCommands { + /** + * A list of the Metaschema-related command pathways, for reuse in this and + * other CLI applications. + */ @NonNull public static final List COMMANDS = ObjectUtils.notNull(List.of( new ValidateModuleCommand(), @@ -54,6 +68,11 @@ public final class MetaschemaCommands { new ConvertContentUsingModuleCommand(), new MetapathCommand())); + /** + * Used by commands to declare a required Metaschema module for processing. + * + * @since 2.0.0 + */ @NonNull public static final Option METASCHEMA_REQUIRED_OPTION = ObjectUtils.notNull( Option.builder("m") @@ -63,6 +82,11 @@ public final class MetaschemaCommands { .desc("metaschema resource") .numberOfArgs(1) .build()); + /** + * Used by commands to declare an optional Metaschema module for processing. + * + * @since 2.0.0 + */ @NonNull public static final Option METASCHEMA_OPTIONAL_OPTION = ObjectUtils.notNull( Option.builder("m") @@ -71,12 +95,22 @@ public final class MetaschemaCommands { .desc("metaschema resource") .numberOfArgs(1) .build()); + /** + * Used by commands to protect existing files from being overwritten, unless + * this option is provided. + */ @NonNull public static final Option OVERWRITE_OPTION = ObjectUtils.notNull( Option.builder() .longOpt("overwrite") .desc("overwrite the destination if it exists") .build()); + /** + * Used by commands to identify the target format for a content conversion + * operation. + * + * @since 2.0.0 + */ @NonNull public static final Option TO_OPTION = ObjectUtils.notNull( Option.builder() @@ -88,6 +122,12 @@ public final class MetaschemaCommands { .collect(CustomCollectors.joiningWithOxfordComma("or"))) .numberOfArgs(1) .build()); + /** + * Used by commands to identify the source format for a content-related + * operation. + * + * @since 2.0.0 + */ @NonNull public static final Option AS_FORMAT_OPTION = ObjectUtils.notNull( Option.builder() @@ -99,6 +139,12 @@ public final class MetaschemaCommands { .collect(CustomCollectors.joiningWithOxfordComma("or"))) .numberOfArgs(1) .build()); + /** + * Used by commands that produce schemas to identify the schema format to + * produce. + * + * @since 2.0.0 + */ @NonNull public static final Option AS_SCHEMA_FORMAT_OPTION = ObjectUtils.notNull( Option.builder() @@ -124,6 +170,7 @@ public final class MetaschemaCommands { * @return the absolute URI for the resource * @throws CommandExecutionException * if the resulting URI is not a well-formed URI + * @since 2.0.0 */ @NonNull public static URI handleSource( @@ -156,6 +203,7 @@ public static URI handleSource( * @return the absolute URI for the resource * @throws CommandExecutionException * if the path exists and cannot be overwritten or is not writable + * @since 2.0.0 */ public static Path handleDestination( @NonNull String path, @@ -202,14 +250,15 @@ public static Path handleDestination( * @return the format * @throws CommandExecutionException * if the format option was not provided or was an invalid choice + * @since 2.0.0 */ @SuppressWarnings("PMD.PreserveStackTrace") @NonNull public static Format getFormat( - @NonNull CommandLine cmdLine, + @NonNull CommandLine commandLine, @NonNull Option option) throws CommandExecutionException { // use the option - String toFormatText = cmdLine.getOptionValue(option); + String toFormatText = commandLine.getOptionValue(option); if (toFormatText == null) { throw new CommandExecutionException( ExitCode.INVALID_ARGUMENTS, @@ -234,33 +283,81 @@ public static Format getFormat( } /** + * Parse the command line options to get the selected schema format. * - * @param cmdLine + * @param commandLine + * the provided command line argument information * @param option + * the option specifying the format, which must be present on the + * command line + * @return the format + * @throws CommandExecutionException + * if the format option was not provided or was an invalid choice + * @since 2.0.0 + */ + @SuppressWarnings("PMD.PreserveStackTrace") + @NonNull + public static SchemaFormat getSchemaFormat( + @NonNull CommandLine commandLine, + @NonNull Option option) throws CommandExecutionException { + // use the option + String toFormatText = commandLine.getOptionValue(option); + if (toFormatText == null) { + throw new CommandExecutionException( + ExitCode.INVALID_ARGUMENTS, + String.format("Option '%s' not provided.", + option.hasLongOpt() + ? "--" + option.getLongOpt() + : "-" + option.getOpt())); + } + try { + return SchemaFormat.valueOf(toFormatText.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException ex) { + throw new CommandExecutionException( + ExitCode.INVALID_ARGUMENTS, + String.format("Invalid '%s' argument. The schema format must be one of: %s.", + option.hasLongOpt() + ? "--" + option.getLongOpt() + : "-" + option.getOpt(), + Arrays.stream(SchemaFormat.values()) + .map(Enum::name) + .collect(CustomCollectors.joiningWithOxfordComma("or"))), + ex); + } + } + + /** + * Detect the source format for content identified using the provided option. + *

+ * This method will first check if the source format is explicitly declared on + * the command line. If so, this format will be returned. + *

+ * If not, then the content will be analyzed to determine the format. + * + * @param commandLine + * the provided command line argument information + * @param option + * the option specifying the format, which must be present on the + * command line * @param loader + * the content loader to use to load the content instance * @param resource - * @return - * @throws InvalidArgumentException - * if the option is not a supported format - * @throws FileNotFoundException - * if the URI is a file that was not found - * @throws IOException - * if there was an error reading the file to determine the format of - * the file - * @throws IllegalArgumentException - * if the format of the source file was not recognized + * the resource to load + * @return the identified content format + * @throws CommandExecutionException + * if an error occurred while determining the source format * @since 2.0.0 */ @SuppressWarnings({ "PMD.PreserveStackTrace", "PMD.OnlyOneReturn" }) @NonNull public static Format determineSourceFormat( - @NonNull CommandLine cmdLine, + @NonNull CommandLine commandLine, @NonNull Option option, @NonNull IBoundLoader loader, @NonNull URI resource) throws CommandExecutionException { - if (cmdLine.hasOption(option)) { + if (commandLine.hasOption(option)) { // use the option - return getFormat(cmdLine, option); + return getFormat(commandLine, option); } // attempt to determine the format @@ -284,11 +381,29 @@ public static Format determineSourceFormat( } } + /** + * Load a Metaschema module based on the provided command line option. + * + * @param commandLine + * the provided command line argument information + * @param option + * the option specifying the module to load, which must be present on + * the command line + * @param currentWorkingDirectory + * the URI of the current working directory + * @param bindingContext + * the context used to access Metaschema module information based on + * Java class bindings + * @return the loaded module + * @throws CommandExecutionException + * if an error occurred while loading the module + * @since 2.0.0 + */ @NonNull - public static IModule handleModule( + public static IModule loadModule( @NonNull CommandLine commandLine, @NonNull Option option, - @NonNull URI cwd, + @NonNull URI currentWorkingDirectory, @NonNull IBindingContext bindingContext) throws CommandExecutionException { String moduleName = commandLine.getOptionValue(option); if (moduleName == null) { @@ -302,7 +417,7 @@ public static IModule handleModule( URI moduleUri; try { - moduleUri = UriUtils.toUri(moduleName, cwd); + moduleUri = UriUtils.toUri(moduleName, currentWorkingDirectory); } catch (URISyntaxException ex) { throw new CommandExecutionException( ExitCode.INVALID_ARGUMENTS, @@ -311,11 +426,29 @@ public static IModule handleModule( ex.getLocalizedMessage()), ex); } - return handleModule(moduleUri, bindingContext); + return loadModule(moduleUri, bindingContext); } + /** + * Load a Metaschema module from the provided relative resource path. + *

+ * This method will resolve the provided resource against the current working + * directory to create an absolute URI. + * + * @param moduleResource + * the relative path to the module resource to load + * @param currentWorkingDirectory + * the URI of the current working directory + * @param bindingContext + * the context used to access Metaschema module information based on + * Java class bindings + * @return the loaded module + * @throws CommandExecutionException + * if an error occurred while loading the module + * @since 2.0.0 + */ @NonNull - public static IModule handleModule( + public static IModule loadModule( @NonNull String moduleResource, @NonNull URI currentWorkingDirectory, @NonNull IBindingContext bindingContext) throws CommandExecutionException { @@ -323,7 +456,7 @@ public static IModule handleModule( URI moduleUri = getResourceUri( moduleResource, currentWorkingDirectory); - return handleModule(moduleUri, bindingContext); + return loadModule(moduleUri, bindingContext); } catch (URISyntaxException ex) { throw new CommandExecutionException( ExitCode.INVALID_ARGUMENTS, @@ -334,10 +467,24 @@ public static IModule handleModule( } } + /** + * Load a Metaschema module from the provided resource path. + * + * @param moduleResource + * the absolute path to the module resource to load + * @param bindingContext + * the context used to access Metaschema module information based on + * Java class bindings + * @return the loaded module + * @throws CommandExecutionException + * if an error occurred while loading the module + * @since 2.0.0 + */ @NonNull - public static IModule handleModule( + public static IModule loadModule( @NonNull URI moduleResource, @NonNull IBindingContext bindingContext) throws CommandExecutionException { + // TODO: ensure the resource URI is absolute try { IBindingModuleLoader loader = bindingContext.newModuleLoader(); loader.allowEntityResolution(); @@ -352,7 +499,7 @@ public static IModule handleModule( * * @param location * the resource location - * @param cwd + * @param currentWorkingDirectory * the URI of the current working directory * @return the resolved URI * @throws URISyntaxException @@ -361,24 +508,40 @@ public static IModule handleModule( @NonNull public static URI getResourceUri( @NonNull String location, - @NonNull URI cwd) throws URISyntaxException { - return UriUtils.toUri(location, cwd); + @NonNull URI currentWorkingDirectory) throws URISyntaxException { + return UriUtils.toUri(location, currentWorkingDirectory); } + /** + * Load a set of external Metaschema module constraints based on the provided + * command line option. + * + * @param commandLine + * the provided command line argument information + * @param option + * the option specifying the constraints to load, which must be present + * on the command line + * @param currentWorkingDirectory + * the URI of the current working directory + * @return the set of loaded constraints + * @throws CommandExecutionException + * if an error occurred while loading the module + * @since 2.0.0 + */ @NonNull public static Set loadConstraintSets( - @NonNull CommandLine cmdLine, + @NonNull CommandLine commandLine, @NonNull Option option, - @NonNull URI cwd) throws CommandExecutionException { + @NonNull URI currentWorkingDirectory) throws CommandExecutionException { Set constraintSets; - if (cmdLine.hasOption(option)) { + if (commandLine.hasOption(option)) { IConstraintLoader constraintLoader = IBindingContext.getConstraintLoader(); constraintSets = new LinkedHashSet<>(); - String[] args = cmdLine.getOptionValues(option); + String[] args = commandLine.getOptionValues(option); for (String arg : args) { assert arg != null; try { - URI constraintUri = ObjectUtils.requireNonNull(UriUtils.toUri(arg, cwd)); + URI constraintUri = ObjectUtils.requireNonNull(UriUtils.toUri(arg, currentWorkingDirectory)); constraintSets.addAll(constraintLoader.load(constraintUri)); } catch (URISyntaxException | IOException | MetaschemaException | MetapathException ex) { throw new CommandExecutionException( @@ -395,24 +558,52 @@ public static Set loadConstraintSets( return constraintSets; } + /** + * Create a temporary directory for ephemeral files that will be deleted on + * shutdown. + * + * @return the temp directory path + * @throws IOException + * if an error occurred while creating the temporary directory + */ @NonNull public static Path newTempDir() throws IOException { Path retval = Files.createTempDirectory("metaschema-cli-"); - retval.toFile().deleteOnExit(); - return retval; + DeleteOnShutdown.register(retval); + return ObjectUtils.notNull(retval); } + /** + * Create a new {@link IBindingContext} that is configured for dynamic + * compilation. + * + * @return the binding context + * @throws CommandExecutionException + * if an error occurred while creating the binding context + * @since 2.0.0 + */ @NonNull public static IBindingContext newBindingContextWithDynamicCompilation() throws CommandExecutionException { return newBindingContextWithDynamicCompilation(CollectionUtil.emptySet()); } + /** + * Create a new {@link IBindingContext} that is configured for dynamic + * compilation and to use the provided constraints. + * + * @param constraintSets + * the Metaschema module constraints to dynamicly bind to loaded + * modules + * @return the binding context + * @throws CommandExecutionException + * if an error occurred while creating the binding context + * @since 2.0.0 + */ @NonNull public static IBindingContext newBindingContextWithDynamicCompilation(@NonNull Set constraintSets) throws CommandExecutionException { try { Path tempDir = newTempDir(); - tempDir.toFile().deleteOnExit(); return IBindingContext.builder() .compilePath(tempDir) .constraintSet(constraintSets) @@ -424,34 +615,6 @@ public static IBindingContext newBindingContextWithDynamicCompilation(@NonNull S } } - @SuppressWarnings("PMD.PreserveStackTrace") - @NonNull - public static SchemaFormat getSchemaFormat( - @NonNull CommandLine cmdLine, - @NonNull Option option) throws InvalidArgumentException { - // use the option - String toFormatText = cmdLine.getOptionValue(option); - if (toFormatText == null) { - throw new IllegalArgumentException( - String.format("Option '%s' not provided.", - option.hasLongOpt() - ? "--" + option.getLongOpt() - : "-" + option.getOpt())); - } - try { - return SchemaFormat.valueOf(toFormatText.toUpperCase(Locale.ROOT)); - } catch (IllegalArgumentException ex) { - throw new InvalidArgumentException( - String.format("Invalid '%s' argument. The schema format must be one of: %s.", - option.hasLongOpt() - ? "--" + option.getLongOpt() - : "-" + option.getOpt(), - Arrays.stream(SchemaFormat.values()) - .map(Enum::name) - .collect(CustomCollectors.joiningWithOxfordComma("or")))); - } - } - private MetaschemaCommands() { // disable construction } diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateContentUsingModuleCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateContentUsingModuleCommand.java index 87b091a42..300c3413d 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateContentUsingModuleCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateContentUsingModuleCommand.java @@ -42,7 +42,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; -public class ValidateContentUsingModuleCommand +/** + * This command implementation supports validation of a content instance based + * on a provided Metaschema module. + */ +class ValidateContentUsingModuleCommand extends AbstractValidateContentCommand { @NonNull private static final String COMMAND = "validate-content"; @@ -92,7 +96,7 @@ protected IBindingContext getBindingContext(@NonNull Set constra protected IModule getModule( CommandLine commandLine, IBindingContext bindingContext) throws CommandExecutionException { - return MetaschemaCommands.handleModule( + return MetaschemaCommands.loadModule( commandLine, MetaschemaCommands.METASCHEMA_REQUIRED_OPTION, ObjectUtils.notNull(getCurrentWorkingDirectory().toUri()), diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateModuleCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateModuleCommand.java index cecd2f474..22400c6bc 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateModuleCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateModuleCommand.java @@ -38,7 +38,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; import nl.talsmasoftware.lazy4j.Lazy; -public class ValidateModuleCommand +/** + * This command implementation supports validation a Metaschema module. + */ +class ValidateModuleCommand extends AbstractValidateContentCommand { @NonNull private static final String COMMAND = "validate"; @@ -55,14 +58,14 @@ public String getDescription() { @Override public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine commandLine) { - return new ValidateModuleCommandExecutor(callingContext, commandLine); + return new CommandExecutor(callingContext, commandLine); } - private final class ValidateModuleCommandExecutor + private final class CommandExecutor extends AbstractValidationCommandExecutor { private final Lazy validationProvider = Lazy.lazy(ValidationProvider::new); - private ValidateModuleCommandExecutor( + private CommandExecutor( @NonNull CallingContext callingContext, @NonNull CommandLine commandLine) { super(callingContext, commandLine); diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathCommand.java index 2c1f9b4e5..5ab8be68c 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathCommand.java @@ -8,7 +8,6 @@ import gov.nist.secauto.metaschema.cli.commands.MetaschemaCommands; import gov.nist.secauto.metaschema.cli.processor.CLIProcessor.CallingContext; import gov.nist.secauto.metaschema.cli.processor.ExitCode; -import gov.nist.secauto.metaschema.cli.processor.InvalidArgumentException; import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand; import gov.nist.secauto.metaschema.cli.processor.command.CommandExecutionException; import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument; @@ -44,7 +43,21 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -public class EvaluateMetapathCommand +/** + * This command implementation executes a Metapath query. + *

+ * The query is executed using one of the following configurations: + *

    + *
  1. module and content: on a content instance parsed using a provided + * Metaschema module,
  2. + *
  3. module-only: against the Metaschema module itself if no content + * instance is provided, or
  4. + *
  5. without content or module: if both a module and content are + * omitted then the execution will be limited to operations that do not act on + * content.
  6. + *
+ */ +class EvaluateMetapathCommand extends AbstractTerminalCommand { private static final Logger LOGGER = LogManager.getLogger(EvaluateMetapathCommand.class); @@ -91,14 +104,6 @@ public List getExtraArguments() { return CollectionUtil.emptyList(); } - @Override - public void validateOptions(CallingContext callingContext, CommandLine cmdLine) throws InvalidArgumentException { - List extraArgs = cmdLine.getArgList(); - if (!extraArgs.isEmpty()) { - throw new InvalidArgumentException("Illegal number of extra arguments."); - } - } - @Override public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine cmdLine) { return ICommandExecutor.using(callingContext, cmdLine, this::executeCommand); @@ -106,7 +111,10 @@ public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine c @SuppressWarnings({ "PMD.OnlyOneReturn", // readability - "PMD.AvoidCatchingGenericException" + "PMD.AvoidCatchingGenericException", + "PMD.NPathComplexity", + "PMD.CognitiveComplexity", + "PMD.CyclomaticComplexity" }) @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Catching generic exception for CLI error handling") @@ -119,12 +127,11 @@ private void executeCommand( if (cmdLine.hasOption(MetaschemaCommands.METASCHEMA_OPTIONAL_OPTION)) { IBindingContext bindingContext = MetaschemaCommands.newBindingContextWithDynamicCompilation(); - module = MetaschemaCommands.handleModule( + module = bindingContext.registerModule(MetaschemaCommands.loadModule( cmdLine, MetaschemaCommands.METASCHEMA_OPTIONAL_OPTION, ObjectUtils.notNull(getCurrentWorkingDirectory().toUri()), - bindingContext); - bindingContext.registerModule(module); + bindingContext)); // determine if the query is evaluated against the module or the instance if (cmdLine.hasOption(CONTENT_OPTION)) { @@ -158,16 +165,18 @@ private void executeCommand( ex); } } else { + // evaluate against the module item = INodeItemFactory.instance().newModuleNodeItem(module); } } else if (cmdLine.hasOption(CONTENT_OPTION)) { // content provided, but no module; require module - String contentLocation = ObjectUtils.requireNonNull(cmdLine.getOptionValue(CONTENT_OPTION)); throw new CommandExecutionException( ExitCode.INVALID_ARGUMENTS, - String.format("Must use '%s' to specify the Metaschema module.", CONTENT_OPTION.getArgName())); + String.format("Must use '%s' to specify the Metaschema module.", + CONTENT_OPTION.getArgName())); } + // now setup to evaluate the metapath StaticContext.Builder builder = StaticContext.builder(); if (module != null) { builder.defaultModelNamespace(module.getXmlNamespace()); @@ -186,6 +195,7 @@ private void executeCommand( MetapathExpression compiledMetapath = MetapathExpression.compile(expression, staticContext); ISequence sequence = compiledMetapath.evaluate(item, new DynamicContext(staticContext)); + // handle the metapath results try (Writer stringWriter = new StringWriter()) { try (PrintWriter writer = new PrintWriter(stringWriter)) { try (IItemWriter itemWriter = new DefaultItemWriter(writer)) { diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/ListFunctionsSubcommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/ListFunctionsSubcommand.java index cc597da12..2537725f8 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/ListFunctionsSubcommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/ListFunctionsSubcommand.java @@ -28,7 +28,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; -public class ListFunctionsSubcommand +/** + * This command list the Metapath functions currently provided by the Metaschema + * runtime. + */ +class ListFunctionsSubcommand extends AbstractTerminalCommand { private static final Logger LOGGER = LogManager.getLogger(ListFunctionsSubcommand.class); @@ -61,7 +65,8 @@ public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine c */ @SuppressWarnings({ "PMD.OnlyOneReturn", // readability - "PMD.AvoidInstantiatingObjectsInLoops" + "PMD.AvoidInstantiatingObjectsInLoops", + "PMD.CognitiveComplexity" }) protected ExitStatus executeCommand( @NonNull CallingContext callingContext, diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/MetapathCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/MetapathCommand.java index 903fd1590..0d1a0d79c 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/MetapathCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/MetapathCommand.java @@ -7,6 +7,10 @@ import gov.nist.secauto.metaschema.cli.processor.command.AbstractParentCommand; +/** + * This sub-command implementation contains all command that relate to Metapath + * execution. + */ public class MetapathCommand extends AbstractParentCommand { private static final String COMMAND = "metapath"; diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/util/LoggingValidationHandler.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/util/LoggingValidationHandler.java index 0fe51e7eb..16ac89d9c 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/util/LoggingValidationHandler.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/util/LoggingValidationHandler.java @@ -26,6 +26,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +/** + * Supports logging validation findings to the console using ANSI color codes to + * improve the visibility of warnings and errors. + */ public final class LoggingValidationHandler extends AbstractValidationResultProcessor { private static final Logger LOGGER = LogManager.getLogger(LoggingValidationHandler.class); diff --git a/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/CLITest.java b/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/CLITest.java index 27efe0397..e9b7e8a79 100644 --- a/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/CLITest.java +++ b/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/CLITest.java @@ -39,14 +39,13 @@ void evaluateResult(@NonNull ExitStatus status, @NonNull ExitCode expectedCode, @NonNull Class thrownClass) { status.generateMessage(true); Throwable thrown = status.getThrowable(); - assert thrown != null; - assertAll(() -> assertEquals(expectedCode, status.getExitCode(), "exit code mismatch"), - () -> assertEquals(thrownClass, thrown.getClass(), "expected Throwable mismatch")); + assertAll( + () -> assertEquals(expectedCode, status.getExitCode(), "exit code mismatch"), + () -> assertEquals(thrownClass, thrown == null ? null : thrown.getClass(), "expected Throwable mismatch")); } private static Stream providesValues() { - @SuppressWarnings("serial") - List values = new LinkedList<>() { + @SuppressWarnings("serial") List values = new LinkedList<>() { { add(Arguments.of(new String[] {}, ExitCode.INVALID_COMMAND, NO_EXCEPTION_CLASS)); @@ -159,7 +158,7 @@ private static Stream providesValues() { "--disable-schema-validation" }, // fail due to missing element during parsing - ExitCode.IO_ERROR, java.io.IOException.class)); + ExitCode.FAIL, NO_EXCEPTION_CLASS)); add(Arguments.of( new String[] { "validate-content", "-m", @@ -181,15 +180,6 @@ private static Stream providesValues() { "../core/metaschema/schema/metaschema/metaschema-module-metaschema.xml", }, ExitCode.OK, NO_EXCEPTION_CLASS)); - add(Arguments.of( - new String[] { "validate-content", - "-m", - "src/test/resources/content/215-module.xml", - "src/test/resources/content/215.xml", - "--disable-schema-validation", - "--show-stack-trace" - }, - ExitCode.FAIL, NO_EXCEPTION_CLASS)); } }; @@ -214,10 +204,10 @@ void testAllCommands(@NonNull String[] args, @NonNull ExitCode expectedExitCode, void test() { String[] cliArgs = { "validate-content", "-m", - "src/test/resources/content/215-module.xml", - "src/test/resources/content/215.xml", - "--disable-schema-validation", - "--show-stack-trace" + "src/test/resources/content/schema-validation-module.xml", + "src/test/resources/content/schema-validation-module-missing-required.xml", + "--as=xml", + "--disable-schema-validation" }; CLI.runCli(cliArgs); } diff --git a/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathSubCommandTest.java b/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathSubCommandTest.java index b307f3fd1..9949e77fd 100644 --- a/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathSubCommandTest.java +++ b/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/commands/metapath/EvaluateMetapathSubCommandTest.java @@ -11,13 +11,13 @@ import org.junit.jupiter.api.Test; -import nl.altindag.console.ConsoleCaptor; +import nl.altindag.log.LogCaptor; class EvaluateMetapathSubCommandTest { @Test void test() { - try (ConsoleCaptor consoleCaptor = new ConsoleCaptor()) { + try (LogCaptor captor = LogCaptor.forRoot()) { String[] args = { "metapath", @@ -26,7 +26,7 @@ void test() { "3 + 4 + 5", "--show-stack-trace" }; CLI.runCli(args); - assertThat(consoleCaptor.getStandardOutput()).contains("12"); + assertThat(captor.getInfoLogs().contains("12")); } } } diff --git a/metaschema-maven-plugin/pom.xml b/metaschema-maven-plugin/pom.xml index 6646e9a95..2ebf37fdd 100644 --- a/metaschema-maven-plugin/pom.xml +++ b/metaschema-maven-plugin/pom.xml @@ -95,6 +95,14 @@ + + + src/main/resources + + + ${project.build.directory}/generated-resources/maven-plugin + + @@ -129,6 +137,11 @@ org.apache.maven.plugins maven-plugin-plugin + + metaschema + true + ${project.build.directory}/classes/META-INF/maven + default-addPluginArtifactMetadata @@ -138,21 +151,37 @@ - default-descriptor - process-classes + help-mojo - descriptor + helpmojo - metaschema - false + ${project.build.directory}/generated-sources/maven-plugin - help-mojo + default-descriptor - helpmojo + descriptor + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-generated-sources + generate-sources + + add-source + + + ${project.build.directory}/generated-sources/maven-plugin + + diff --git a/metaschema-maven-plugin/src/main/java/gov/nist/secauto/metaschema/maven/plugin/AbstractMetaschemaMojo.java b/metaschema-maven-plugin/src/main/java/gov/nist/secauto/metaschema/maven/plugin/AbstractMetaschemaMojo.java index ce70fb581..b3dea218e 100644 --- a/metaschema-maven-plugin/src/main/java/gov/nist/secauto/metaschema/maven/plugin/AbstractMetaschemaMojo.java +++ b/metaschema-maven-plugin/src/main/java/gov/nist/secauto/metaschema/maven/plugin/AbstractMetaschemaMojo.java @@ -617,7 +617,7 @@ public IProduction generateClasses(@NonNull IModule module) { return production; } - public void compileClasses(@NonNull IProduction production, @NonNull Path classDir) + private void compileClasses(@NonNull IProduction production, @NonNull Path classDir) throws IOException, DependencyResolutionRequiredException { List classesToCompile = production.getGeneratedClasses().collect(Collectors.toList()); diff --git a/pom.xml b/pom.xml index bb7994603..6790b7552 100644 --- a/pom.xml +++ b/pom.xml @@ -41,7 +41,6 @@ 3.8.1 4.13.2 - 1.1.1 3.26.3 1.9.0 4.4 @@ -177,12 +176,6 @@ cli-processor ${project.version}
- - com.google.auto.service - auto-service-annotations - ${dependency.auto-service.version} - provided - org.apache.xmlbeans xmlbeans @@ -515,24 +508,11 @@ org.apache.maven.plugins maven-compiler-plugin - - - - com.google.auto.service - auto-service - ${dependency.auto-service.version} - - - org.apache.maven.plugins maven-dependency-plugin - - - com.google.auto.service:auto-service - org.apache.logging.log4j:log4j-core diff --git a/schemagen/pom.xml b/schemagen/pom.xml index 6c214762d..b309c88d8 100644 --- a/schemagen/pom.xml +++ b/schemagen/pom.xml @@ -32,11 +32,6 @@ ${project.groupId} metaschema-databind - - - com.google.auto.service - auto-service-annotations - ${project.groupId} metaschema-testing