mirror of
https://github.com/itplr-kosit/validator.git
synced 2026-05-26 01:05:38 +00:00
(enhance) introduce resolving strategy (configurable xml security); introduce API configuration
This commit is contained in:
parent
7a86f049ac
commit
35c0797898
67 changed files with 2441 additions and 845 deletions
|
|
@ -34,6 +34,8 @@ import lombok.RequiredArgsConstructor;
|
|||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import de.kosit.validationtool.impl.xml.RelativeUriResolver;
|
||||
|
||||
/**
|
||||
* {@link LSResourceResolver} der objekte relativ zu einem Basis-Pfad aus dem Classpath der Anwendung laden kann.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -24,11 +24,13 @@ import java.net.URI;
|
|||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.URIResolver;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
|
|
@ -40,12 +42,16 @@ import org.xml.sax.SAXException;
|
|||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
|
||||
import de.kosit.validationtool.impl.Scenario.Transformation;
|
||||
import de.kosit.validationtool.impl.xml.RelativeUriResolver;
|
||||
import de.kosit.validationtool.model.scenarios.NamespaceType;
|
||||
import de.kosit.validationtool.model.scenarios.ResourceType;
|
||||
import de.kosit.validationtool.model.scenarios.ScenarioType;
|
||||
import de.kosit.validationtool.model.scenarios.ValidateWithSchematron;
|
||||
|
||||
import net.sf.saxon.s9api.Processor;
|
||||
import net.sf.saxon.s9api.SaxonApiException;
|
||||
|
|
@ -70,9 +76,16 @@ public class ContentRepository {
|
|||
|
||||
private final URI repository;
|
||||
|
||||
private final ResolvingMode mode = ResolvingMode.STRICT_RELATIVE;
|
||||
private final URIResolver resolver;
|
||||
|
||||
private Source resolve(final URL resource) {
|
||||
@Setter
|
||||
private SchemaFactory schemaFactory;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private ResolvingConfigurationStrategy resolvingConfigurationStrategy;
|
||||
|
||||
private static Source resolve(final URL resource) {
|
||||
try {
|
||||
return new StreamSource(resource.openStream(), resource.toURI().getRawPath());
|
||||
} catch (final IOException | URISyntaxException e) {
|
||||
|
|
@ -82,7 +95,7 @@ public class ContentRepository {
|
|||
|
||||
private Schema createSchema(final Source[] schemaSources, final LSResourceResolver resourceResolver) {
|
||||
try {
|
||||
final SchemaFactory sf = ObjectFactory.createSchemaFactory();
|
||||
final SchemaFactory sf = this.schemaFactory;
|
||||
sf.setResourceResolver(resourceResolver);
|
||||
return sf.newSchema(schemaSources);
|
||||
} catch (final SAXException e) {
|
||||
|
|
@ -106,9 +119,13 @@ public class ContentRepository {
|
|||
final CollectingErrorEventHandler listener = new CollectingErrorEventHandler();
|
||||
try {
|
||||
xsltCompiler.setErrorListener(listener);
|
||||
xsltCompiler.setURIResolver(createResolver());
|
||||
final URIResolver resolver = getResolver();
|
||||
if (resolver != null) {
|
||||
// otherwise use default resolver
|
||||
xsltCompiler.setURIResolver(resolver);
|
||||
}
|
||||
|
||||
return xsltCompiler.compile(resolve(uri));
|
||||
return xsltCompiler.compile(resolveInRepository(uri));
|
||||
} catch (final SaxonApiException e) {
|
||||
listener.getErrors().forEach(event -> event.log(log));
|
||||
throw new IllegalStateException("Can not compile xslt executable for uri " + uri, e);
|
||||
|
|
@ -130,6 +147,10 @@ public class ContentRepository {
|
|||
return createSchema(url, null);
|
||||
}
|
||||
|
||||
public Schema createSchema(final URI uri) {
|
||||
return createSchema(new Source[] { resolveInRepository(uri) });
|
||||
}
|
||||
|
||||
public Schema createSchema(final URL url, final LSResourceResolver resourceResolver) {
|
||||
log.info("Load schema from source {}", url.getPath());
|
||||
return createSchema(new Source[] { resolve(url) }, resourceResolver);
|
||||
|
|
@ -150,11 +171,11 @@ public class ContentRepository {
|
|||
* @return ReportInput-Schema
|
||||
*/
|
||||
public Schema getReportInputSchema() {
|
||||
if (reportInputSchema == null) {
|
||||
if (this.reportInputSchema == null) {
|
||||
final Source source = resolve(ContentRepository.class.getResource("/xsd/createReportInput.xsd"));
|
||||
reportInputSchema = createSchema(new Source[] { source }, new ClassPathResourceResolver("/xsd"));
|
||||
this.reportInputSchema = createSchema(new Source[] { source }, new ClassPathResourceResolver("/xsd"));
|
||||
}
|
||||
return reportInputSchema;
|
||||
return this.reportInputSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -164,7 +185,7 @@ public class ContentRepository {
|
|||
* @return das Schema
|
||||
*/
|
||||
public Schema createSchema(final Collection<String> uris) {
|
||||
return createSchema(uris.stream().map(s -> resolve(URI.create(s))).toArray(Source[]::new));
|
||||
return createSchema(uris.stream().map(s -> resolveInRepository(URI.create(s))).toArray(Source[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -182,9 +203,19 @@ public class ContentRepository {
|
|||
return schema;
|
||||
}
|
||||
|
||||
private Source resolve(final URI source) {
|
||||
final URI resolved = this.mode.resolve(source, this.repository);
|
||||
return new StreamSource(resolved.toASCIIString());
|
||||
private Source resolveInRepository(final URI source) {
|
||||
try {
|
||||
if (this.resolver == null) {
|
||||
// TODO wie wird ohne resolver das richtige Artefakt gefunden?
|
||||
// assume local
|
||||
final URI resolved = RelativeUriResolver.resolve(source, this.repository);
|
||||
return new StreamSource(resolved.toASCIIString());
|
||||
}
|
||||
return this.resolver.resolve(source.toString(), this.repository.toString());
|
||||
} catch (final TransformerException e) {
|
||||
log.error("Error resolving source {}", source, e);
|
||||
throw new IllegalStateException(String.format("Can not resolve %s in repository %s", source, this.repository), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -212,8 +243,8 @@ public class ContentRepository {
|
|||
*
|
||||
* @return ein neuer Resolver
|
||||
*/
|
||||
public URIResolver createResolver() {
|
||||
return this.mode.createResolver(this.repository);
|
||||
public URIResolver getResolver() {
|
||||
return this.resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -223,6 +254,10 @@ public class ContentRepository {
|
|||
*/
|
||||
public Transformation createReportTransformation(final ScenarioType t) {
|
||||
final ResourceType resource = t.getCreateReport().getResource();
|
||||
return createTransformation(resource);
|
||||
}
|
||||
|
||||
public Transformation createTransformation(final ResourceType resource) {
|
||||
final XsltExecutable executable = loadXsltScript(URI.create(resource.getLocation()));
|
||||
return new Transformation(executable, resource);
|
||||
}
|
||||
|
|
@ -238,4 +273,13 @@ public class ContentRepository {
|
|||
.collect(Collectors.toMap(NamespaceType::getPrefix, NamespaceType::getValue));
|
||||
return createXPath(s.getAcceptMatch(), namespaces);
|
||||
}
|
||||
|
||||
public List<Transformation> createSchematronTransformations(final ScenarioType s) {
|
||||
return s.getValidateWithSchematron() != null && s.getValidateWithSchematron().isEmpty() ? Collections.emptyList()
|
||||
: s.getValidateWithSchematron().stream().map(this::createSchematronTransformation).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Transformation createSchematronTransformation(final ValidateWithSchematron validateWithSchematron) {
|
||||
return createTransformation(validateWithSchematron.getResource());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ import javax.xml.bind.Unmarshaller;
|
|||
import javax.xml.bind.ValidationEventHandler;
|
||||
import javax.xml.bind.annotation.XmlRegistry;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
|
|
@ -47,7 +46,6 @@ import javax.xml.transform.stream.StreamSource;
|
|||
import javax.xml.validation.Schema;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
|
@ -86,6 +84,15 @@ public class ConversionService {
|
|||
// context setup
|
||||
private JAXBContext jaxbContext;
|
||||
|
||||
public JAXBContext
|
||||
|
||||
getJaxbContext() {
|
||||
if (this.jaxbContext == null) {
|
||||
initialize();
|
||||
}
|
||||
return this.jaxbContext;
|
||||
}
|
||||
|
||||
private static <T> QName createQName(final T model) {
|
||||
return new QName(model.getClass().getSimpleName().toLowerCase());
|
||||
}
|
||||
|
|
@ -141,13 +148,6 @@ public class ConversionService {
|
|||
}
|
||||
}
|
||||
|
||||
private JAXBContext getJaxbContext() {
|
||||
if (this.jaxbContext == null) {
|
||||
initialize();
|
||||
}
|
||||
return this.jaxbContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshalls a specifc xml model into a defined java object.
|
||||
*
|
||||
|
|
@ -233,24 +233,8 @@ public class ConversionService {
|
|||
}
|
||||
}
|
||||
|
||||
public <T> Document writeDocument(final T input) {
|
||||
if (input == null) {
|
||||
throw new ConversionExeption("Can not serialize null");
|
||||
}
|
||||
final DocumentBuilder builder = ObjectFactory.createDocumentBuilder(false);
|
||||
final Document document = builder.newDocument();
|
||||
|
||||
// Marshal the Object to a Document
|
||||
Marshaller marshaller = null;
|
||||
try {
|
||||
marshaller = getJaxbContext().createMarshaller();
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||
marshaller.marshal(input, document);
|
||||
return document;
|
||||
} catch (final JAXBException e) {
|
||||
throw new ConversionExeption(String.format("Error serializing Object %s to document", input.getClass().getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public <T> T readDocument(final Source source, final Class<T> type) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -21,9 +21,15 @@ package de.kosit.validationtool.impl;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.xml.datatype.DatatypeConfigurationException;
|
||||
import javax.xml.datatype.DatatypeFactory;
|
||||
import javax.xml.datatype.XMLGregorianCalendar;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
|
@ -35,6 +41,7 @@ import de.kosit.validationtool.api.XmlError;
|
|||
import de.kosit.validationtool.impl.tasks.CheckAction;
|
||||
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
|
||||
import de.kosit.validationtool.impl.tasks.ComputeAcceptanceAction;
|
||||
import de.kosit.validationtool.impl.tasks.CreateDocumentIdentificationAction;
|
||||
import de.kosit.validationtool.impl.tasks.CreateReportAction;
|
||||
import de.kosit.validationtool.impl.tasks.DocumentParseAction;
|
||||
import de.kosit.validationtool.impl.tasks.ScenarioSelectionAction;
|
||||
|
|
@ -56,15 +63,11 @@ import net.sf.saxon.s9api.Processor;
|
|||
@Slf4j
|
||||
public class DefaultCheck implements Check {
|
||||
|
||||
@Getter
|
||||
private final ScenarioRepository repository;
|
||||
|
||||
@Getter
|
||||
private final ContentRepository contentRepository;
|
||||
|
||||
@Getter
|
||||
private final ConversionService conversionService;
|
||||
|
||||
private final Configuration configuration;
|
||||
|
||||
@Getter
|
||||
private final List<CheckAction> checkSteps;
|
||||
|
||||
|
|
@ -74,21 +77,19 @@ public class DefaultCheck implements Check {
|
|||
* @param configuration die Konfiguration
|
||||
*/
|
||||
public DefaultCheck(final Configuration configuration) {
|
||||
final Processor processor = ObjectFactory.createProcessor();
|
||||
this.configuration = configuration;
|
||||
final ContentRepository content = configuration.getContentRepository();
|
||||
final Processor processor = content.getProcessor();
|
||||
this.conversionService = new ConversionService();
|
||||
this.repository = new ScenarioRepository(configuration);
|
||||
|
||||
// TODO get rid of it
|
||||
this.contentRepository = configuration.getContentRepository();
|
||||
this.checkSteps = new ArrayList<>();
|
||||
this.checkSteps.add(new DocumentParseAction());
|
||||
this.checkSteps.add(new DocumentParseAction(processor));
|
||||
this.checkSteps.add(new CreateDocumentIdentificationAction());
|
||||
this.checkSteps.add(new ScenarioSelectionAction(this.repository));
|
||||
this.checkSteps.add(new SchemaValidationAction());
|
||||
this.checkSteps.add(new SchematronValidationAction(this.contentRepository, this.conversionService));
|
||||
this.checkSteps
|
||||
.add(new ValidateReportInputAction(this.conversionService, configuration.getContentRepository().getReportInputSchema()));
|
||||
this.checkSteps.add(new CreateReportAction(processor, this.conversionService, this.contentRepository));
|
||||
this.checkSteps.add(new ScenarioSelectionAction(new ScenarioRepository(configuration)));
|
||||
this.checkSteps.add(new SchemaValidationAction(content.getResolvingConfigurationStrategy(), processor));
|
||||
this.checkSteps.add(new SchematronValidationAction(content.getResolver(), this.conversionService));
|
||||
this.checkSteps.add(new ValidateReportInputAction(this.conversionService, content.getReportInputSchema()));
|
||||
this.checkSteps.add(new CreateReportAction(processor, this.conversionService, content.getResolver()));
|
||||
this.checkSteps.add(new ComputeAcceptanceAction());
|
||||
}
|
||||
|
||||
|
|
@ -97,11 +98,22 @@ public class DefaultCheck implements Check {
|
|||
final EngineType e = new EngineType();
|
||||
e.setName(EngineInformation.getName());
|
||||
type.setEngine(e);
|
||||
type.setTimestamp(ObjectFactory.createTimestamp());
|
||||
type.setTimestamp(createTimestamp());
|
||||
type.setFrameworkVersion(EngineInformation.getFrameworkVersion());
|
||||
return type;
|
||||
}
|
||||
|
||||
private static XMLGregorianCalendar createTimestamp() {
|
||||
try {
|
||||
final GregorianCalendar cal = new GregorianCalendar();
|
||||
cal.setTime(new Date());
|
||||
return DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
|
||||
|
||||
} catch (final DatatypeConfigurationException e) {
|
||||
throw new IllegalStateException("Can not create timestamp", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result checkInput(final Input input) {
|
||||
final CheckAction.Bag t = new CheckAction.Bag(input, createReport());
|
||||
|
|
@ -124,7 +136,8 @@ public class DefaultCheck implements Check {
|
|||
}
|
||||
|
||||
private Result createResult(final Bag t) {
|
||||
final DefaultResult result = new DefaultResult(t.getReport(), t.getAcceptStatus(), new HtmlExtractor(this.contentRepository));
|
||||
final DefaultResult result = new DefaultResult(t.getReport(), t.getAcceptStatus(),
|
||||
new HtmlExtractor(this.configuration.getContentRepository().getProcessor()));
|
||||
result.setWellformed(t.getParserResult().isValid());
|
||||
result.setReportInput(t.getReportInput());
|
||||
if (t.getSchemaValidationResult() != null) {
|
||||
|
|
|
|||
|
|
@ -6,13 +6,16 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import net.sf.saxon.dom.NodeOverNodeInfo;
|
||||
import net.sf.saxon.s9api.Processor;
|
||||
import net.sf.saxon.s9api.SaxonApiException;
|
||||
import net.sf.saxon.s9api.Serializer;
|
||||
import net.sf.saxon.s9api.XPathCompiler;
|
||||
import net.sf.saxon.s9api.XPathExecutable;
|
||||
import net.sf.saxon.s9api.XPathSelector;
|
||||
import net.sf.saxon.s9api.XdmItem;
|
||||
|
|
@ -26,38 +29,51 @@ import net.sf.saxon.s9api.XdmNode;
|
|||
@RequiredArgsConstructor
|
||||
public class HtmlExtractor {
|
||||
|
||||
private final ContentRepository repository;
|
||||
private final Processor processor;
|
||||
|
||||
private XPathExecutable executable;
|
||||
|
||||
public List<XdmNode> extract(XdmNode xdmSource) {
|
||||
public List<XdmNode> extract(final XdmNode xdmSource) {
|
||||
try {
|
||||
final XPathSelector selector = getSelector();
|
||||
selector.setContextItem(xdmSource);
|
||||
return selector.stream().map(this::castToNode).collect(Collectors.toList());
|
||||
return selector.stream().map(HtmlExtractor::castToNode).collect(Collectors.toList());
|
||||
|
||||
} catch (SaxonApiException e) {
|
||||
} catch (final SaxonApiException e) {
|
||||
throw new IllegalStateException("Can not extract html content", e);
|
||||
}
|
||||
}
|
||||
|
||||
private XdmNode castToNode(final XdmItem xdmItem) {
|
||||
private static XdmNode castToNode(final XdmItem xdmItem) {
|
||||
return (XdmNode) xdmItem;
|
||||
}
|
||||
|
||||
private XPathSelector getSelector() {
|
||||
if (executable == null) {
|
||||
Map<String, String> ns = new HashMap<>();
|
||||
if (this.executable == null) {
|
||||
final Map<String, String> ns = new HashMap<>();
|
||||
ns.put("html", "http://www.w3.org/1999/xhtml");
|
||||
executable = repository.createXPath("//html:html", ns);
|
||||
this.executable = createXPath("//html:html", ns);
|
||||
}
|
||||
return executable.load();
|
||||
return this.executable.load();
|
||||
}
|
||||
|
||||
private static String convertToString(final XdmNode element) {
|
||||
private XPathExecutable createXPath(final String expression, final Map<String, String> namespaces) {
|
||||
try {
|
||||
final XPathCompiler compiler = this.processor.newXPathCompiler();
|
||||
if (namespaces != null) {
|
||||
namespaces.forEach(compiler::declareNamespace);
|
||||
}
|
||||
return compiler.compile(expression);
|
||||
} catch (final SaxonApiException e) {
|
||||
throw new IllegalStateException(String.format("Can not compile xpath match expression '%s'",
|
||||
StringUtils.isNotBlank(expression) ? expression : "EMPTY EXPRESSION"), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String convertToString(final XdmNode element) {
|
||||
try {
|
||||
final StringWriter writer = new StringWriter();
|
||||
final Serializer serializer = ObjectFactory.createProcessor().newSerializer(writer);
|
||||
final Serializer serializer = this.processor.newSerializer(writer);
|
||||
serializer.serializeNode(element);
|
||||
return writer.toString();
|
||||
} catch (final SaxonApiException e) {
|
||||
|
|
@ -72,7 +88,7 @@ public class HtmlExtractor {
|
|||
* @return HTML-Fragment als String
|
||||
*/
|
||||
public List<String> extractAsString(final XdmNode node) {
|
||||
return extract(node).stream().map(HtmlExtractor::convertToString).collect(Collectors.toList());
|
||||
return extract(node).stream().map(this::convertToString).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Element> extractAsElement(final XdmNode node) {
|
||||
|
|
|
|||
|
|
@ -24,13 +24,8 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.datatype.DatatypeConfigurationException;
|
||||
import javax.xml.datatype.DatatypeFactory;
|
||||
import javax.xml.datatype.XMLGregorianCalendar;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
|
@ -40,13 +35,6 @@ import javax.xml.transform.Transformer;
|
|||
import javax.xml.transform.TransformerConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import javax.xml.validation.Validator;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXNotRecognizedException;
|
||||
import org.xml.sax.SAXNotSupportedException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
|
@ -70,6 +58,7 @@ import net.sf.saxon.trans.XPathException;
|
|||
* @author Andreas Penski
|
||||
*/
|
||||
@Slf4j
|
||||
@Deprecated
|
||||
public class ObjectFactory {
|
||||
|
||||
private static class SecureUriResolver implements CollectionFinder, OutputURIResolver, UnparsedTextURIResolver {
|
||||
|
|
@ -116,7 +105,7 @@ public class ObjectFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private ObjectFactory() {
|
||||
ObjectFactory() {
|
||||
// hide, it's a factory
|
||||
}
|
||||
|
||||
|
|
@ -154,13 +143,17 @@ public class ObjectFactory {
|
|||
public static Transformer createTransformer(final boolean prettyPrint) {
|
||||
Transformer transformer = null;
|
||||
try {
|
||||
transformer = TransformerFactory.newInstance().newTransformer();
|
||||
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
|
||||
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); // Compliant
|
||||
transformer = transformerFactory.newTransformer();
|
||||
if (prettyPrint) {
|
||||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
|
||||
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
|
||||
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
|
||||
|
||||
}
|
||||
return transformer;
|
||||
} catch (final TransformerConfigurationException e) {
|
||||
|
|
@ -168,21 +161,6 @@ public class ObjectFactory {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt einen Zeitstempel zur Verwendung in XML-Objekten
|
||||
*
|
||||
* @return eine Instanz {@link XMLGregorianCalendar}
|
||||
*/
|
||||
public static XMLGregorianCalendar createTimestamp() {
|
||||
try {
|
||||
final GregorianCalendar cal = new GregorianCalendar();
|
||||
cal.setTime(new Date());
|
||||
return DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
|
||||
|
||||
} catch (final DatatypeConfigurationException e) {
|
||||
throw new IllegalStateException("Can not create timestamp", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static DocumentBuilder createDocumentBuilder(final boolean validating) {
|
||||
try {
|
||||
|
|
@ -223,38 +201,6 @@ public class ObjectFactory {
|
|||
return processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt einen Validier für das angegebenen Schema.
|
||||
*
|
||||
* @param schema das Schema mit dem validiert werden soll
|
||||
* @return einen vorkonfigurierten Validator
|
||||
*/
|
||||
public static Validator createValidator(final Schema schema) {
|
||||
final Validator validator = schema.newValidator();
|
||||
try {
|
||||
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
|
||||
} catch (final SAXNotRecognizedException | SAXNotSupportedException e) {
|
||||
log.warn("Can not disable external DTD access. Maybe an unsupported JAXP implementation is used.");
|
||||
log.debug(e.getMessage(), e);
|
||||
}
|
||||
try {
|
||||
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
|
||||
} catch (final SAXNotRecognizedException | SAXNotSupportedException e) {
|
||||
log.warn("Can not disable external DTD access. Maybe an unsupported JAXP implementation is used.");
|
||||
log.debug(e.getMessage(), e);
|
||||
|
||||
}
|
||||
return validator;
|
||||
}
|
||||
|
||||
public static SchemaFactory createSchemaFactory() {
|
||||
final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
try {
|
||||
sf.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
|
||||
sf.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "file");
|
||||
} catch (final SAXException e) {
|
||||
log.warn("Can not disable external DTD access, maybe an unsupported JAXP implementation is used", e);
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,34 @@
|
|||
package de.kosit.validationtool.impl;
|
||||
|
||||
import java.net.URI;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import javax.xml.transform.URIResolver;
|
||||
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
|
||||
import de.kosit.validationtool.impl.xml.StrictLocalResolvingStrategy;
|
||||
import de.kosit.validationtool.impl.xml.StrictRelativeResolvingStrategy;
|
||||
|
||||
/**
|
||||
* Defines how artefacts are resolved internally.
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public enum ResolvingMode {
|
||||
|
||||
/**
|
||||
* Resolving using only the configured content repository. No furthing resolving allowed. This
|
||||
*/
|
||||
STRICT_RELATIVE {
|
||||
STRICT_RELATIVE(new StrictRelativeResolvingStrategy()) {
|
||||
|
||||
@Override
|
||||
public URI resolve(final URI source, final URI repository) {
|
||||
return RelativeUriResolver.resolve(source, repository);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URIResolver createResolver(final URI repository) {
|
||||
return new RelativeUriResolver(repository);
|
||||
}
|
||||
},
|
||||
|
||||
STRICT_LOCAL,
|
||||
STRICT_LOCAL(new StrictLocalResolvingStrategy()),
|
||||
|
||||
JDK_SUPPORTED,
|
||||
JDK_SUPPORTED(null),
|
||||
|
||||
CUSTOM;
|
||||
CUSTOM(null);
|
||||
|
||||
public URI resolve(final URI source, final URI repository) {
|
||||
throw new NotImplementedException("Not yet implemented");
|
||||
}
|
||||
@Getter
|
||||
private final ResolvingConfigurationStrategy strategy;
|
||||
|
||||
public URIResolver createResolver(final URI repository) {
|
||||
throw new NotImplementedException("Not yet implemented");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import net.sf.saxon.s9api.XsltExecutable;
|
|||
* @author Andreas Penski
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class Scenario {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ public class ScenarioRepository {
|
|||
|
||||
public ScenarioRepository(final Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
configuration.build();
|
||||
log.info("Loaded scenarios for {} by {} from {}. The following scenarios are available:\n\n{}", configuration.getName(),
|
||||
configuration.getAuthor(), configuration.getDate(), summarizeScenarios());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package de.kosit.validationtool.impl;
|
||||
package de.kosit.validationtool.impl.tasks;
|
||||
|
||||
import de.kosit.validationtool.impl.tasks.CheckAction;
|
||||
import de.kosit.validationtool.model.reportInput.DocumentIdentificationType;
|
||||
|
||||
/**
|
||||
|
|
@ -8,7 +7,7 @@ import de.kosit.validationtool.model.reportInput.DocumentIdentificationType;
|
|||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
class CreateDocumentIdentificationAction implements CheckAction {
|
||||
public class CreateDocumentIdentificationAction implements CheckAction {
|
||||
|
||||
@Override
|
||||
public void check(final Bag transporter) {
|
||||
|
|
@ -19,23 +19,31 @@
|
|||
|
||||
package de.kosit.validationtool.impl.tasks;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.util.JAXBSource;
|
||||
import javax.xml.transform.URIResolver;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.ContentHandler;
|
||||
import org.xml.sax.DTDHandler;
|
||||
import org.xml.sax.EntityResolver;
|
||||
import org.xml.sax.ErrorHandler;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXNotRecognizedException;
|
||||
import org.xml.sax.SAXNotSupportedException;
|
||||
import org.xml.sax.XMLReader;
|
||||
import org.xml.sax.helpers.AttributesImpl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import de.kosit.validationtool.impl.CollectingErrorEventHandler;
|
||||
import de.kosit.validationtool.impl.ContentRepository;
|
||||
import de.kosit.validationtool.impl.ConversionService;
|
||||
import de.kosit.validationtool.impl.EngineInformation;
|
||||
import de.kosit.validationtool.impl.ObjectFactory;
|
||||
import de.kosit.validationtool.impl.Scenario;
|
||||
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
|
||||
|
||||
|
|
@ -58,12 +66,108 @@ import net.sf.saxon.s9api.XsltTransformer;
|
|||
@RequiredArgsConstructor
|
||||
public class CreateReportAction implements CheckAction {
|
||||
|
||||
/**
|
||||
* Wrapper to fix some inconsistencies between sax and saxon. Saxon tries to set some properties which has no effect on
|
||||
* {@link JAXBSource}'s XMLReader, but it throws exceptions on unknown properties. This just drops this exceptions.
|
||||
*/
|
||||
private static class ReaderWrapper implements XMLReader {
|
||||
|
||||
private final XMLReader delegate;
|
||||
|
||||
public ReaderWrapper(final XMLReader xmlReader) {
|
||||
this.delegate = xmlReader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getFeature(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
|
||||
if (name.equals("http://xml.org/sax/features/namespaces")) {
|
||||
return true;
|
||||
} else if (name.equals("http://xml.org/sax/features/namespace-prefixes")) {
|
||||
return false;
|
||||
}
|
||||
// just return false on unknown properties
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFeature(final String name, final boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
|
||||
// this inverts the logic from JaxbSource pseude parser
|
||||
if (name.equals("http://xml.org/sax/features/namespaces") && !value) {
|
||||
throw new SAXNotRecognizedException(name);
|
||||
}
|
||||
if (name.equals("http://xml.org/sax/features/namespace-prefixes") && value) {
|
||||
throw new SAXNotRecognizedException(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
|
||||
return this.delegate.getProperty(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(final String name, final Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
|
||||
this.delegate.setProperty(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntityResolver(final EntityResolver resolver) {
|
||||
this.delegate.setEntityResolver(resolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityResolver getEntityResolver() {
|
||||
return this.delegate.getEntityResolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDTDHandler(final DTDHandler handler) {
|
||||
this.delegate.setDTDHandler(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DTDHandler getDTDHandler() {
|
||||
return this.delegate.getDTDHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentHandler(final ContentHandler handler) {
|
||||
this.delegate.setContentHandler(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentHandler getContentHandler() {
|
||||
return this.delegate.getContentHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setErrorHandler(final ErrorHandler handler) {
|
||||
this.delegate.setErrorHandler(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorHandler getErrorHandler() {
|
||||
return this.delegate.getErrorHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(final InputSource input) throws IOException, SAXException {
|
||||
this.delegate.parse(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(final String systemId) throws IOException, SAXException {
|
||||
this.delegate.parse(systemId);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String ERROR_MESSAGE_ELEMENT = "error-message";
|
||||
|
||||
private final Processor processor;
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
private final ContentRepository contentRepository;
|
||||
private final URIResolver resolver;
|
||||
|
||||
private static XsltExecutable loadFromScenario(final Scenario object) {
|
||||
return object.getReportTransformation().getExecutable();
|
||||
|
|
@ -77,14 +181,17 @@ public class CreateReportAction implements CheckAction {
|
|||
final XdmNode parsedDocument = results.getParserResult().isValid() ? results.getParserResult().getObject()
|
||||
: createErrorInformation(results.getParserResult().getErrors());
|
||||
|
||||
final Document reportInput = this.conversionService.writeDocument(results.getReportInput());
|
||||
final XdmNode root = documentBuilder.build(new DOMSource(reportInput));
|
||||
final Marshaller marshaller = this.conversionService.getJaxbContext().createMarshaller();
|
||||
final JAXBSource source = new JAXBSource(marshaller, results.getReportInput());
|
||||
// wrap to circumvent inconsistency between sax and saxon
|
||||
source.setXMLReader(new ReaderWrapper(source.getXMLReader()));
|
||||
|
||||
final XdmNode root = documentBuilder.build(source);
|
||||
final XsltTransformer transformer = getTransformation(results).load();
|
||||
transformer.setInitialContextNode(root);
|
||||
final CollectingErrorEventHandler e = new CollectingErrorEventHandler();
|
||||
final URIResolver resolver = this.contentRepository.createResolver();
|
||||
transformer.setMessageListener(e);
|
||||
transformer.setURIResolver(resolver);
|
||||
transformer.setURIResolver(this.resolver);
|
||||
// transformer.getUnderlyingController().setUnparsedTextURIResolver(resolver);
|
||||
if (parsedDocument != null) {
|
||||
transformer.setParameter(new QName("input-document"), parsedDocument);
|
||||
|
|
@ -94,13 +201,13 @@ public class CreateReportAction implements CheckAction {
|
|||
transformer.transform();
|
||||
results.setReport(destination.getXdmNode());
|
||||
|
||||
} catch (final SaxonApiException | SAXException e) {
|
||||
} catch (final SaxonApiException | SAXException | JAXBException e) {
|
||||
throw new IllegalStateException("Can not create final report", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static XdmNode createErrorInformation(final Collection<XMLSyntaxError> errors) throws SaxonApiException, SAXException {
|
||||
final BuildingContentHandler contentHandler = ObjectFactory.createProcessor().newDocumentBuilder().newBuildingContentHandler();
|
||||
private XdmNode createErrorInformation(final Collection<XMLSyntaxError> errors) throws SaxonApiException, SAXException {
|
||||
final BuildingContentHandler contentHandler = this.processor.newDocumentBuilder().newBuildingContentHandler();
|
||||
contentHandler.startDocument();
|
||||
contentHandler.startElement(EngineInformation.getFrameworkNamespace(), ERROR_MESSAGE_ELEMENT, ERROR_MESSAGE_ELEMENT,
|
||||
new AttributesImpl());
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ import lombok.RequiredArgsConstructor;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import de.kosit.validationtool.api.Input;
|
||||
import de.kosit.validationtool.impl.ObjectFactory;
|
||||
import de.kosit.validationtool.impl.model.Result;
|
||||
import de.kosit.validationtool.model.reportInput.ValidationResultsWellformedness;
|
||||
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
|
||||
import de.kosit.validationtool.model.reportInput.XMLSyntaxErrorSeverity;
|
||||
|
||||
import net.sf.saxon.s9api.DocumentBuilder;
|
||||
import net.sf.saxon.s9api.Processor;
|
||||
import net.sf.saxon.s9api.SaxonApiException;
|
||||
import net.sf.saxon.s9api.XdmNode;
|
||||
|
||||
|
|
@ -46,6 +46,7 @@ import net.sf.saxon.s9api.XdmNode;
|
|||
@RequiredArgsConstructor
|
||||
public class DocumentParseAction implements CheckAction {
|
||||
|
||||
private final Processor processor;
|
||||
/**
|
||||
* Parsed und überprüft ein übergebenes Dokument darauf ob es well-formed ist. Dies stellt den ersten
|
||||
* Verarbeitungsschritt des Prüf-Tools dar. Diese Funktion verzichtet explizit auf die Validierung gegenüber einem
|
||||
|
|
@ -54,13 +55,13 @@ public class DocumentParseAction implements CheckAction {
|
|||
* @param content ein Dokument
|
||||
* @return Ergebnis des Parsings inklusive etwaiger Fehler
|
||||
*/
|
||||
public static Result<XdmNode, XMLSyntaxError> parseDocument(final Input content) {
|
||||
public Result<XdmNode, XMLSyntaxError> parseDocument(final Input content) {
|
||||
if (content == null) {
|
||||
throw new IllegalArgumentException("Input may not be null");
|
||||
}
|
||||
Result<XdmNode, XMLSyntaxError> result;
|
||||
try {
|
||||
final DocumentBuilder builder = ObjectFactory.createProcessor().newDocumentBuilder();
|
||||
final DocumentBuilder builder = this.processor.newDocumentBuilder();
|
||||
builder.setLineNumbering(true);
|
||||
final XdmNode doc = builder.build(content.getSource());
|
||||
result = new Result<>(doc, Collections.emptyList());
|
||||
|
|
|
|||
|
|
@ -36,12 +36,13 @@ import org.xml.sax.SAXException;
|
|||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import de.kosit.validationtool.api.Input;
|
||||
import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
|
||||
import de.kosit.validationtool.impl.CollectingErrorEventHandler;
|
||||
import de.kosit.validationtool.impl.ObjectFactory;
|
||||
import de.kosit.validationtool.impl.Scenario;
|
||||
import de.kosit.validationtool.impl.input.AbstractInput;
|
||||
import de.kosit.validationtool.impl.model.Result;
|
||||
|
|
@ -49,6 +50,7 @@ import de.kosit.validationtool.model.reportInput.CreateReportInput;
|
|||
import de.kosit.validationtool.model.reportInput.ValidationResultsXmlSchema;
|
||||
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
|
||||
|
||||
import net.sf.saxon.s9api.Processor;
|
||||
import net.sf.saxon.s9api.SaxonApiException;
|
||||
import net.sf.saxon.s9api.Serializer;
|
||||
import net.sf.saxon.s9api.XdmNode;
|
||||
|
|
@ -67,16 +69,20 @@ import net.sf.saxon.s9api.XdmNode;
|
|||
* @author Andreas Penski
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class SchemaValidationAction implements CheckAction {
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class ByteArraySerializedDocument implements SerializedDocument {
|
||||
|
||||
private byte[] bytes;
|
||||
|
||||
private final Processor processor;
|
||||
|
||||
@Override
|
||||
public void serialize(final XdmNode node) throws SaxonApiException, IOException {
|
||||
try ( final ByteArrayOutputStream out = new ByteArrayOutputStream() ) {
|
||||
final Serializer serializer = ObjectFactory.createProcessor().newSerializer();
|
||||
final Serializer serializer = this.processor.newSerializer();
|
||||
serializer.setOutputStream(out);
|
||||
serializer.serializeNode(node);
|
||||
serializer.close();
|
||||
|
|
@ -97,16 +103,20 @@ public class SchemaValidationAction implements CheckAction {
|
|||
|
||||
private static class FileSerializedDocument implements SerializedDocument {
|
||||
|
||||
|
||||
private final Path file;
|
||||
|
||||
FileSerializedDocument() throws IOException {
|
||||
private final Processor processor;
|
||||
|
||||
FileSerializedDocument(final Processor processor) throws IOException {
|
||||
this.file = Files.createTempFile("validator", ".xml");
|
||||
this.processor = processor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(final XdmNode node) throws SaxonApiException, IOException {
|
||||
try ( final OutputStream out = Files.newOutputStream(this.file) ) {
|
||||
final Serializer serializer = ObjectFactory.createProcessor().newSerializer();
|
||||
final Serializer serializer = this.processor.newSerializer();
|
||||
serializer.setOutputStream(out);
|
||||
serializer.serializeNode(node);
|
||||
serializer.close();
|
||||
|
|
@ -128,6 +138,10 @@ public class SchemaValidationAction implements CheckAction {
|
|||
|
||||
private static final String LIMIT_PARAMETER = "schema.validation.inmem.limit";
|
||||
|
||||
private final ResolvingConfigurationStrategy factory;
|
||||
|
||||
private final Processor processor;
|
||||
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
@Getter
|
||||
private long inMemoryLimit = Long.parseLong(System.getProperty(LIMIT_PARAMETER, BA_LIMIT.toString())) * FileUtils.ONE_MB;
|
||||
|
|
@ -137,7 +151,7 @@ public class SchemaValidationAction implements CheckAction {
|
|||
final CollectingErrorEventHandler errorHandler = new CollectingErrorEventHandler();
|
||||
try ( final SourceProvider validateInput = resolveSource(results) ) {
|
||||
|
||||
final Validator validator = ObjectFactory.createValidator(scenario.getSchema());
|
||||
final Validator validator = this.factory.createValidator(scenario.getSchema());
|
||||
validator.setErrorHandler(errorHandler);
|
||||
validator.validate(validateInput.getSource());
|
||||
return new Result<>(!errorHandler.hasErrors(), errorHandler.getErrors());
|
||||
|
|
@ -180,9 +194,9 @@ public class SchemaValidationAction implements CheckAction {
|
|||
private SerializedDocument serialize(final Input input, final XdmNode object) throws IOException, SaxonApiException {
|
||||
final SerializedDocument doc;
|
||||
if (input instanceof AbstractInput && ((AbstractInput) input).getLength() < getInMemoryLimit()) {
|
||||
doc = new ByteArraySerializedDocument();
|
||||
doc = new ByteArraySerializedDocument(this.processor);
|
||||
} else {
|
||||
doc = new FileSerializedDocument();
|
||||
doc = new FileSerializedDocument(this.processor);
|
||||
}
|
||||
doc.serialize(object);
|
||||
return doc;
|
||||
|
|
|
|||
|
|
@ -26,21 +26,19 @@ import javax.xml.transform.URIResolver;
|
|||
import javax.xml.transform.dom.DOMSource;
|
||||
|
||||
import org.oclc.purl.dsdl.svrl.SchematronOutput;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import de.kosit.validationtool.impl.CollectingErrorEventHandler;
|
||||
import de.kosit.validationtool.impl.ContentRepository;
|
||||
import de.kosit.validationtool.impl.ConversionService;
|
||||
import de.kosit.validationtool.impl.ObjectFactory;
|
||||
import de.kosit.validationtool.impl.Scenario;
|
||||
import de.kosit.validationtool.model.reportInput.CreateReportInput;
|
||||
import de.kosit.validationtool.model.reportInput.ValidationResultsSchematron;
|
||||
|
||||
import net.sf.saxon.s9api.DOMDestination;
|
||||
import net.sf.saxon.dom.NodeOverNodeInfo;
|
||||
import net.sf.saxon.s9api.SaxonApiException;
|
||||
import net.sf.saxon.s9api.XdmDestination;
|
||||
import net.sf.saxon.s9api.XdmNode;
|
||||
import net.sf.saxon.s9api.XsltTransformer;
|
||||
|
||||
|
|
@ -53,7 +51,7 @@ import net.sf.saxon.s9api.XsltTransformer;
|
|||
@Slf4j
|
||||
public class SchematronValidationAction implements CheckAction {
|
||||
|
||||
private final ContentRepository repository;
|
||||
private final URIResolver resolver;
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
|
|
@ -67,18 +65,19 @@ public class SchematronValidationAction implements CheckAction {
|
|||
try {
|
||||
final XsltTransformer transformer = validation.getExecutable().load();
|
||||
// resolving nur relative zum Repository
|
||||
final URIResolver resolver = this.repository.createResolver();
|
||||
transformer.setURIResolver(resolver);
|
||||
transformer.setURIResolver(this.resolver);
|
||||
final CollectingErrorEventHandler e = new CollectingErrorEventHandler();
|
||||
transformer.setMessageListener(e);
|
||||
|
||||
final Document result = ObjectFactory.createDocumentBuilder(false).newDocument();
|
||||
transformer.setDestination(new DOMDestination(result));
|
||||
final XdmDestination result = new XdmDestination();
|
||||
transformer.setDestination(result);
|
||||
transformer.setInitialContextNode(document);
|
||||
transformer.transform();
|
||||
|
||||
final ValidationResultsSchematron.Results r = new ValidationResultsSchematron.Results();
|
||||
r.setSchematronOutput(this.conversionService.readDocument(new DOMSource(result), SchematronOutput.class));
|
||||
r.setSchematronOutput(this.conversionService.readDocument(
|
||||
new DOMSource(NodeOverNodeInfo.wrap(result.getXdmNode().getUnderlyingNode()).getOwnerDocument()),
|
||||
SchematronOutput.class));
|
||||
s.setResults(r);
|
||||
|
||||
} catch (final SaxonApiException e) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
package de.kosit.validationtool.impl.xml;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import javax.xml.validation.Validator;
|
||||
|
||||
import org.xml.sax.SAXNotRecognizedException;
|
||||
import org.xml.sax.SAXNotSupportedException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
|
||||
|
||||
/**
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BaseResolvingStrategy implements ResolvingConfigurationStrategy {
|
||||
|
||||
protected static final String DISSALLOW_DOCTYPE_DECL_FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
|
||||
|
||||
protected static final String LOAD_EXTERNAL_DTD_FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
|
||||
|
||||
protected static final String FEATURE_SECURE_PROCESSING = "http://javax.xml.XMLConstants/feature/secure-processing";
|
||||
|
||||
private static final String ORACLE_XERCES_CLASS = "com.sun.org.apache.xerces.internal.impl.Constants";
|
||||
|
||||
public static void forceOpenJdkXmlImplementation() {
|
||||
if (!isOpenJdkXmlImplementationAvailable()) {
|
||||
throw new IllegalStateException("No OpenJDK version of XERCES found");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isOpenJdkXmlImplementationAvailable() {
|
||||
try {
|
||||
Class.forName(ORACLE_XERCES_CLASS);
|
||||
return true;
|
||||
} catch (final ClassNotFoundException e) {
|
||||
log.warn("No oracle JDK version of XERCES found. Configured security features may not have any effect.");
|
||||
log.warn("Please take care of XML security while checking your xml contents");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void setProperty(final PropertySetter setter, final boolean lenient, final String errorMessage) {
|
||||
try {
|
||||
setter.apply();
|
||||
} catch (final SAXNotRecognizedException | SAXNotSupportedException e) {
|
||||
|
||||
if (lenient) {
|
||||
log.warn(errorMessage);
|
||||
log.debug(e.getMessage(), e);
|
||||
} else {
|
||||
throw new IllegalStateException(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void allowExternalSchema(final Validator validator, final String... scheme) {
|
||||
allowExternalSchema(validator, false, scheme);
|
||||
}
|
||||
|
||||
protected void allowExternalSchema(final SchemaFactory schemaFactory, final String... scheme) {
|
||||
allowExternalSchema(schemaFactory, false, scheme);
|
||||
}
|
||||
|
||||
protected void allowExternalSchema(final Validator validator, final boolean lenient, final String... schemes) {
|
||||
final String schemeString = String.join(",", schemes);
|
||||
setProperty(() -> validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, schemeString), lenient, format(
|
||||
"Can set external schema access to schemes (%s). Maybe an unsupported JAXP implementation is used.", schemeString));
|
||||
}
|
||||
|
||||
protected void allowExternalSchema(final SchemaFactory schemaFactory, final boolean lenient, final String... schemes) {
|
||||
final String schemeString = String.join(",", schemes);
|
||||
setProperty(() -> schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, schemeString), lenient, format(
|
||||
"Can set external schema access to schemes (%s). Maybe an unsupported JAXP implementation is used.", schemeString));
|
||||
}
|
||||
|
||||
protected void disableExternalEntities(final Validator validator) {
|
||||
disableExternalEntities(validator, false);
|
||||
}
|
||||
|
||||
protected void disableExternalEntities(final SchemaFactory schemaFactory) {
|
||||
disableExternalEntities(schemaFactory, false);
|
||||
}
|
||||
|
||||
protected void disableExternalEntities(final Validator validator, final boolean lenient) {
|
||||
log.debug("Try to disable extern DTD access");
|
||||
setProperty(() -> validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""), lenient,
|
||||
"Can not disable external DTD access. Maybe an unsupported JAXP implementation is used.");
|
||||
|
||||
}
|
||||
|
||||
protected void disableExternalEntities(final SchemaFactory schemaFactory, final boolean lenient) {
|
||||
log.debug("Try to disable extern DTD access");
|
||||
setProperty(() -> schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""), lenient,
|
||||
"Can not disable external DTD access. Maybe an unsupported JAXP implementation is used.");
|
||||
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface PropertySetter {
|
||||
|
||||
void apply() throws SAXNotRecognizedException, SAXNotSupportedException;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
package de.kosit.validationtool.impl;
|
||||
package de.kosit.validationtool.impl.xml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
|
@ -25,14 +25,13 @@ import java.io.Reader;
|
|||
import java.net.URI;
|
||||
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.URIResolver;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import net.sf.saxon.Configuration;
|
||||
import net.sf.saxon.lib.UnparsedTextURIResolver;
|
||||
import net.sf.saxon.trans.XPathException;
|
||||
|
||||
/**
|
||||
|
|
@ -41,24 +40,24 @@ import net.sf.saxon.trans.XPathException;
|
|||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
public class RelativeUriResolver implements URIResolver, UnparsedTextURIResolver {
|
||||
@RequiredArgsConstructor()
|
||||
public class RelativeUriResolver implements URIResolver {
|
||||
|
||||
/** the base uri */
|
||||
private final URI baseUri;
|
||||
|
||||
@Override
|
||||
public Source resolve(final String href, final String base) {
|
||||
public Source resolve(final String href, final String base) throws TransformerException {
|
||||
final URI resolved = resolve(URI.create(href), URI.create(base));
|
||||
if (isUnderBaseUri(resolved)) {
|
||||
try {
|
||||
return new StreamSource(resolved.toURL().openStream(), resolved.toASCIIString());
|
||||
} catch (final IOException e) {
|
||||
|
||||
throw new IllegalStateException(String.format("Can not resolve required %s", href), e);
|
||||
throw new TransformerException(String.format("Can not resolve required %s", href), e);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException(String
|
||||
throw new TransformerException(String
|
||||
.format("The resolved transformation artifact %s is not within the configured repository %s", resolved, this.baseUri));
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +86,7 @@ public class RelativeUriResolver implements URIResolver, UnparsedTextURIResolver
|
|||
return r.startsWith(base);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public Reader resolve(final URI absoluteURI, final String encoding, final Configuration config) throws XPathException {
|
||||
if (isUnderBaseUri(absoluteURI)) {
|
||||
try {
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package de.kosit.validationtool.impl.xml;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.xml.transform.URIResolver;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import javax.xml.validation.Validator;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import net.sf.saxon.s9api.Processor;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
@Slf4j
|
||||
public class StrictLocalResolvingStrategy extends StrictRelativeResolvingStrategy {
|
||||
|
||||
/**
|
||||
* e.g. don't allow any scheme
|
||||
*/
|
||||
|
||||
@Override
|
||||
public SchemaFactory createSchemaFactory() {
|
||||
final SchemaFactory schemaFactory = super.createSchemaFactory();
|
||||
allowExternalSchema(schemaFactory, "file", "jar");
|
||||
return schemaFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Processor createProcessor() {
|
||||
return super.createProcessor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URIResolver createResolver(final URI repository) {
|
||||
// intentionally return 'null', since all resolving is configured with the other objects
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Validator createValidator(final Schema schema) {
|
||||
final Validator validator = super.createValidator(schema);
|
||||
allowExternalSchema(validator, "file", "jar");
|
||||
return validator;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
package de.kosit.validationtool.impl.xml;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.transform.Result;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.URIResolver;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import javax.xml.validation.Validator;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import net.sf.saxon.Configuration;
|
||||
import net.sf.saxon.expr.XPathContext;
|
||||
import net.sf.saxon.lib.CollectionFinder;
|
||||
import net.sf.saxon.lib.Feature;
|
||||
import net.sf.saxon.lib.FeatureKeys;
|
||||
import net.sf.saxon.lib.OutputURIResolver;
|
||||
import net.sf.saxon.lib.ResourceCollection;
|
||||
import net.sf.saxon.lib.UnparsedTextURIResolver;
|
||||
import net.sf.saxon.s9api.Processor;
|
||||
import net.sf.saxon.trans.XPathException;
|
||||
|
||||
/**
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class StrictRelativeResolvingStrategy extends BaseResolvingStrategy {
|
||||
|
||||
private static class SecureUriResolver implements CollectionFinder, OutputURIResolver, UnparsedTextURIResolver {
|
||||
|
||||
public static final String MESSAGE = "Configuration error. Resolving ist not allowed";
|
||||
|
||||
@Override
|
||||
public OutputURIResolver newInstance() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result resolve(final String href, final String base) throws TransformerException {
|
||||
throw new IllegalStateException(MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(final Result result) throws TransformerException {
|
||||
throw new IllegalStateException(MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reader resolve(final URI absoluteURI, final String encoding, final Configuration config) throws XPathException {
|
||||
throw new IllegalStateException(MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceCollection findCollection(final XPathContext context, final String collectionURI) throws XPathException {
|
||||
throw new IllegalStateException(MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* e.g. don't allow any scheme
|
||||
*/
|
||||
private static final String EMPTY_SCHEME = "";
|
||||
|
||||
@Override
|
||||
public SchemaFactory createSchemaFactory() {
|
||||
final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
disableExternalEntities(sf);
|
||||
allowExternalSchema(sf, "file");
|
||||
return sf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Processor createProcessor() {
|
||||
final Processor processor = new Processor(false);
|
||||
// verhindere global im Prinzip alle resolving strategien
|
||||
final SecureUriResolver resolver = new SecureUriResolver();
|
||||
processor.getUnderlyingConfiguration().setCollectionFinder(resolver);
|
||||
processor.getUnderlyingConfiguration().setOutputURIResolver(resolver);
|
||||
processor.getUnderlyingConfiguration().setUnparsedTextURIResolver(resolver);
|
||||
|
||||
// grundsätzlich Feature-konfiguration:
|
||||
processor.setConfigurationProperty(Feature.DTD_VALIDATION, false);
|
||||
processor.setConfigurationProperty(Feature.ENTITY_RESOLVER_CLASS, "");
|
||||
processor.setConfigurationProperty(Feature.XINCLUDE, false);
|
||||
processor.setConfigurationProperty(Feature.ALLOW_EXTERNAL_FUNCTIONS, false);
|
||||
|
||||
// Konfiguration des zu verwendenden Parsers, wenn Saxon selbst einen erzeugen muss, bspw. beim XSL parsen
|
||||
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(FEATURE_SECURE_PROCESSING), true);
|
||||
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(DISSALLOW_DOCTYPE_DECL_FEATURE), true);
|
||||
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(LOAD_EXTERNAL_DTD_FEATURE), false);
|
||||
return processor;
|
||||
}
|
||||
|
||||
private static String encode(final String input) {
|
||||
try {
|
||||
return URLEncoder.encode(input, StandardCharsets.UTF_8.name());
|
||||
} catch (final UnsupportedEncodingException e) {
|
||||
throw new IllegalStateException("Error encoding property while initializing saxon", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URIResolver createResolver(final URI repositoryURI) {
|
||||
return new RelativeUriResolver(repositoryURI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Validator createValidator(final Schema schema) {
|
||||
if (schema == null) {
|
||||
throw new IllegalArgumentException("No schema supplied. Can not create validator");
|
||||
}
|
||||
forceOpenJdkXmlImplementation();
|
||||
final Validator validator = schema.newValidator();
|
||||
disableExternalEntities(validator);
|
||||
allowExternalSchema(validator, "file" /* allow nothing external */);
|
||||
return validator;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue