#40 Fix NPE in Result.getReportDocument for malformed xml input

This commit is contained in:
Andreas Penski (init) 2020-01-06 09:10:45 +01:00
parent b44603b55f
commit da70421620
9 changed files with 74 additions and 44 deletions

View file

@ -6,7 +6,7 @@
<name>KoSIT XML Prüftool Implementierung</name> <name>KoSIT XML Prüftool Implementierung</name>
<groupId>de.kosit</groupId> <groupId>de.kosit</groupId>
<version>1.1.1</version> <version>1.1.2-SNAPSHOT</version>
<artifactId>validationtool</artifactId> <artifactId>validationtool</artifactId>
<description>KoSIT XML Validator against XSD and Schematron based on defined scenarios.</description> <description>KoSIT XML Validator against XSD and Schematron based on defined scenarios.</description>
@ -19,7 +19,6 @@
<organizationUrl>http://www.xoev.de</organizationUrl> <organizationUrl>http://www.xoev.de</organizationUrl>
</developer> </developer>
<developer> <developer>
<id>renzo.kottmann</id> <id>renzo.kottmann</id>
<name>Renzo Kottmann</name> <name>Renzo Kottmann</name>
<organization>KoSIT</organization> <organization>KoSIT</organization>
@ -37,12 +36,6 @@
<role>developer</role> <role>developer</role>
</roles> </roles>
</developer> </developer>
<developer>
<id>fabian.buettner</id>
<name>Fabian Büttner</name>
<organization>KoSIT</organization>
<organizationUrl>http://www.xoev.de</organizationUrl>
</developer>
</developers> </developers>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

View file

@ -43,7 +43,6 @@ import de.kosit.validationtool.impl.tasks.SchematronValidationAction;
import de.kosit.validationtool.impl.tasks.ValidateReportInputAction; import de.kosit.validationtool.impl.tasks.ValidateReportInputAction;
import de.kosit.validationtool.model.reportInput.CreateReportInput; import de.kosit.validationtool.model.reportInput.CreateReportInput;
import de.kosit.validationtool.model.reportInput.EngineType; import de.kosit.validationtool.model.reportInput.EngineType;
import de.kosit.validationtool.model.reportInput.ProcessingError;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError; import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
import net.sf.saxon.s9api.Processor; import net.sf.saxon.s9api.Processor;
@ -116,12 +115,6 @@ public class DefaultCheck implements Check {
action.check(t); action.check(t);
} }
log.debug("Step {} finished in {}ms", action.getClass().getSimpleName(), System.currentTimeMillis() - start); log.debug("Step {} finished in {}ms", action.getClass().getSimpleName(), System.currentTimeMillis() - start);
if (t.isStopped()) {
final ProcessingError processingError = t.getReportInput().getProcessingError();
log.error("Error processing input {}: {}", t.getInput().getName(),
processingError != null ? String.join("\n", processingError.getError()) : "");
break;
}
} }
t.setFinished(true); t.setFinished(true);
log.info("Finished check of {} in {}ms\n", t.getInput().getName(), System.currentTimeMillis() - started); log.info("Finished check of {} in {}ms\n", t.getInput().getName(), System.currentTimeMillis() - started);

View file

