View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.metaschema.cli.commands;
28  
29  import gov.nist.secauto.metaschema.cli.processor.CLIProcessor;
30  import gov.nist.secauto.metaschema.cli.processor.CLIProcessor.CallingContext;
31  import gov.nist.secauto.metaschema.cli.processor.ExitCode;
32  import gov.nist.secauto.metaschema.cli.processor.ExitStatus;
33  import gov.nist.secauto.metaschema.cli.processor.InvalidArgumentException;
34  import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand;
35  import gov.nist.secauto.metaschema.cli.processor.command.DefaultExtraArgument;
36  import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument;
37  import gov.nist.secauto.metaschema.cli.processor.command.ICommandExecutor;
38  import gov.nist.secauto.metaschema.cli.util.LoggingValidationHandler;
39  import gov.nist.secauto.metaschema.core.model.util.XmlUtil;
40  import gov.nist.secauto.metaschema.core.model.validation.IContentValidator;
41  import gov.nist.secauto.metaschema.core.model.validation.IValidationResult;
42  import gov.nist.secauto.metaschema.core.model.xml.ModuleLoader;
43  import gov.nist.secauto.metaschema.core.util.CollectionUtil;
44  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
45  
46  import org.apache.commons.cli.CommandLine;
47  import org.apache.logging.log4j.LogManager;
48  import org.apache.logging.log4j.Logger;
49  import org.xml.sax.SAXException;
50  
51  import java.io.File;
52  import java.io.IOException;
53  import java.nio.file.Path;
54  import java.nio.file.Paths;
55  import java.util.LinkedList;
56  import java.util.List;
57  
58  import javax.xml.transform.Source;
59  
60  import edu.umd.cs.findbugs.annotations.NonNull;
61  
62  public class ValidateModuleCommand
63      extends AbstractTerminalCommand {
64    private static final Logger LOGGER = LogManager.getLogger(ValidateModuleCommand.class);
65    @NonNull
66    private static final String COMMAND = "validate";
67    @NonNull
68    private static final List<ExtraArgument> EXTRA_ARGUMENTS;
69  
70    static {
71      EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of(
72          new DefaultExtraArgument("Module file to validate", true)));
73    }
74  
75    @Override
76    public String getName() {
77      return COMMAND;
78    }
79  
80    @Override
81    public String getDescription() {
82      return "Validate that the specified Module is well-formed and valid to the Module model";
83    }
84  
85    @Override
86    public List<ExtraArgument> getExtraArguments() {
87      return EXTRA_ARGUMENTS;
88    }
89  
90    @NonNull
91    protected List<Source> getXmlSchemaSources() throws IOException {
92      List<Source> retval = new LinkedList<>();
93      retval.add(XmlUtil.getStreamSource(
94          ObjectUtils.requireNonNull(
95              ModuleLoader.class.getResource("/schema/xml/metaschema.xsd"),
96              "Unable to load '/schema/xml/metaschema.xsd' on the classpath")));
97      return CollectionUtil.unmodifiableList(retval);
98    }
99  
100   @Override
101   public void validateOptions(CallingContext callingContext, CommandLine cmdLine) throws InvalidArgumentException {
102     List<String> extraArgs = cmdLine.getArgList();
103     if (extraArgs.size() != 1) {
104       throw new InvalidArgumentException("The source to validate must be provided.");
105     }
106 
107     File target = new File(extraArgs.get(0));
108     if (!target.exists()) {
109       throw new InvalidArgumentException("The provided source file '" + target.getPath() + "' does not exist.");
110     }
111     if (!target.canRead()) {
112       throw new InvalidArgumentException("The provided source file '" + target.getPath() + "' is not readable.");
113     }
114   }
115 
116   @Override
117   public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine cmdLine) {
118     return ICommandExecutor.using(callingContext, cmdLine, this::executeCommand);
119   }
120 
121   @SuppressWarnings({ "PMD.OnlyOneReturn", "unused" }) // readability
122   @NonNull
123   protected ExitStatus executeCommand(CallingContext callingContext, CommandLine cmdLine) {
124     List<String> extraArgs = cmdLine.getArgList();
125     Path target = Paths.get(extraArgs.get(0));
126     assert target != null;
127 
128     IValidationResult schemaValidationResult;
129     try {
130       List<Source> schemaSources = getXmlSchemaSources();
131       schemaValidationResult = IContentValidator.validateWithXmlSchema(
132           ObjectUtils.notNull(target.toUri()),
133           schemaSources);
134     } catch (IOException | SAXException ex) {
135       return ExitCode.PROCESSING_ERROR.exit().withThrowable(ex);
136     }
137 
138     if (!schemaValidationResult.isPassing()) {
139       if (LOGGER.isErrorEnabled()) {
140         LOGGER.error("The file '{}' has validation issue(s). The issues are:", target);
141       }
142       LoggingValidationHandler.instance().handleValidationResults(schemaValidationResult);
143       return ExitCode.FAIL.exit();
144     }
145 
146     if (schemaValidationResult.isPassing() && !cmdLine.hasOption(CLIProcessor.QUIET_OPTION) && LOGGER.isInfoEnabled()) {
147       LOGGER.info("The file '{}' is valid.", target);
148     }
149     return ExitCode.OK.exit();
150   }
151 }