@ -94,7 +94,7 @@ public abstract class BaseScenario {
* @return das passende Schema * @return das passende Schema
*/ */
public Schema getSchema() { public Schema getSchema() {
if (this.schema == null) { if (this.schema == null && getValidateWithXmlSchema() != null) {
final List<String> schemaResources = getValidateWithXmlSchema().getResource().stream().map(ResourceType::getLocation) final List<String> schemaResources = getValidateWithXmlSchema().getResource().stream().map(ResourceType::getLocation)
.collect(Collectors.toList()); .collect(Collectors.toList());
this.schema = this.repository.createSchema(schemaResources); this.schema = this.repository.createSchema(schemaResources);

View file

@ -22,6 +22,7 @@ package de.kosit.validationtool.impl.tasks;
import javax.xml.transform.dom.DOMSource; import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -31,9 +32,9 @@ import de.kosit.validationtool.impl.ConversionService;
import de.kosit.validationtool.impl.ObjectFactory; import de.kosit.validationtool.impl.ObjectFactory;
import de.kosit.validationtool.impl.RelativeUriResolver; import de.kosit.validationtool.impl.RelativeUriResolver;
import de.kosit.validationtool.impl.ScenarioRepository; import de.kosit.validationtool.impl.ScenarioRepository;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.model.scenarios.ScenarioType; import de.kosit.validationtool.model.scenarios.ScenarioType;
import net.sf.saxon.s9api.BuildingContentHandler;
import net.sf.saxon.s9api.DocumentBuilder; import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.Processor; import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName; import net.sf.saxon.s9api.QName;
@ -70,7 +71,7 @@ public class CreateReportAction implements CheckAction {
try { try {
final XdmNode parsedDocument = results.getParserResult().isValid() ? results.getParserResult().getObject() final XdmNode parsedDocument = results.getParserResult().isValid() ? results.getParserResult().getObject()
: ObjectFactory.createProcessor().newDocumentBuilder().newBuildingContentHandler().getDocumentNode(); : createEmpty();
final Document reportInput = this.conversionService.writeDocument(results.getReportInput()); final Document reportInput = this.conversionService.writeDocument(results.getReportInput());
final XdmNode root = documentBuilder.build(new DOMSource(reportInput)); final XdmNode root = documentBuilder.build(new DOMSource(reportInput));
@ -81,21 +82,27 @@ public class CreateReportAction implements CheckAction {
transformer.setMessageListener(e); transformer.setMessageListener(e);
transformer.setURIResolver(resolver); transformer.setURIResolver(resolver);
transformer.getUnderlyingController().setUnparsedTextURIResolver(resolver); transformer.getUnderlyingController().setUnparsedTextURIResolver(resolver);
transformer.setParameter(new QName("input-document"), parsedDocument); if (parsedDocument != null) {
transformer.setParameter(new QName("input-document"), parsedDocument);
}
final XdmDestination destination = new XdmDestination(); final XdmDestination destination = new XdmDestination();
transformer.setDestination(destination); transformer.setDestination(destination);
transformer.transform(); transformer.transform();
results.setReport(destination.getXdmNode()); results.setReport(destination.getXdmNode());
} catch (final SaxonApiException e) { } catch (final SaxonApiException | SAXException e) {
throw new IllegalStateException("Can not create final report", e); throw new IllegalStateException("Can not create final report", e);
} }
} }
private XsltExecutable getTransformation(final Bag results) { private static XdmNode createEmpty() throws SaxonApiException, SAXException {
final Result<ScenarioType, String> scenario = results.getScenarioSelectionResult(); final BuildingContentHandler contentHandler = ObjectFactory.createProcessor().newDocumentBuilder().newBuildingContentHandler();
final ScenarioType reportScenario = scenario.isValid() ? scenario.getObject() : this.scenarioRepository.getFallbackScenario(); contentHandler.startDocument();
return loadFromScenario(reportScenario); return contentHandler.getDocumentNode();
}
private static XsltExecutable getTransformation(final Bag results) {
return loadFromScenario(results.getScenarioSelectionResult().getObject());
} }
} }

View file

@ -26,8 +26,11 @@ import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.model.reportInput.CreateReportInput; import de.kosit.validationtool.model.reportInput.CreateReportInput;
import de.kosit.validationtool.model.scenarios.ScenarioType; import de.kosit.validationtool.model.scenarios.ScenarioType;
import net.sf.saxon.s9api.XdmNode;
/** /**
* Identifiziert das der Eingabe entsprechende Szenario, sofern eines konfiguriert ist. * Identifiziert das der Eingabe entsprechende Szenario, sofern eines konfiguriert ist. Setzt das Fallback-Szenario,
* wenn keines identifiziert werden konnte.
* *
* @author Andreas Penski * @author Andreas Penski
*/ */
@ -39,18 +42,23 @@ public class ScenarioSelectionAction implements CheckAction {
@Override @Override
public void check(final Bag results) { public void check(final Bag results) {
final CreateReportInput report = results.getReportInput(); final CreateReportInput report = results.getReportInput();
final Result<ScenarioType, String> scenarioTypeResult = this.repository.selectScenario(results.getParserResult().getObject()); final Result<ScenarioType, String> scenarioTypeResult;
results.setScenarioSelectionResult(scenarioTypeResult);
if (scenarioTypeResult.isValid()) { if (results.getParserResult().isValid()) {
final ScenarioType scenario = scenarioTypeResult.getObject(); scenarioTypeResult = determineScenario(results.getParserResult().getObject());
report.setScenario(scenario);
} else { } else {
results.stopProcessing(scenarioTypeResult.getErrors()); scenarioTypeResult = new Result<>(repository.getFallbackScenario());
} }
results.setScenarioSelectionResult(scenarioTypeResult);
report.setScenario(scenarioTypeResult.getObject());
} }
@Override private Result<ScenarioType, String> determineScenario(final XdmNode document) {
public boolean isSkipped(final Bag results) { final Result<ScenarioType, String> result = this.repository.selectScenario(document);
return results.getParserResult().isInvalid(); if (result.isInvalid()) {
return new Result<>(repository.getFallbackScenario());
}
return result;
} }
} }

View file

@ -46,26 +46,26 @@ import de.kosit.validationtool.model.scenarios.ScenarioType;
@Slf4j @Slf4j
public class SchemaValidationAction implements CheckAction { public class SchemaValidationAction implements CheckAction {
private Result<Boolean, XMLSyntaxError> validate(byte[] document, ScenarioType scenarioType) { private static Result<Boolean, XMLSyntaxError> validate(final byte[] document, final ScenarioType scenarioType) {
log.debug("Validating document using scenario {}", scenarioType.getName()); log.debug("Validating document using scenario {}", scenarioType.getName());
final CollectingErrorEventHandler errorHandler = new CollectingErrorEventHandler(); final CollectingErrorEventHandler errorHandler = new CollectingErrorEventHandler();
try ( InputStream input = new ByteArrayInputStream(document) ) { try ( final InputStream input = new ByteArrayInputStream(document) ) {
final Validator validator = ObjectFactory.createValidator(scenarioType.getSchema()); final Validator validator = ObjectFactory.createValidator(scenarioType.getSchema());
validator.setErrorHandler(errorHandler); validator.setErrorHandler(errorHandler);
validator.validate(new StreamSource(input)); validator.validate(new StreamSource(input));
return new Result<>(!errorHandler.hasErrors(), errorHandler.getErrors()); return new Result<>(!errorHandler.hasErrors(), errorHandler.getErrors());
} catch (SAXException | IOException e) { } catch (final SAXException | IOException e) {
throw new IllegalStateException("Error validating document", e); throw new IllegalStateException("Error validating document", e);
} }
} }
@Override @Override
public void check(Bag results) { public void check(final Bag results) {
final CreateReportInput report = results.getReportInput(); final CreateReportInput report = results.getReportInput();
final ScenarioType scenario = results.getScenarioSelectionResult().getObject(); final ScenarioType scenario = results.getScenarioSelectionResult().getObject();
final Result<Boolean, XMLSyntaxError> validateResult = validate(results.getInput().getContent(), scenario); final Result<Boolean, XMLSyntaxError> validateResult = validate(results.getInput().getContent(), scenario);
results.setSchemaValidationResult(validateResult); results.setSchemaValidationResult(validateResult);
ValidationResultsXmlSchema result = new ValidationResultsXmlSchema(); final ValidationResultsXmlSchema result = new ValidationResultsXmlSchema();
report.setValidationResultsXmlSchema(result); report.setValidationResultsXmlSchema(result);
result.getResource().addAll(scenario.getValidateWithXmlSchema().getResource()); result.getResource().addAll(scenario.getValidateWithXmlSchema().getResource());
if (!validateResult.isValid()) { if (!validateResult.isValid()) {
@ -76,11 +76,11 @@ public class SchemaValidationAction implements CheckAction {
@Override @Override
public boolean isSkipped(Bag results) { public boolean isSkipped(final Bag results) {
return hasNoScenario(results); return hasNoSchema(results);
} }
private static boolean hasNoScenario(Bag results) { private static boolean hasNoSchema(final Bag results) {
return results.getScenarioSelectionResult() == null || results.getScenarioSelectionResult().isInvalid(); return results.getScenarioSelectionResult() == null || results.getScenarioSelectionResult().getObject().getSchema() == null;
} }
} }

View file

@ -95,6 +95,10 @@ public class SchematronValidationAction implements CheckAction {
@Override @Override
public boolean isSkipped(final Bag results) { public boolean isSkipped(final Bag results) {
return results.getSchemaValidationResult() == null || results.getSchemaValidationResult().isInvalid(); return hasNoSchematrons(results.getScenarioSelectionResult().getObject());
}
private static boolean hasNoSchematrons(final ScenarioType object) {
return object.getValidateWithSchematron() == null || object.getValidateWithSchematron().size() == 0;
} }
} }

View file

@ -121,6 +121,9 @@ public class DefaultCheckTest {
assertThat(result.isWellformed()).isFalse(); assertThat(result.isWellformed()).isFalse();
assertThat(result.isSchemaValid()).isFalse(); assertThat(result.isSchemaValid()).isFalse();
assertThat(result.isProcessingSuccessful()).isFalse(); assertThat(result.isProcessingSuccessful()).isFalse();
assertThat(result.getAcceptRecommendation()).isEqualTo(AcceptRecommendation.REJECT);
assertThat(result.getReport()).isNotNull();
assertThat(result.getReportDocument()).isNotNull();
} }
} }

View file

@ -22,13 +22,21 @@ package de.kosit.validationtool.impl;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.StringWriter;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.nio.file.Paths; import java.nio.file.Paths;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import net.sf.saxon.dom.NodeOverNodeInfo;
import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XdmNode; import net.sf.saxon.s9api.XdmNode;
@ -123,4 +131,18 @@ public class Helper {
return new ContentRepository(ObjectFactory.createProcessor(), new File("src/test/resources/examples/repository").toURI()); return new ContentRepository(ObjectFactory.createProcessor(), new File("src/test/resources/examples/repository").toURI());
} }
public static String serialize(final Document doc) {
try ( final StringWriter writer = new StringWriter() ) {
final Transformer transformer = ObjectFactory.createTransformer(true);
transformer.transform(new DOMSource(doc), new StreamResult(writer));
return writer.toString();
} catch (final IOException | TransformerException e) {
throw new IllegalStateException("Can not serialize document", e);
}
}
public static String serialize(final XdmNode node) {
return serialize((Document) NodeOverNodeInfo.wrap(node.getUnderlyingNode()));
}
} }