From e8ff6765a4985000342c10d4fc6720e0abe881aa Mon Sep 17 00:00:00 2001
From: Renzo Kottmann
Date: Wed, 18 Mar 2020 15:51:46 +0100
Subject: [PATCH 01/36] Translate to English
---
.../kosit/validationtool/api/AcceptRecommendation.java | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/main/java/de/kosit/validationtool/api/AcceptRecommendation.java b/src/main/java/de/kosit/validationtool/api/AcceptRecommendation.java
index 3d07933..c491201 100644
--- a/src/main/java/de/kosit/validationtool/api/AcceptRecommendation.java
+++ b/src/main/java/de/kosit/validationtool/api/AcceptRecommendation.java
@@ -1,21 +1,21 @@
package de.kosit.validationtool.api;
/**
- * Status der Empfehlung.
+ * Tri-state describtion of a Recommendation.
*/
public enum AcceptRecommendation {
/**
- * Nicht definiert, weil eine Evaluierung nicht durchgeführt wurde, oder nicht durchgeführt werden konnte.
+ * The evaluation of the overall validation could not be computed.
*/
UNDEFINED,
/**
- * Das Dokument ist gemäß Konfiguration valide und kann akzeptiert werden.
+ * Recommendation is to accept input based on the evaluation of the overall validation.
*/
ACCEPTABLE,
/**
- * Das Dokuemnt ist gemäß Konfiguration invalide und sollte NICHT akzeptiert werden.
+ * Recommendation is to reject input based on the evaluation of the overall validation.
*/
REJECT
-}
\ No newline at end of file
+}
From 7999fa179022c2b6730b7ad074e850479c9b99d9 Mon Sep 17 00:00:00 2001
From: Renzo Kottmann
Date: Wed, 18 Mar 2020 15:52:37 +0100
Subject: [PATCH 02/36] Translate getAcceptRecommendation javadoc to English
---
.../java/de/kosit/validationtool/api/Result.java | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/src/main/java/de/kosit/validationtool/api/Result.java b/src/main/java/de/kosit/validationtool/api/Result.java
index 11dfa20..5581e29 100644
--- a/src/main/java/de/kosit/validationtool/api/Result.java
+++ b/src/main/java/de/kosit/validationtool/api/Result.java
@@ -9,7 +9,7 @@ import net.sf.saxon.s9api.XdmNode;
/**
* API Rückgabe Objekt des Ergebnisses des Validierungsprozesses.
- *
+ *
* @author Andreas Penski
*/
public interface Result {
@@ -17,7 +17,7 @@ public interface Result {
/**
* Zeigt an, ob die Verarbeitung durch den Validator erfolgreich durchlaufen wurde. Diese Funktion macht ausdrücklich
* keine Aussage über die zur Akzeptanz.
- *
+ *
* @return true, wenn die Verarbeitung komplett und erfolgreich durchlaufen wurde
* @see #getAcceptRecommendation()
*/
@@ -25,7 +25,7 @@ public interface Result {
/**
* Gibt eine Liste mit Verarbeitungsfehlermeldungen zurück.
- *
+ *
* @return Liste mit Fehlermeldungen
*/
List getProcessingErrors();
@@ -36,7 +36,9 @@ public interface Result {
XdmNode getReport();
/**
- * Das evaluierte Ergebnis.
+ * The Recommendation based on the evaluation of this Result.
+ *
+ * @return AcceptRecommendation
*/
AcceptRecommendation getAcceptRecommendation();
@@ -62,21 +64,21 @@ public interface Result {
/**
* Liefert die Ergebnisse der Schematron-Prüfungen, in der Reihenfolge der Szenario-Konfiguration.
- *
+ *
* @return Liste mit Schematron-Ergebnissen
*/
List getSchematronResult();
/**
* Liefert ein true, wenn keine Schema-Violations vorhanden sind.
- *
+ *
* @return true wenn Schema-valide
*/
boolean isSchemaValid();
/**
* Liefert ein true, wenn der Prüfling eine well-formed XML-Datei ist.
- *
+ *
* @return true wenn well-formed
*/
boolean isWellformed();
From aa53eaea7311078f747055a43a812f759245c54f Mon Sep 17 00:00:00 2001
From: Renzo Kottmann
Date: Wed, 18 Mar 2020 18:07:47 +0100
Subject: [PATCH 03/36] Add a little englisch foc to acceptMatch Element
---
src/main/model/xsd/scenarios.xsd | 81 ++++++++++++++++----------------
1 file changed, 40 insertions(+), 41 deletions(-)
diff --git a/src/main/model/xsd/scenarios.xsd b/src/main/model/xsd/scenarios.xsd
index 1b2f9d9..2f41a1f 100644
--- a/src/main/model/xsd/scenarios.xsd
+++ b/src/main/model/xsd/scenarios.xsd
@@ -1,4 +1,4 @@
-
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -68,72 +66,73 @@
-
-
-
+
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
From fa1dbe75fdfba8ed8e9179a8611ce9a922ecd17c Mon Sep 17 00:00:00 2001
From: Renzo Kottmann
Date: Wed, 18 Mar 2020 18:16:35 +0100
Subject: [PATCH 04/36] Add accept documentation about acceptRecommendation
---
docs/api.md | 18 ++++++++++++++++++
docs/architecture.md | 10 ++++++++--
2 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/docs/api.md b/docs/api.md
index 848741c..e537f83 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -89,3 +89,21 @@ Initializing all XML artifacts and XSLT-executables is expensive. The `Check` in
The only input `de.kosit.validationtool.api.Input` which can be created by various methods of `de.kosit.validationtool.api.InputFactory`.
The `InputFactory` calculates a hash sum for each Input which is also written to the Report. _SHA-256_ from the JDK is the default algorithm. It can be changed using the `read`-methods of `InputFactory`.
+
+## Accept Recommendation and Accept Match
+
+A tri-state Object `AcceptRecommendation` can be retrieved from the `Result` using `getAcceptRecommendation()`.
+
+The three defined states are:
+
+1. `UNDEFINED` i.e. the evaluation of the overall validation could not be computed.
+2. `ACCEPTABLE` i.e. the recommendation is to accept input based on the evaluation of the overall validation.
+3. `REJECT` i.e. the recommendation is to reject input based on the evaluation of the overall validation.
+
+By default it is `UNDEFINED`.
+
+### Accept Match in Scenario Configuration
+
+For your own configuration you can add an `acceptMatch` element in each scenario. It can contain in XPATH expression over your own defined `Report` to compute a boolean. An XPATH expression evaluating to true will lead to an `ACCEPTABLE` amd otherwise to a `REJECT` recommendation.
+
+This allows to have own control over what validation result is to be considered acceptable for your own application context.
diff --git a/docs/architecture.md b/docs/architecture.md
index fc9b2a2..18eb16c 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -13,9 +13,9 @@ the validation and generates a report in XML format. This report is then the inp
The validator reports valid/invalid, a configuration reports acceptance/rejection!
-## General process
+## General default process
-The general process is like this:
+The general process is like this (the default is defined in `DefaultCheck`):
```mermaid
@@ -30,6 +30,7 @@ sequenceDiagram
e->>e: validate Schematron
e->>e: create Validator Report
e->>+c: execute configuration report generator
+ e->>e: Compute Recommendation
```
@@ -50,3 +51,8 @@ sequenceDiagram
6. *execute configuration report generator*
The Validator will search for the XSLT as configured in scenario.xml and execute it with the Validator Report as input
+7. compute Recommendation
+
+ In case a scenario contains an `acceptMatch` element with an XPATH expression, this expression will be executed.
+
+ In case the XPATH returns `true`, the recommendation will be set to `ACCEPT` else to `REJECT`. In case no such XPATH is defined it is `UNDEFINED`.
From 7a86f049ace4baa2d213016737773ca72c140789 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Tue, 21 Apr 2020 08:34:56 +0200
Subject: [PATCH 05/36] Refactor: don't reuse JAXB objects for internal
configuration; create a Configuration interface.
---
.../api/CheckConfiguration.java | 65 +++--
.../validationtool/api/Configuration.java | 59 +++++
.../de/kosit/validationtool/cmd/Daemon.java | 10 +-
.../de/kosit/validationtool/cmd/Health.java | 72 +++---
.../config/BaseConfiguration.java | 92 +++++++
.../config/ConfigurationBuilder.java | 10 +
.../config/LoadConfiguration.java | 178 ++++++++++++++
.../config/ScenarioBuilder.java | 72 ++++++
.../impl/ContentRepository.java | 74 +++++-
.../validationtool/impl/DefaultCheck.java | 17 +-
.../validationtool/impl/ObjectFactory.java | 2 +-
.../validationtool/impl/ResolvingMode.java | 45 ++++
.../kosit/validationtool/impl/Scenario.java | 83 +++++++
.../impl/ScenarioRepository.java | 126 ++--------
.../impl/model/BaseScenario.java | 224 ------------------
.../impl/tasks/CheckAction.java | 4 +-
.../impl/tasks/ComputeAcceptanceAction.java | 11 +-
.../impl/tasks/CreateReportAction.java | 13 +-
.../impl/tasks/ScenarioSelectionAction.java | 10 +-
.../impl/tasks/SchemaValidationAction.java | 14 +-
.../tasks/SchematronValidationAction.java | 15 +-
src/main/model/binding/global.xjb | 3 -
.../impl/ContentRepositoryTest.java | 20 +-
.../impl/ConversionServiceTest.java | 8 +-
.../impl/ScenarioRepositoryTest.java | 101 +++++---
.../validationtool/impl/VersioningTest.java | 11 +-
.../tasks/ComputeAcceptanceActionTest.java | 16 +-
.../impl/tasks/SchemaValidatorActionTest.java | 4 +-
.../tasks/SchematronValidationActionTest.java | 6 +-
.../impl/tasks/TestBagBuilder.java | 23 +-
30 files changed, 871 insertions(+), 517 deletions(-)
create mode 100644 src/main/java/de/kosit/validationtool/api/Configuration.java
create mode 100644 src/main/java/de/kosit/validationtool/config/BaseConfiguration.java
create mode 100644 src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
create mode 100644 src/main/java/de/kosit/validationtool/config/LoadConfiguration.java
create mode 100644 src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
create mode 100644 src/main/java/de/kosit/validationtool/impl/ResolvingMode.java
create mode 100644 src/main/java/de/kosit/validationtool/impl/Scenario.java
delete mode 100644 src/main/java/de/kosit/validationtool/impl/model/BaseScenario.java
diff --git a/src/main/java/de/kosit/validationtool/api/CheckConfiguration.java b/src/main/java/de/kosit/validationtool/api/CheckConfiguration.java
index e507daf..31f6aff 100644
--- a/src/main/java/de/kosit/validationtool/api/CheckConfiguration.java
+++ b/src/main/java/de/kosit/validationtool/api/CheckConfiguration.java
@@ -20,24 +20,31 @@
package de.kosit.validationtool.api;
import java.net.URI;
+import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
-import de.kosit.validationtool.impl.RelativeUriResolver;
+import de.kosit.validationtool.config.LoadConfiguration;
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.Scenario;
+
+import net.sf.saxon.s9api.Processor;
/**
* Zentrale Konfigration einer Prüf-Instanz.
*
* @author Andreas Penski
+ * @deprecated since 2.0 use {@link Configuration} instead
*/
@Getter
@Setter
@Slf4j
@RequiredArgsConstructor
-public class CheckConfiguration {
+@Deprecated
+public class CheckConfiguration implements Configuration {
/**
* URL, die auf die scenerio.xml Datei zeigt.
@@ -49,22 +56,52 @@ public class CheckConfiguration {
*/
private URI scenarioRepository;
+ private LoadConfiguration delegate;
- /**
- * Liefert das Repository mit den Artefakten der einzelnen Szenarien.
- *
- * @return uri die durch entsprechende resolver aufgelöst werden kann
- */
- public URI getScenarioRepository() {
- if (this.scenarioRepository == null) {
- this.scenarioRepository = createDefaultRepository();
+ private LoadConfiguration getDelegate() {
+ if (this.delegate == null) {
+ this.delegate = Configuration.load(this.scenarioDefinition, this.scenarioRepository);
}
- return this.scenarioRepository;
+ return this.delegate;
}
- private URI createDefaultRepository() {
- log.info("Creating default scenario repository (alongside scenario definition)");
- return RelativeUriResolver.resolve(URI.create("."), this.scenarioDefinition);
+ @Override
+ public List getScenarios() {
+ return getDelegate().getScenarios();
}
+ @Override
+ public Scenario getFallbackScenario() {
+ return getDelegate().getFallbackScenario();
+ }
+
+ @Override
+ public void build() {
+ getDelegate().build();
+ }
+
+ @Override
+ public String getDate() {
+ return getDelegate().getDate();
+ }
+
+ @Override
+ public String getName() {
+ return getDelegate().getName();
+ }
+
+ @Override
+ public String getAuthor() {
+ return getDelegate().getAuthor();
+ }
+
+ @Override
+ public Processor getProcessor() {
+ return getDelegate().getProcessor();
+ }
+
+ @Override
+ public ContentRepository getContentRepository() {
+ return getDelegate().getContentRepository();
+ }
}
diff --git a/src/main/java/de/kosit/validationtool/api/Configuration.java b/src/main/java/de/kosit/validationtool/api/Configuration.java
new file mode 100644
index 0000000..ac99275
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/api/Configuration.java
@@ -0,0 +1,59 @@
+package de.kosit.validationtool.api;
+
+import java.net.URI;
+import java.util.List;
+
+import de.kosit.validationtool.config.ConfigurationBuilder;
+import de.kosit.validationtool.config.LoadConfiguration;
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.Scenario;
+
+import net.sf.saxon.s9api.Processor;
+
+/**
+ * Configuration of the actual {@link Check} instance. This is a contruct and can be used implemented by custom
+ * configuration classes. There are two implementations supported out of the box:
+ *
+ *
+ * {@link LoadConfiguration} implements loading {@link Check} configurations from a scenario.xml file
+ * Using a builder style api {@link de.kosit.validationtool.config.ConfigurationBuilder}to configure the
+ * {@link Check}
+ *
+ *
+ * Both methods can be used via convinience methods. See below.
+ *
+ * @author Andreas Penski
+ */
+
+public interface Configuration {
+
+ List getScenarios();
+
+ static LoadConfiguration load(final URI scenarioDefinition) {
+ return load(scenarioDefinition, null);
+ }
+
+ static LoadConfiguration load(final URI scenarioDefinition, final URI repository) {
+ final LoadConfiguration config = new LoadConfiguration(scenarioDefinition, repository);
+ config.build();
+ return config;
+ }
+
+ static ConfigurationBuilder create() {
+ return new ConfigurationBuilder();
+ }
+
+ Scenario getFallbackScenario();
+
+ void build();
+
+ String getAuthor();
+
+ String getName();
+
+ String getDate();
+
+ Processor getProcessor();
+
+ ContentRepository getContentRepository();
+}
diff --git a/src/main/java/de/kosit/validationtool/cmd/Daemon.java b/src/main/java/de/kosit/validationtool/cmd/Daemon.java
index 560c039..91aceda 100644
--- a/src/main/java/de/kosit/validationtool/cmd/Daemon.java
+++ b/src/main/java/de/kosit/validationtool/cmd/Daemon.java
@@ -27,11 +27,11 @@ import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.Check;
import de.kosit.validationtool.api.CheckConfiguration;
+import de.kosit.validationtool.api.Configuration;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.DefaultCheck;
import de.kosit.validationtool.impl.ObjectFactory;
import de.kosit.validationtool.impl.input.SourceInput;
-import de.kosit.validationtool.model.scenarios.Scenarios;
/**
* HTTP-Daemon für die Bereitstellung der Prüf-Funktionalität via http.
@@ -98,10 +98,10 @@ class Daemon {
@Slf4j
static class HealthHandler implements HttpHandler {
- private final Scenarios scenarios;
+ private final Configuration scenarios;
- HealthHandler(final Scenarios scenarios) {
- this.scenarios = scenarios;
+ HealthHandler(final Configuration config) {
+ this.scenarios = config;
}
@Override
@@ -188,7 +188,7 @@ class Daemon {
server = HttpServer.create(new InetSocketAddress(this.hostName, this.port), 0);
final DefaultCheck check = new DefaultCheck(config);
server.createContext("/", new HttpServerHandler(check));
- server.createContext("/health", new HealthHandler(check.getRepository().getScenarios()));
+ server.createContext("/health", new HealthHandler(config));
server.setExecutor(Executors.newFixedThreadPool(this.threadCount));
server.start();
log.info("Server unter Port {} ist erfolgreich gestartet", this.port);
diff --git a/src/main/java/de/kosit/validationtool/cmd/Health.java b/src/main/java/de/kosit/validationtool/cmd/Health.java
index e3056a6..2694142 100644
--- a/src/main/java/de/kosit/validationtool/cmd/Health.java
+++ b/src/main/java/de/kosit/validationtool/cmd/Health.java
@@ -1,8 +1,6 @@
package de.kosit.validationtool.cmd;
import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -10,7 +8,8 @@ import org.w3c.dom.Node;
import lombok.extern.slf4j.Slf4j;
-import de.kosit.validationtool.model.scenarios.Scenarios;
+import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.impl.ObjectFactory;
/**
* Klasse zur Erzeugung Health Xml , die optiamle Status.
@@ -26,15 +25,15 @@ class Health {
private final long totalMemory;
- private final Scenarios scenarios;
+ private final Configuration config;
- Health(Scenarios scenarios) {
+ Health(final Configuration config) {
- Runtime runtime = Runtime.getRuntime();
- freeMemory = runtime.freeMemory();
- maxMemory = runtime.maxMemory();
- totalMemory = runtime.totalMemory();
- this.scenarios = scenarios;
+ final Runtime runtime = Runtime.getRuntime();
+ this.freeMemory = runtime.freeMemory();
+ this.maxMemory = runtime.maxMemory();
+ this.totalMemory = runtime.totalMemory();
+ this.config = config;
}
/**
@@ -42,20 +41,13 @@ class Health {
*
*/
Document writeHealthXml() {
- DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
- DocumentBuilder dBuilder;
- Document doc = null;
- try {
- dBuilder = dbFactory.newDocumentBuilder();
- doc = dBuilder.newDocument();
- Element rootElement = doc.createElementNS("https://localhost:8080/Health", "Health");
- doc.appendChild(rootElement);
- rootElement.appendChild(getMemory(doc, freeMemory, maxMemory, totalMemory));
- rootElement.appendChild(getState(doc));
- rootElement.appendChild(getScenario(doc, scenarios));
- } catch (ParserConfigurationException e) {
- log.error("Fehler beim Schreiben der Status-Informationen", e);
- }
+ final DocumentBuilder dBuilder = ObjectFactory.createDocumentBuilder(false);
+ final Document doc = dBuilder.newDocument();
+ final Element rootElement = doc.createElementNS("https://localhost:8080/Health", "Health");
+ doc.appendChild(rootElement);
+ rootElement.appendChild(getMemory(doc, this.freeMemory, this.maxMemory, this.totalMemory));
+ rootElement.appendChild(getState(doc));
+ rootElement.appendChild(getScenario(doc, this.config));
return doc;
}
@@ -65,10 +57,10 @@ class Health {
* @param doc Vom Typ Dokument.
*
*/
- private Node getState(Document doc) {
- Element state = doc.createElement("state");
+ private static Node getState(final Document doc) {
+ final Element state = doc.createElement("state");
state.setAttribute("indicator", "OK");
- Element stateNode = doc.createElement("message");
+ final Element stateNode = doc.createElement("message");
stateNode.appendChild(doc.createTextNode("System is up and running normally"));
state.appendChild(stateNode);
return state;
@@ -78,13 +70,13 @@ class Health {
* Methode, die schreibt das Scnarios Information Node im Xml File
*
* @param doc Vom Typ Dokument .
- * @param scenarios Vom Typ {@link Scenarios} das verwendete scenario.
+ * @param config Vom Typ {@link Configuration} das verwendete scenario.
*
*/
- private Node getScenario(Document doc, Scenarios scenarios) {
- Element scenario = doc.createElement("scenario");
- Element scenarioNameNode = doc.createElement("name");
- scenarioNameNode.appendChild(doc.createTextNode(scenarios.getName()));
+ private static Node getScenario(final Document doc, final Configuration config) {
+ final Element scenario = doc.createElement("scenario");
+ final Element scenarioNameNode = doc.createElement("name");
+ scenarioNameNode.appendChild(doc.createTextNode(config.getName()));
scenario.appendChild(scenarioNameNode);
return scenario;
}
@@ -98,18 +90,18 @@ class Health {
* @param totalMemory Vom Typ long , der Gesamte speicher.
*
*/
- private static Node getMemory(Document doc, long freeMemory, long maxMemory, long totalMemory) {
- Element memory = doc.createElement("memoryState");
- String freeM = Long.toString(freeMemory);
- Element freeMNode = doc.createElement("freeMemory");
+ private static Node getMemory(final Document doc, final long freeMemory, final long maxMemory, final long totalMemory) {
+ final Element memory = doc.createElement("memoryState");
+ final String freeM = Long.toString(freeMemory);
+ final Element freeMNode = doc.createElement("freeMemory");
freeMNode.appendChild(doc.createTextNode(freeM));
memory.appendChild(freeMNode);
- String maxM = Long.toString(maxMemory);
- Element maxMNode = doc.createElement("maxMemory");
+ final String maxM = Long.toString(maxMemory);
+ final Element maxMNode = doc.createElement("maxMemory");
maxMNode.appendChild(doc.createTextNode(maxM));
memory.appendChild(maxMNode);
- String totalM = Long.toString(totalMemory);
- Element totalMNode = doc.createElement("totalMemory");
+ final String totalM = Long.toString(totalMemory);
+ final Element totalMNode = doc.createElement("totalMemory");
totalMNode.appendChild(doc.createTextNode(totalM));
memory.appendChild(totalMNode);
return memory;
diff --git a/src/main/java/de/kosit/validationtool/config/BaseConfiguration.java b/src/main/java/de/kosit/validationtool/config/BaseConfiguration.java
new file mode 100644
index 0000000..167099a
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/BaseConfiguration.java
@@ -0,0 +1,92 @@
+package de.kosit.validationtool.config;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.impl.ObjectFactory;
+import de.kosit.validationtool.impl.Scenario;
+
+import net.sf.saxon.s9api.Processor;
+
+/**
+ * Base configuration class.
+ *
+ * @author Andreas Penski
+ */
+@Slf4j
+public abstract class BaseConfiguration implements Configuration {
+
+ @RequiredArgsConstructor
+ @Getter
+ @Setter
+ protected static class RuntimeArtefacts {
+
+ private final List scenarios;
+
+ private final Scenario fallbackScenario;
+
+ private String name;
+
+ private String author;
+
+ private String date;
+ }
+
+ private RuntimeArtefacts artefacts;
+
+ protected abstract RuntimeArtefacts buildArtefacts();
+
+ @Override
+ public void build() {
+ if (this.artefacts != null) {
+ log.warn("Configuration already complete. Will drop previous artefacts and build again");
+ }
+ this.artefacts = buildArtefacts();
+ }
+
+ @Override
+ public List getScenarios() {
+ assertBuild();
+ return this.artefacts.getScenarios();
+ }
+
+ @Override
+ public Scenario getFallbackScenario() {
+ assertBuild();
+ return this.artefacts.getFallbackScenario();
+ }
+
+ private void assertBuild() {
+ if (this.artefacts == null) {
+ throw new IllegalStateException("Configuration");
+ }
+ }
+
+ @Override
+ public Processor getProcessor() {
+ return ObjectFactory.createProcessor();
+ }
+
+ @Override
+ public String getAuthor() {
+ assertBuild();
+ return this.artefacts.getAuthor();
+ }
+
+ @Override
+ public String getName() {
+ assertBuild();
+ return this.artefacts.getName();
+ }
+
+ @Override
+ public String getDate() {
+ assertBuild();
+ return this.artefacts.getDate();
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
new file mode 100644
index 0000000..27dc6b5
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
@@ -0,0 +1,10 @@
+package de.kosit.validationtool.config;
+
+/**
+ * @author Andreas Penski
+ */
+public class ConfigurationBuilder {
+
+ private ScenarioBuilder scenarioBuilder;
+
+}
diff --git a/src/main/java/de/kosit/validationtool/config/LoadConfiguration.java b/src/main/java/de/kosit/validationtool/config/LoadConfiguration.java
new file mode 100644
index 0000000..e9f52e0
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/LoadConfiguration.java
@@ -0,0 +1,178 @@
+package de.kosit.validationtool.config;
+
+import static org.apache.commons.lang3.StringUtils.startsWith;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+import de.kosit.validationtool.api.Check;
+import de.kosit.validationtool.api.InputFactory;
+import de.kosit.validationtool.impl.CollectingErrorEventHandler;
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.ConversionService;
+import de.kosit.validationtool.impl.RelativeUriResolver;
+import de.kosit.validationtool.impl.Scenario;
+import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.impl.tasks.DocumentParseAction;
+import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
+import de.kosit.validationtool.model.scenarios.CreateReportType;
+import de.kosit.validationtool.model.scenarios.ScenarioType;
+import de.kosit.validationtool.model.scenarios.Scenarios;
+
+import net.sf.saxon.s9api.QName;
+import net.sf.saxon.s9api.XdmNode;
+import net.sf.saxon.s9api.XdmNodeKind;
+
+/**
+ * Configuration class that loads neccessary {@link Check} configuration from an existing scenario.xml specification.
+ * This is the recommended option when an official configuration exists as is the case with 'xrechnung'.
+ *
+ * @author Andreas Penski
+ */
+@AllArgsConstructor
+@Slf4j
+public class LoadConfiguration extends BaseConfiguration {
+
+ private static final String SUPPORTED_MAJOR_VERSION = "1";
+
+ private static final String SUPPORTED_MAJOR_VERSION_SCHEMA = "http://www.xoev.de/de/validator/framework/1/scenarios";
+
+ /**
+ * URL, die auf die scenerio.xml Datei zeigt.
+ */
+ @Getter(AccessLevel.PACKAGE)
+ private final URI scenarioDefinition;
+
+ /**
+ * Root-Ordner mit den von den einzelnen Szenarien benötigten Dateien
+ */
+ private final URI scenarioRepository;
+
+ URI getScenarioRepository() {
+ if (this.scenarioRepository == null) {
+ log.info("Creating default scenario repository (alongside scenario definition)");
+ return RelativeUriResolver.resolve(URI.create("."), this.scenarioDefinition);
+ }
+ return this.scenarioRepository;
+ }
+
+ private static void checkVersion(final URI scenarioDefinition) {
+ try {
+ final Result result = DocumentParseAction.parseDocument(InputFactory.read(scenarioDefinition.toURL()));
+ if (result.isValid() && !isSupportedDocument(result.getObject())) {
+ throw new IllegalStateException(String.format(
+ "Specified scenario configuration %s is not supported.%nThis version only supports definitions of '%s'",
+ scenarioDefinition, SUPPORTED_MAJOR_VERSION_SCHEMA));
+
+ }
+ } catch (final MalformedURLException e) {
+ throw new IllegalStateException("Error reading definition file");
+ }
+ }
+
+ private static XdmNode findRoot(final XdmNode doc) {
+ for (final XdmNode node : doc.children()) {
+ if (node.getNodeKind() == XdmNodeKind.ELEMENT) {
+ return node;
+ }
+ }
+ throw new IllegalArgumentException("Kein root element gefunden");
+ }
+
+ private static boolean isSupportedDocument(final XdmNode doc) {
+ final XdmNode root = findRoot(doc);
+ final String frameworkVersion = root.getAttributeValue(new QName("frameworkVersion"));
+ return startsWith(frameworkVersion, SUPPORTED_MAJOR_VERSION)
+ && root.getNodeName().getNamespaceURI().equals(SUPPORTED_MAJOR_VERSION_SCHEMA);
+ }
+
+ private static Scenario createFallback(final Scenarios scenarios, final ContentRepository repository) {
+ final ScenarioType t = new ScenarioType();
+ t.setName("Fallback-Scenario");
+ t.setMatch("count(/)<0");
+ final CreateReportType reportType = new CreateReportType();
+ reportType.setResource(scenarios.getNoScenarioReport().getResource());
+ // always reject
+ t.setAcceptMatch("count(/)<0");
+ t.setCreateReport(reportType);
+ final Scenario sceanrio = initialize(t, repository);
+ sceanrio.setFallback(true);
+ return sceanrio;
+ }
+
+ @Override
+ protected RuntimeArtefacts buildArtefacts() {
+ final ContentRepository contentRepository = buildContentRepository();
+ final Scenarios def = loadScenarios();
+ final List scenarios = initializeScenarios(def, contentRepository);
+ final Scenario fallbackScenario = createFallback(def, contentRepository);
+ final RuntimeArtefacts runtimeArtefacts = new RuntimeArtefacts(scenarios, fallbackScenario);
+ runtimeArtefacts.setAuthor(def.getAuthor());
+ runtimeArtefacts.setDate(def.getDate().toString());
+ runtimeArtefacts.setName(def.getName());
+ return runtimeArtefacts;
+ }
+
+ private static List initializeScenarios(final Scenarios def, final ContentRepository contentRepository) {
+ return def.getScenario().stream().map(s -> initialize(s, contentRepository)).collect(Collectors.toList());
+ }
+
+ private ContentRepository buildContentRepository() {
+ return new ContentRepository(getProcessor(), getScenarioRepository());
+ }
+
+ @Override
+ public ContentRepository getContentRepository() {
+ return buildContentRepository();
+ }
+
+ private Scenarios loadScenarios() {
+ final ConversionService conversionService = new ConversionService();
+ checkVersion(this.scenarioDefinition);
+ log.info("Loading scenarios from {}", this.scenarioDefinition);
+ final CollectingErrorEventHandler handler = new CollectingErrorEventHandler();
+ final Scenarios scenarios = conversionService.readXml(this.scenarioDefinition, Scenarios.class,
+ getContentRepository().getScenarioSchema(), handler);
+ if (!handler.hasErrors()) {
+ log.info("Loading scenario content from {}", this.scenarioRepository);
+ } else {
+ throw new IllegalStateException(
+ String.format("Can not load scenarios from %s due to %s", getScenarioDefinition(), handler.getErrorDescription()));
+ }
+ return scenarios;
+
+ }
+
+ private static Scenario initialize(final ScenarioType def, final ContentRepository repository) {
+ final Scenario s = new Scenario(def);
+ s.setSchema(repository.createSchema(def));
+ s.setReportTransformation(repository.createReportTransformation(def));
+ s.setMatchExecutable(repository.createMatchExecutable(def));
+ if (def.getAcceptMatch() != null) {
+ s.setAcceptExecutable(repository.createAccepptExecutable(def));
+ }
+ return s;
+ }
+
+ @Override
+ public String getAuthor() {
+ return null;
+ }
+
+ @Override
+ public String getName() {
+ return null;
+ }
+
+ @Override
+ public String getDate() {
+ return null;
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java b/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
new file mode 100644
index 0000000..0a2a700
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
@@ -0,0 +1,72 @@
+package de.kosit.validationtool.config;
+
+import java.net.URL;
+import java.util.Collections;
+import java.util.Map;
+
+import javax.xml.validation.Schema;
+
+import org.w3c.dom.ls.LSResourceResolver;
+
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.ObjectFactory;
+import de.kosit.validationtool.model.scenarios.ScenarioType;
+
+import net.sf.saxon.s9api.XsltExecutable;
+
+/**
+ * @author Andreas Penski
+ */
+public class ScenarioBuilder {
+
+ private final ScenarioType scenario;
+
+ private final ContentRepository contentRepository = new ContentRepository(ObjectFactory.createProcessor(), null);
+
+ ScenarioBuilder(final String name) {
+ this.scenario = new ScenarioType();
+ this.scenario.setName(name);
+ }
+
+ public ScenarioBuilder matches(final String xpath) {
+ return matches(xpath, Collections.emptyMap());
+ }
+
+ private ScenarioBuilder matches(final String xpath, final Map namespaces) {
+ // final XPathExecutable matchExecutable = this.contentRepository.createXPath(xpath, namespaces);
+ // this.scenario.setMatchExecutable(matchExecutable);
+ // this.scenario.setMatch(xpath);
+ // if (namespaces != null) {
+ // this.scenario.getNamespace().addAll(namespaces.entrySet().stream().map(e -> {
+ // NamespaceType t = new NamespaceType();
+ // t.setPrefix(e.getKey());
+ // t.setValue(e.getValue());
+ // return t;
+ // }).collect(Collectors.toList()));
+ // } else {
+ // this.scenario.getNamespace().clear();
+ // }
+ return this;
+ }
+
+ public ScenarioBuilder schemaValidation(final Schema schema) {
+ return this;
+ }
+
+ public ScenarioBuilder schemaValidation(final URL url) {
+ return schemalidation(url, null);
+ }
+
+ private ScenarioBuilder schemalidation(final URL url, final LSResourceResolver resolver) {
+ return this;
+ }
+
+ public ScenarioBuilder addSchematronValidation(final XsltExecutable executable) {
+ return this;
+ }
+
+ public ScenarioBuilder withReportGenerator(final XsltExecutable executable) {
+ return this;
+ }
+
+}
diff --git a/src/main/java/de/kosit/validationtool/impl/ContentRepository.java b/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
index 0da0736..4e56e4a 100644
--- a/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
+++ b/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
@@ -24,9 +24,12 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import javax.xml.transform.Source;
+import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
@@ -39,6 +42,11 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import de.kosit.validationtool.impl.Scenario.Transformation;
+import de.kosit.validationtool.model.scenarios.NamespaceType;
+import de.kosit.validationtool.model.scenarios.ResourceType;
+import de.kosit.validationtool.model.scenarios.ScenarioType;
+
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XPathCompiler;
@@ -55,14 +63,16 @@ import net.sf.saxon.s9api.XsltExecutable;
@Slf4j
public class ContentRepository {
+ private Schema reportInputSchema;
+
@Getter
private final Processor processor;
private final URI repository;
- private Schema reportInputSchema;
+ private final ResolvingMode mode = ResolvingMode.STRICT_RELATIVE;
- private static Source resolve(final URL resource) {
+ private Source resolve(final URL resource) {
try {
return new StreamSource(resource.openStream(), resource.toURI().getRawPath());
} catch (final IOException | URISyntaxException e) {
@@ -70,7 +80,7 @@ public class ContentRepository {
}
}
- private static Schema createSchema(final Source[] schemaSources, final LSResourceResolver resourceResolver) {
+ private Schema createSchema(final Source[] schemaSources, final LSResourceResolver resourceResolver) {
try {
final SchemaFactory sf = ObjectFactory.createSchemaFactory();
sf.setResourceResolver(resourceResolver);
@@ -80,7 +90,7 @@ public class ContentRepository {
}
}
- private static Schema createSchema(final Source[] schemaSources) {
+ private Schema createSchema(final Source[] schemaSources) {
return createSchema(schemaSources, null);
}
@@ -116,11 +126,11 @@ public class ContentRepository {
* @param url die url
* @return das erzeugte Schema
*/
- public static Schema createSchema(final URL url) {
+ public Schema createSchema(final URL url) {
return createSchema(url, null);
}
- public static Schema createSchema(final URL url, final LSResourceResolver resourceResolver) {
+ public Schema createSchema(final URL url, final LSResourceResolver resourceResolver) {
log.info("Load schema from source {}", url.getPath());
return createSchema(new Source[] { resolve(url) }, resourceResolver);
}
@@ -130,7 +140,7 @@ public class ContentRepository {
*
* @return Scenario-Schema
*/
- public static Schema getScenarioSchema() {
+ public Schema getScenarioSchema() {
return createSchema(ContentRepository.class.getResource("/xsd/scenarios.xsd"));
}
@@ -140,11 +150,11 @@ public class ContentRepository {
* @return ReportInput-Schema
*/
public Schema getReportInputSchema() {
- if (this.reportInputSchema == null) {
+ if (reportInputSchema == null) {
final Source source = resolve(ContentRepository.class.getResource("/xsd/createReportInput.xsd"));
- this.reportInputSchema = createSchema(new Source[] { source }, new ClassPathResourceResolver("/xsd"));
+ reportInputSchema = createSchema(new Source[] { source }, new ClassPathResourceResolver("/xsd"));
}
- return this.reportInputSchema;
+ return reportInputSchema;
}
/**
@@ -157,8 +167,23 @@ public class ContentRepository {
return createSchema(uris.stream().map(s -> resolve(URI.create(s))).toArray(Source[]::new));
}
+ /**
+ * Liefert das Schema zu diesem Szenario.
+ *
+ * @return das passende Schema
+ */
+ public Schema createSchema(final ScenarioType s) {
+ Schema schema = null;
+ if (s.getValidateWithXmlSchema() != null) {
+ final List schemaResources = s.getValidateWithXmlSchema().getResource().stream().map(ResourceType::getLocation)
+ .collect(Collectors.toList());
+ schema = createSchema(schemaResources);
+ }
+ return schema;
+ }
+
private Source resolve(final URI source) {
- final URI resolved = RelativeUriResolver.resolve(source, this.repository);
+ final URI resolved = this.mode.resolve(source, this.repository);
return new StreamSource(resolved.toASCIIString());
}
@@ -187,7 +212,30 @@ public class ContentRepository {
*
* @return ein neuer Resolver
*/
- public RelativeUriResolver createResolver() {
- return new RelativeUriResolver(this.repository);
+ public URIResolver createResolver() {
+ return this.mode.createResolver(this.repository);
+ }
+
+ /**
+ * Gibt eine Transformation zurück.
+ *
+ * @return initialisierte Transformation
+ */
+ public Transformation createReportTransformation(final ScenarioType t) {
+ final ResourceType resource = t.getCreateReport().getResource();
+ final XsltExecutable executable = loadXsltScript(URI.create(resource.getLocation()));
+ return new Transformation(executable, resource);
+ }
+
+ public XPathExecutable createMatchExecutable(final ScenarioType s) {
+ final Map namespaces = s.getNamespace().stream()
+ .collect(Collectors.toMap(NamespaceType::getPrefix, NamespaceType::getValue));
+ return createXPath(s.getMatch(), namespaces);
+ }
+
+ public XPathExecutable createAccepptExecutable(final ScenarioType s) {
+ final Map namespaces = s.getNamespace().stream()
+ .collect(Collectors.toMap(NamespaceType::getPrefix, NamespaceType::getValue));
+ return createXPath(s.getAcceptMatch(), namespaces);
}
}
diff --git a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java
index 02e2922..2d9fa78 100644
--- a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java
+++ b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java
@@ -28,7 +28,7 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.Check;
-import de.kosit.validationtool.api.CheckConfiguration;
+import de.kosit.validationtool.api.Configuration;
import de.kosit.validationtool.api.Input;
import de.kosit.validationtool.api.Result;
import de.kosit.validationtool.api.XmlError;
@@ -73,20 +73,22 @@ public class DefaultCheck implements Check {
*
* @param configuration die Konfiguration
*/
- public DefaultCheck(final CheckConfiguration configuration) {
+ public DefaultCheck(final Configuration configuration) {
final Processor processor = ObjectFactory.createProcessor();
this.conversionService = new ConversionService();
- this.contentRepository = new ContentRepository(processor, configuration.getScenarioRepository());
- this.repository = new ScenarioRepository(this.contentRepository);
- this.repository.initialize(configuration);
+ 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 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, this.contentRepository.getReportInputSchema()));
- this.checkSteps.add(new CreateReportAction(processor, this.conversionService, this.repository, this.contentRepository));
+ this.checkSteps
+ .add(new ValidateReportInputAction(this.conversionService, configuration.getContentRepository().getReportInputSchema()));
+ this.checkSteps.add(new CreateReportAction(processor, this.conversionService, this.contentRepository));
this.checkSteps.add(new ComputeAcceptanceAction());
}
@@ -139,5 +141,4 @@ public class DefaultCheck implements Check {
return (List) (List>) errors;
}
-
}
diff --git a/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java b/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
index eace869..76b66ee 100644
--- a/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
+++ b/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
@@ -207,7 +207,7 @@ public class ObjectFactory {
final SecureUriResolver resolver = new SecureUriResolver();
processor.getUnderlyingConfiguration().setCollectionFinder(resolver);
processor.getUnderlyingConfiguration().setOutputURIResolver(resolver);
- //hier fehlt eigentlich noch der UriResolver für unparsed text, wird erst ab Saxon 9.8 unterstützt
+ processor.getUnderlyingConfiguration().setUnparsedTextURIResolver(resolver);
//grundsätzlich Feature-konfiguration:
processor.setConfigurationProperty(FeatureKeys.DTD_VALIDATION, false);
diff --git a/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java b/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java
new file mode 100644
index 0000000..618c74a
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java
@@ -0,0 +1,45 @@
+package de.kosit.validationtool.impl;
+
+import java.net.URI;
+
+import javax.xml.transform.URIResolver;
+
+import org.apache.commons.lang3.NotImplementedException;
+
+/**
+ * Defines how artefacts are resolved internally.
+ *
+ * @author Andreas Penski
+ */
+public enum ResolvingMode {
+
+ /**
+ * Resolving using only the configured content repository. No furthing resolving allowed. This
+ */
+ STRICT_RELATIVE {
+
+ @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,
+
+ JDK_SUPPORTED,
+
+ CUSTOM;
+
+ public URI resolve(final URI source, final URI repository) {
+ throw new NotImplementedException("Not yet implemented");
+ }
+
+ public URIResolver createResolver(final URI repository) {
+ throw new NotImplementedException("Not yet implemented");
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/impl/Scenario.java b/src/main/java/de/kosit/validationtool/impl/Scenario.java
new file mode 100644
index 0000000..159fa87
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/impl/Scenario.java
@@ -0,0 +1,83 @@
+package de.kosit.validationtool.impl;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import javax.xml.validation.Schema;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+
+import de.kosit.validationtool.model.scenarios.ResourceType;
+import de.kosit.validationtool.model.scenarios.ScenarioType;
+
+import net.sf.saxon.s9api.XPathExecutable;
+import net.sf.saxon.s9api.XPathSelector;
+import net.sf.saxon.s9api.XsltExecutable;
+
+/**
+ * @author Andreas Penski
+ */
+@RequiredArgsConstructor
+
+@Setter
+@Getter
+public class Scenario {
+
+ /**
+ * Runtime objects for a transformation e.g. schematron or report.
+ */
+ @Getter
+ @Setter
+ @AllArgsConstructor
+ public static class Transformation {
+
+ private XsltExecutable executable;
+
+ private ResourceType resourceType;
+ }
+
+ private final ScenarioType configuration;
+
+ private Schema schema;
+
+ private boolean fallback;
+
+ private XPathExecutable matchExecutable;
+
+ private XPathExecutable acceptExecutable;
+
+ @Setter
+ private List schematronValidations;
+
+ private Transformation reportTransformation;
+
+ public List getSchematronValidations() {
+ return this.schematronValidations == null ? Collections.emptyList() : this.schematronValidations;
+ }
+
+ public String getName() {
+ return this.configuration.getName();
+ }
+
+ public XPathSelector getMatchSelector() {
+ if (this.matchExecutable == null) {
+ throw new IllegalStateException("No match executable supplied");
+ }
+ return this.matchExecutable.load();
+ }
+
+ /**
+ * Liefert einen neuen XPath-Selector zur Evaluierung der {@link de.kosit.validationtool.api.AcceptRecommendation}.
+ *
+ * @return neuer Selector
+ */
+ public Optional getAcceptSelector() {
+ final XPathSelector selector = this.acceptExecutable != null ? this.acceptExecutable.load() : null;
+ return Optional.ofNullable(selector);
+ }
+
+}
diff --git a/src/main/java/de/kosit/validationtool/impl/ScenarioRepository.java b/src/main/java/de/kosit/validationtool/impl/ScenarioRepository.java
index 6992329..de5b7de 100644
--- a/src/main/java/de/kosit/validationtool/impl/ScenarioRepository.java
+++ b/src/main/java/de/kosit/validationtool/impl/ScenarioRepository.java
@@ -19,34 +19,18 @@
package de.kosit.validationtool.impl;
-import static org.apache.commons.lang3.StringUtils.startsWith;
-
-import java.net.MalformedURLException;
-import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
-import lombok.AccessLevel;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
-import de.kosit.validationtool.api.CheckConfiguration;
-import de.kosit.validationtool.api.InputFactory;
+import de.kosit.validationtool.api.Configuration;
import de.kosit.validationtool.impl.model.Result;
-import de.kosit.validationtool.impl.tasks.DocumentParseAction;
-import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
-import de.kosit.validationtool.model.scenarios.CreateReportType;
-import de.kosit.validationtool.model.scenarios.ScenarioType;
-import de.kosit.validationtool.model.scenarios.Scenarios;
-import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XPathSelector;
import net.sf.saxon.s9api.XdmNode;
-import net.sf.saxon.s9api.XdmNodeKind;
/**
* Repository for die aktiven Szenario einer Prüfinstanz.
@@ -54,86 +38,29 @@ import net.sf.saxon.s9api.XdmNodeKind;
* @author Andreas Penski
*/
@Slf4j
-@RequiredArgsConstructor
+
public class ScenarioRepository {
- private static final String SUPPORTED_MAJOR_VERSION = "1";
+ private final Configuration configuration;
- private static final String SUPPORTED_MAJOR_VERSION_SCHEMA = "http://www.xoev.de/de/validator/framework/1/scenarios";
-
-
- @Getter(value = AccessLevel.PACKAGE)
- private final ContentRepository repository;
-
- @Getter
- private Scenarios scenarios;
-
- @Setter(AccessLevel.PACKAGE)
- @Getter
- private ScenarioType fallbackScenario;
-
- private static boolean isSupportedDocument(final XdmNode doc) {
- final XdmNode root = findRoot(doc);
- final String frameworkVersion = root.getAttributeValue(new QName("frameworkVersion"));
- return startsWith(frameworkVersion, SUPPORTED_MAJOR_VERSION)
- && root.getNodeName().getNamespaceURI().equals(SUPPORTED_MAJOR_VERSION_SCHEMA);
+ 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());
}
- private static XdmNode findRoot(final XdmNode doc) {
- for (final XdmNode node : doc.children()) {
- if (node.getNodeKind() == XdmNodeKind.ELEMENT) {
- return node;
- }
- }
- throw new IllegalArgumentException("Kein root element gefunden");
+ public Scenario getFallbackScenario() {
+ return this.configuration.getFallbackScenario();
}
- private static void checkVersion(final URI scenarioDefinition) {
- final DocumentParseAction p = new DocumentParseAction();
- try {
- final Result result = DocumentParseAction.parseDocument(InputFactory.read(scenarioDefinition.toURL()));
- if (result.isValid() && !isSupportedDocument(result.getObject())) {
- throw new IllegalStateException(String.format(
- "Specified scenario configuration %s is not supported.%nThis version only supports definitions of '%s'",
- scenarioDefinition, SUPPORTED_MAJOR_VERSION_SCHEMA));
-
- }
- } catch (final MalformedURLException e) {
- throw new IllegalStateException("Error reading definition file");
- }
- }
-
-
-
- /**
- * Initialisiert das Repository mit der angegebenen Konfiguration.
- *
- * @param config die Konfiguration
- */
- public void initialize(final CheckConfiguration config) {
- final ConversionService conversionService = new ConversionService();
- checkVersion(config.getScenarioDefinition());
- log.info("Loading scenarios from {}", config.getScenarioDefinition());
- final CollectingErrorEventHandler handler = new CollectingErrorEventHandler();
- this.scenarios = conversionService.readXml(config.getScenarioDefinition(), Scenarios.class, ContentRepository.getScenarioSchema(),
- handler);
- if (!handler.hasErrors()) {
- log.info("Loaded scenarios for {} by {} from {}. The following scenarios are available:\n\n{}", this.scenarios.getName(),
- this.scenarios.getAuthor(), this.scenarios.getDate(), summarizeScenarios());
- log.info("Loading scenario content from {}", config.getScenarioRepository());
- getScenarios().getScenario().forEach(s -> s.initialize(this.repository, false));
- } else {
- throw new IllegalStateException(String.format("Can not load scenarios from %s due to %s", config.getScenarioDefinition(),
- handler.getErrorDescription()));
- }
- // initialize fallback report eager
- this.fallbackScenario = createFallback();
-
+ public List getScenarios() {
+ return this.configuration.getScenarios();
}
private String summarizeScenarios() {
final StringBuilder b = new StringBuilder();
- this.scenarios.getScenario().forEach(s -> {
+ getScenarios().forEach(s -> {
b.append(s.getName());
b.append("\n");
});
@@ -146,9 +73,9 @@ public class ScenarioRepository {
* @param document das Eingabedokument
* @return ein Ergebnis-Objekt zur weiteren Verarbeitung
*/
- public Result selectScenario(final XdmNode document) {
- final Result result;
- final List collect = this.scenarios.getScenario().stream().filter(s -> match(document, s))
+ public Result selectScenario(final XdmNode document) {
+ final Result result;
+ final List collect = getScenarios().stream().filter(s -> match(document, s))
.collect(Collectors.toList());
if (collect.size() == 1) {
result = new Result<>(collect.get(0));
@@ -162,23 +89,9 @@ public class ScenarioRepository {
}
- private ScenarioType createFallback() {
- final ScenarioType t = new ScenarioType();
- t.setFallback(true);
- t.setName("Fallback-Scenario");
- t.setMatch("count(/)<0");
- final CreateReportType reportType = new CreateReportType();
- reportType.setResource(this.scenarios.getNoScenarioReport().getResource());
- t.initialize(this.repository, true);
- // always reject
- t.setAcceptMatch("count(/)<0");
- t.setCreateReport(reportType);
- return t;
- }
-
- private static boolean match(final XdmNode document, final ScenarioType scenario) {
+ private static boolean match(final XdmNode document, final Scenario scenario) {
try {
- final XPathSelector selector = scenario.getSelector();
+ final XPathSelector selector = scenario.getMatchSelector();
selector.setContextItem(document);
return selector.effectiveBooleanValue();
} catch (final SaxonApiException e) {
@@ -187,7 +100,4 @@ public class ScenarioRepository {
return false;
}
- void initialize(final Scenarios def) {
- this.scenarios = def;
- }
}
diff --git a/src/main/java/de/kosit/validationtool/impl/model/BaseScenario.java b/src/main/java/de/kosit/validationtool/impl/model/BaseScenario.java
deleted file mode 100644
index c92ebaa..0000000
--- a/src/main/java/de/kosit/validationtool/impl/model/BaseScenario.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Licensed to the Koordinierungsstelle für IT-Standards (KoSIT) under
- * one or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. KoSIT licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package de.kosit.validationtool.impl.model;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlTransient;
-import javax.xml.validation.Schema;
-
-import org.apache.commons.lang3.NotImplementedException;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.Setter;
-
-import de.kosit.validationtool.impl.ContentRepository;
-import de.kosit.validationtool.impl.ScenarioRepository;
-import de.kosit.validationtool.model.scenarios.CreateReportType;
-import de.kosit.validationtool.model.scenarios.NamespaceType;
-import de.kosit.validationtool.model.scenarios.ResourceType;
-import de.kosit.validationtool.model.scenarios.ValidateWithSchematron;
-import de.kosit.validationtool.model.scenarios.ValidateWithXmlSchema;
-
-import net.sf.saxon.s9api.XPathExecutable;
-import net.sf.saxon.s9api.XPathSelector;
-import net.sf.saxon.s9api.XsltExecutable;
-
-/**
- * Eine Basis-Klasse für Szenarien. Wiederverwendbare Objekte mit Bezug zum Szenario werden hier gecached. Die Klasse
- * stellt im eigentlichen Sinne eine Erweiterung der durch JAXB generierten Strukturen dar.
- *
- * @author Andreas Penski
- */
-@XmlAccessorType(XmlAccessType.NONE)
-public abstract class BaseScenario {
-
- /**
- * Laufzeitinformationen über eine Transformation.
- */
- @Getter
- @Setter
- @AllArgsConstructor
- public static class Transformation {
-
- private XsltExecutable executable;
-
- private ResourceType resourceType;
- }
-
- @XmlTransient
- @Getter
- @Setter
- private boolean fallback;
-
- private XPathExecutable matchExecutable;
-
- private XPathExecutable acceptExecutable;
-
- @Setter
- @XmlTransient
- private Schema schema;
-
- @Setter
- private List schematronValidations;
-
- private ContentRepository repository;
-
- private Transformation reportTransformation;
-
- /**
- * Gibt eine Transformation zurück.
- *
- * @return initialisierte Transformation
- */
- public Transformation getReportTransformation() {
- if (this.reportTransformation == null) {
- final ResourceType resource = getCreateReport().getResource();
- final XsltExecutable executable = this.repository.loadXsltScript(URI.create(resource.getLocation()));
- this.reportTransformation = new Transformation(executable, resource);
- }
- return this.reportTransformation;
- }
-
- /**
- * Lieferrt das Schema zu diesem Szenario.
- *
- * @return das passende Schema
- */
- public Schema getSchema() {
- if (this.schema == null && getValidateWithXmlSchema() != null) {
- final List schemaResources = getValidateWithXmlSchema().getResource().stream().map(ResourceType::getLocation)
- .collect(Collectors.toList());
- this.schema = this.repository.createSchema(schemaResources);
- }
- return this.schema;
- }
-
- /**
- * Initialisiert das Szenario auf Basis eines [@link ContentRepository}
- *
- * @param repository das Repository mit den Szenario-Artefakten
- * @param lazy optionales lazy loading der XML-Artefakte
- * @return true wenn erfolgreich
- */
- public boolean initialize(final ContentRepository repository, final boolean lazy) {
- this.repository = repository;
- if (!lazy) {
- getSchema();
- getSelector();
- getSchematronValidations();
- getReportTransformation();
- }
- return true;
- }
-
- /**
- * Liefer eine Liste mit Schematron Validierungs-Transformationen.
- *
- * @return liste mit initialisierten Transformationsinformationen
- */
- public List getSchematronValidations() {
- if (this.schematronValidations == null) {
- this.schematronValidations = new ArrayList<>();
- getValidateWithSchematron().forEach(v -> {
- if (v.isPsvi()) {
- throw new NotImplementedException("This implemenation does not support PSVI usage");
- }
- final XsltExecutable xsltExecutable = this.repository.loadXsltScript(URI.create(v.getResource().getLocation()));
- this.schematronValidations.add(new Transformation(xsltExecutable, v.getResource()));
- });
- }
- return this.schematronValidations;
- }
-
- /**
- * Der XPath-Selector zur Identifikation des Scenarios.
- *
- * @return vorbereiteten selector
- * @see ScenarioRepository#selectScenario(net.sf.saxon.s9api.XdmNode)
- */
- public XPathSelector getSelector() {
- if (this.matchExecutable == null) {
- this.matchExecutable = this.repository.createXPath(getMatch(), prepareNamespaces());
- }
- return this.matchExecutable.load();
- }
-
- /**
- * Liefert einen neuen XPath-Selector zur Evaluierung der {@link de.kosit.validationtool.api.AcceptRecommendation}.
- *
- * @return neuer Selector
- */
- public XPathSelector getAcceptSelector() {
- if (this.acceptExecutable == null) {
- this.acceptExecutable = this.repository.createXPath(getAcceptMatch(), prepareNamespaces());
- }
- return this.acceptExecutable.load();
- }
-
- private Map prepareNamespaces() {
- return getNamespace().stream().collect(Collectors.toMap(NamespaceType::getPrefix, NamespaceType::getValue));
- }
-
- /**
- * Getter aus dem schema.
- *
- * @return xpath match
- */
- public abstract String getMatch();
-
- public abstract String getAcceptMatch();
-
- /**
- * Getter aus dem schema.
- *
- * @return {@link List} of {@link NamespaceType}
- */
- public abstract List getNamespace();
-
- /**
- * Getter aus dem schema.
- *
- * @return Validierungsanweisungen
- */
- public abstract ValidateWithXmlSchema getValidateWithXmlSchema();
-
- /**
- * Getter aus dem schema.
- *
- * @return Validierungsanweisungne
- */
- public abstract List getValidateWithSchematron();
-
- /**
- * Getter aus dem schema.
- *
- * @return report informationen
- */
- public abstract CreateReportType getCreateReport();
-
-}
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/CheckAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/CheckAction.java
index bc362c5..23d6476 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/CheckAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/CheckAction.java
@@ -30,11 +30,11 @@ import lombok.Setter;
import de.kosit.validationtool.api.AcceptRecommendation;
import de.kosit.validationtool.api.Input;
+import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.model.reportInput.CreateReportInput;
import de.kosit.validationtool.model.reportInput.ProcessingError;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
-import de.kosit.validationtool.model.scenarios.ScenarioType;
import net.sf.saxon.s9api.XdmNode;
@@ -55,7 +55,7 @@ public interface CheckAction {
@Setter
class Bag {
- private Result scenarioSelectionResult;
+ private Result scenarioSelectionResult;
@Setter(AccessLevel.NONE)
private CreateReportInput reportInput;
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java
index f4c5ca2..3e5db25 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java
@@ -1,6 +1,6 @@
package de.kosit.validationtool.impl.tasks;
-import static org.apache.commons.lang3.StringUtils.isNotBlank;
+import java.util.Optional;
import org.oclc.purl.dsdl.svrl.FailedAssert;
@@ -25,9 +25,9 @@ public class ComputeAcceptanceAction implements CheckAction {
@Override
public void check(final Bag results) {
if (preCondtionsMatch(results)) {
- final String acceptMatch = results.getScenarioSelectionResult().getObject().getAcceptMatch();
- if (results.getSchemaValidationResult().isValid() && isNotBlank(acceptMatch)) {
- evaluateAcceptanceMatch(results);
+ final Optional acceptMatch = results.getScenarioSelectionResult().getObject().getAcceptSelector();
+ if (results.getSchemaValidationResult().isValid() && acceptMatch.isPresent()) {
+ evaluateAcceptanceMatch(results, acceptMatch.get());
} else {
evaluateSchemaAndSchematron(results);
}
@@ -53,9 +53,8 @@ public class ComputeAcceptanceAction implements CheckAction {
.flatMap(e -> e.getActivePatternAndFiredRuleAndFailedAssert().stream()).anyMatch(FailedAssert.class::isInstance);
}
- private static void evaluateAcceptanceMatch(final Bag results) {
+ private static void evaluateAcceptanceMatch(final Bag results, final XPathSelector selector) {
try {
- final XPathSelector selector = results.getScenarioSelectionResult().getObject().getAcceptSelector();
selector.setContextItem(results.getReport());
results.setAcceptStatus(selector.effectiveBooleanValue() ? AcceptRecommendation.ACCEPTABLE : AcceptRecommendation.REJECT);
} catch (final SaxonApiException e) {
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
index 9809479..cfd9ad2 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
@@ -22,6 +22,7 @@ package de.kosit.validationtool.impl.tasks;
import java.util.Collection;
import java.util.stream.Collectors;
+import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Document;
@@ -35,10 +36,8 @@ 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.RelativeUriResolver;
-import de.kosit.validationtool.impl.ScenarioRepository;
+import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
-import de.kosit.validationtool.model.scenarios.ScenarioType;
import net.sf.saxon.s9api.BuildingContentHandler;
import net.sf.saxon.s9api.DocumentBuilder;
@@ -64,11 +63,9 @@ public class CreateReportAction implements CheckAction {
private final ConversionService conversionService;
- private final ScenarioRepository scenarioRepository;
-
private final ContentRepository contentRepository;
- private static XsltExecutable loadFromScenario(final ScenarioType object) {
+ private static XsltExecutable loadFromScenario(final Scenario object) {
return object.getReportTransformation().getExecutable();
}
@@ -85,10 +82,10 @@ public class CreateReportAction implements CheckAction {
final XsltTransformer transformer = getTransformation(results).load();
transformer.setInitialContextNode(root);
final CollectingErrorEventHandler e = new CollectingErrorEventHandler();
- final RelativeUriResolver resolver = this.contentRepository.createResolver();
+ final URIResolver resolver = this.contentRepository.createResolver();
transformer.setMessageListener(e);
transformer.setURIResolver(resolver);
- transformer.getUnderlyingController().setUnparsedTextURIResolver(resolver);
+ // transformer.getUnderlyingController().setUnparsedTextURIResolver(resolver);
if (parsedDocument != null) {
transformer.setParameter(new QName("input-document"), parsedDocument);
}
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/ScenarioSelectionAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/ScenarioSelectionAction.java
index 63a11f6..90b5c46 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/ScenarioSelectionAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/ScenarioSelectionAction.java
@@ -22,10 +22,10 @@ package de.kosit.validationtool.impl.tasks;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.impl.ScenarioRepository;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.model.reportInput.CreateReportInput;
-import de.kosit.validationtool.model.scenarios.ScenarioType;
import net.sf.saxon.s9api.XdmNode;
@@ -44,7 +44,7 @@ public class ScenarioSelectionAction implements CheckAction {
@Override
public void check(final Bag results) {
final CreateReportInput report = results.getReportInput();
- final Result scenarioTypeResult;
+ final Result scenarioTypeResult;
if (results.getParserResult().isValid()) {
scenarioTypeResult = determineScenario(results.getParserResult().getObject());
@@ -53,15 +53,15 @@ public class ScenarioSelectionAction implements CheckAction {
}
results.setScenarioSelectionResult(scenarioTypeResult);
if (!scenarioTypeResult.getObject().isFallback()) {
- report.setScenario(scenarioTypeResult.getObject());
+ report.setScenario(scenarioTypeResult.getObject().getConfiguration());
log.info("Schenario {} identified for {}", scenarioTypeResult.getObject().getName(), results.getInput().getName());
} else {
log.error("No valid schenario configuration found for {}", results.getInput().getName());
}
}
- private Result determineScenario(final XdmNode document) {
- final Result result = this.repository.selectScenario(document);
+ private Result determineScenario(final XdmNode document) {
+ final Result result = this.repository.selectScenario(document);
if (result.isInvalid()) {
return new Result<>(this.repository.getFallbackScenario());
}
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/SchemaValidationAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/SchemaValidationAction.java
index f3859e4..89ddd7c 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/SchemaValidationAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/SchemaValidationAction.java
@@ -42,12 +42,12 @@ import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.Input;
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;
import de.kosit.validationtool.model.reportInput.CreateReportInput;
import de.kosit.validationtool.model.reportInput.ValidationResultsXmlSchema;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
-import de.kosit.validationtool.model.scenarios.ScenarioType;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
@@ -132,17 +132,17 @@ public class SchemaValidationAction implements CheckAction {
@Getter
private long inMemoryLimit = Long.parseLong(System.getProperty(LIMIT_PARAMETER, BA_LIMIT.toString())) * FileUtils.ONE_MB;
- private Result validate(final Bag results, final ScenarioType scenarioType) {
- log.debug("Validating document using scenario {}", scenarioType.getName());
+ private Result validate(final Bag results, final Scenario scenario) {
+ log.debug("Validating document using scenario {}", scenario.getConfiguration().getName());
final CollectingErrorEventHandler errorHandler = new CollectingErrorEventHandler();
try ( final SourceProvider validateInput = resolveSource(results) ) {
- final Validator validator = ObjectFactory.createValidator(scenarioType.getSchema());
+ final Validator validator = ObjectFactory.createValidator(scenario.getSchema());
validator.setErrorHandler(errorHandler);
validator.validate(validateInput.getSource());
return new Result<>(!errorHandler.hasErrors(), errorHandler.getErrors());
} catch (final SAXException | SaxonApiException | IOException e) {
- final String msg = String.format("Error processing schema validation for scenario %s", scenarioType.getName());
+ final String msg = String.format("Error processing schema validation for scenario %s", scenario.getConfiguration().getName());
log.error(msg, e);
results.addProcessingError(msg);
return new Result<>(Boolean.FALSE);
@@ -152,14 +152,14 @@ public class SchemaValidationAction implements CheckAction {
@Override
public void check(final Bag results) {
final CreateReportInput report = results.getReportInput();
- final ScenarioType scenario = results.getScenarioSelectionResult().getObject();
+ final Scenario scenario = results.getScenarioSelectionResult().getObject();
final Result validateResult = validate(results, scenario);
results.setSchemaValidationResult(validateResult);
final ValidationResultsXmlSchema result = new ValidationResultsXmlSchema();
report.setValidationResultsXmlSchema(result);
- result.getResource().addAll(scenario.getValidateWithXmlSchema().getResource());
+ result.getResource().addAll(scenario.getConfiguration().getValidateWithXmlSchema().getResource());
if (!validateResult.isValid()) {
result.getXmlSyntaxError().addAll(validateResult.getErrors());
}
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java
index 531ef4f..377e195 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java
@@ -22,6 +22,7 @@ package de.kosit.validationtool.impl.tasks;
import java.util.List;
import java.util.stream.Collectors;
+import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMSource;
import org.oclc.purl.dsdl.svrl.SchematronOutput;
@@ -34,11 +35,9 @@ 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.RelativeUriResolver;
-import de.kosit.validationtool.impl.model.BaseScenario;
+import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.model.reportInput.CreateReportInput;
import de.kosit.validationtool.model.reportInput.ValidationResultsSchematron;
-import de.kosit.validationtool.model.scenarios.ScenarioType;
import net.sf.saxon.s9api.DOMDestination;
import net.sf.saxon.s9api.SaxonApiException;
@@ -58,17 +57,17 @@ public class SchematronValidationAction implements CheckAction {
private final ConversionService conversionService;
- private List validate(final Bag results, final XdmNode document, final ScenarioType scenario) {
+ private List validate(final Bag results, final XdmNode document, final Scenario scenario) {
return scenario.getSchematronValidations().stream().map(v -> validate(results, document, v)).collect(Collectors.toList());
}
- private ValidationResultsSchematron validate(final Bag results, final XdmNode document, final BaseScenario.Transformation validation) {
+ private ValidationResultsSchematron validate(final Bag results, final XdmNode document, final Scenario.Transformation validation) {
final ValidationResultsSchematron s = new ValidationResultsSchematron();
s.setResource(validation.getResourceType());
try {
final XsltTransformer transformer = validation.getExecutable().load();
// resolving nur relative zum Repository
- final RelativeUriResolver resolver = this.repository.createResolver();
+ final URIResolver resolver = this.repository.createResolver();
transformer.setURIResolver(resolver);
final CollectingErrorEventHandler e = new CollectingErrorEventHandler();
transformer.setMessageListener(e);
@@ -107,7 +106,7 @@ public class SchematronValidationAction implements CheckAction {
return results.getSchemaValidationResult() == null || results.getSchemaValidationResult().isInvalid();
}
- private static boolean hasNoSchematrons(final ScenarioType object) {
- return object.getValidateWithSchematron() == null || object.getValidateWithSchematron().size() == 0;
+ private static boolean hasNoSchematrons(final Scenario object) {
+ return object.getSchematronValidations().isEmpty();
}
}
diff --git a/src/main/model/binding/global.xjb b/src/main/model/binding/global.xjb
index b8e336f..33eced9 100644
--- a/src/main/model/binding/global.xjb
+++ b/src/main/model/binding/global.xjb
@@ -42,9 +42,6 @@
-
- de.kosit.validationtool.impl.model.BaseScenario
-
diff --git a/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java b/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java
index e70948b..4eb20cb 100644
--- a/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java
@@ -42,7 +42,7 @@ import net.sf.saxon.s9api.XPathExecutable;
import net.sf.saxon.s9api.XsltExecutable;
/**
- * Testet das ContentRepository.
+ * Testet das repository.
*
* @author Andreas Penski
*/
@@ -60,7 +60,7 @@ public class ContentRepositoryTest {
@Test
public void testCreateSchema() throws MalformedURLException {
- final Schema schema = ContentRepository.createSchema(Helper.ASSERTION_SCHEMA.toURL());
+ final Schema schema = this.repository.createSchema(Helper.ASSERTION_SCHEMA.toURL());
assertThat(schema).isNotNull();
}
@@ -73,7 +73,7 @@ public class ContentRepositoryTest {
@Test
public void testCreateSchemaNotExisting() throws Exception {
this.exception.expect(IllegalStateException.class);
- ContentRepository.createSchema(Simple.NOT_EXISTING.toURL());
+ this.repository.createSchema(Simple.NOT_EXISTING.toURL());
}
@Test
@@ -122,16 +122,26 @@ public class ContentRepositoryTest {
@Test
public void testLoadSchema() {
final URL main = RelativeUriResolverTest.class.getClassLoader().getResource("loading/main.xsd");
- final Schema schema = ContentRepository.createSchema(main, new ClassPathResourceResolver("/loading"));
+ final Schema schema = this.repository.createSchema(main, new ClassPathResourceResolver("/loading"));
assertThat(schema).isNotNull();
}
@Test
public void testLoadSchemaPackaged() throws URISyntaxException {
final URL main = RelativeUriResolverTest.class.getClassLoader().getResource("packaged/main.xsd");
- final Schema schema = ContentRepository.createSchema(main,
+ final Schema schema = this.repository.createSchema(main,
new ClassPathResourceResolver(RelativeUriResolverTest.class.getClassLoader().getResource("packaged/").toURI()));
assertThat(schema).isNotNull();
}
+ // @Test
+ // public void loadFromJar() throws URISyntaxException {
+ // this.content = new ContentRepository(ObjectFactory.createProcessor(), Helper.JAR_REPOSITORY.toURI());
+ // this.repository = new ScenarioRepository(this.content);
+ // final CheckConfiguration conf = new CheckConfiguration(
+ // ScenarioRepository.class.getClassLoader().getResource("xrechnung/scenarios.xml").toURI());
+ // ScenarioRepository.initialize(conf);
+ // assertThat(this.repository.getScenarios()).isNotNull();
+ // }
+
}
diff --git a/src/test/java/de/kosit/validationtool/impl/ConversionServiceTest.java b/src/test/java/de/kosit/validationtool/impl/ConversionServiceTest.java
index 0353bac..60500c5 100644
--- a/src/test/java/de/kosit/validationtool/impl/ConversionServiceTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/ConversionServiceTest.java
@@ -72,7 +72,7 @@ public class ConversionServiceTest {
}
@Test
- public void testUnmarshal() throws URISyntaxException {
+ public void testUnmarshal() {
final Scenarios s = this.service.readXml(Simple.SCENARIOS, Scenarios.class);
assertThat(s).isNotNull();
assertThat(s.getName()).isEqualToIgnoringCase("HTML-TestSuite");
@@ -80,7 +80,7 @@ public class ConversionServiceTest {
@Test
public void testUnmarshalWithSchema() {
- final Scenarios s = this.service.readXml(Simple.SCENARIOS, Scenarios.class, ContentRepository.createSchema(SCHEMA));
+ final Scenarios s = this.service.readXml(Simple.SCENARIOS, Scenarios.class, this.repository.createSchema(SCHEMA));
assertThat(s).isNotNull();
assertThat(s.getName()).isEqualToIgnoringCase("HTML-TestSuite");
}
@@ -88,13 +88,13 @@ public class ConversionServiceTest {
@Test
public void testUnmarshalInvalidXml() {
this.exception.expect(ConversionService.ConversionExeption.class);
- this.service.readXml(Invalid.SCENARIOS, Scenarios.class, ContentRepository.createSchema(SCHEMA));
+ this.service.readXml(Invalid.SCENARIOS, Scenarios.class, this.repository.createSchema(SCHEMA));
}
@Test
public void testUnmarshalIllFormed() {
this.exception.expect(ConversionService.ConversionExeption.class);
- this.service.readXml(Invalid.SCENARIOS_ILLFORMED, Scenarios.class, ContentRepository.createSchema(SCHEMA));
+ this.service.readXml(Invalid.SCENARIOS_ILLFORMED, Scenarios.class, this.repository.createSchema(SCHEMA));
}
@Test
diff --git a/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java b/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
index abfab1d..e450050 100644
--- a/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
@@ -24,20 +24,25 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.net.URI;
-import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import de.kosit.validationtool.api.CheckConfiguration;
+import lombok.Data;
+
+import de.kosit.validationtool.api.Configuration;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.impl.tasks.DocumentParseAction;
import de.kosit.validationtool.model.scenarios.ScenarioType;
-import de.kosit.validationtool.model.scenarios.Scenarios;
+import net.sf.saxon.s9api.Processor;
+import net.sf.saxon.s9api.XPathExecutable;
import net.sf.saxon.s9api.XdmNode;
/**
@@ -48,57 +53,84 @@ import net.sf.saxon.s9api.XdmNode;
public class ScenarioRepositoryTest {
+ @Data
+ private static class DummyConfiguration implements Configuration {
+
+ private List scenarios;
+
+ private Scenario fallbackScenario;
+
+ private String author;
+
+ private String name;
+
+ private String date;
+
+ private Processor processor;
+
+ private ContentRepository contentRepository;
+
+ @Override
+ public void build() {
+ // nothing
+ }
+ }
+
@Rule
public ExpectedException expectedException = ExpectedException.none();
- ContentRepository content;
-
private ScenarioRepository repository;
+ private DummyConfiguration configInstance;
+
@Before
public void setup() {
- this.content = new ContentRepository(ObjectFactory.createProcessor(), Simple.REPOSITORY);
- final Scenarios def = new Scenarios();
- final ScenarioType t = new ScenarioType();
- t.setMatch("//*:name");
- t.setName("Test");
- t.initialize(this.content, true);
- def.getScenario().add(t);
- this.repository = new ScenarioRepository(this.content);
- this.repository.initialize(def);
+ final Scenario s = createScenario();
+
+ this.configInstance = new DummyConfiguration();
+ this.configInstance.setScenarios(new ArrayList<>());
+ this.configInstance.getScenarios().add(s);
+ this.repository = new ScenarioRepository(this.configInstance);
+ }
+
+ private static Scenario createScenario() {
+ final Scenario s = new Scenario(new ScenarioType());
+ s.setMatchExecutable(createXpath("//*:name"));
+ return s;
}
@Test
public void testHappyCase() throws Exception {
- final Result scenario = this.repository.selectScenario(load(Simple.SCENARIOS));
+ final Result scenario = this.repository.selectScenario(load(Simple.SCENARIOS));
assertThat(scenario).isNotNull();
assertThat(scenario.isValid()).isTrue();
}
@Test
public void testNonMatch() throws Exception {
- this.repository.getScenarios().getScenario().clear();
- final ScenarioType fallback = new ScenarioType();
- fallback.setName("fallback");
- this.repository.setFallbackScenario(fallback);
- final Result scenario = this.repository.selectScenario(load(Simple.SCENARIOS));
+ this.configInstance.setScenarios(new ArrayList<>());
+ final Scenario fallback = createFallback();
+ this.configInstance.setFallbackScenario(fallback);
+ final Result scenario = this.repository.selectScenario(load(Simple.SCENARIOS));
assertThat(scenario).isNotNull();
assertThat(scenario.isValid()).isFalse();
assertThat(scenario.getObject().getName()).isEqualTo("fallback");
}
+ private static Scenario createFallback() {
+ final ScenarioType t = new ScenarioType();
+ t.setName("fallback");
+ final Scenario fallback = new Scenario(t);
+ fallback.setFallback(true);
+ return fallback;
+ }
+
@Test
public void testMultiMatch() throws Exception {
- final ScenarioType t = new ScenarioType();
- t.setMatch("//*:name");
- t.setName("Test");
- t.initialize(this.content, true);
- this.repository.getScenarios().getScenario().add(t);
- final ScenarioType fallback = new ScenarioType();
- fallback.setName("fallback");
- this.repository.setFallbackScenario(fallback);
- final Result scenario = this.repository.selectScenario(load(Simple.SCENARIOS));
+ this.configInstance.getScenarios().add(createScenario());
+ this.configInstance.setFallbackScenario(createFallback());
+ final Result scenario = this.repository.selectScenario(load(Simple.SCENARIOS));
assertThat(scenario).isNotNull();
assertThat(scenario.isValid()).isFalse();
assertThat(scenario.getObject().getName()).isEqualTo("fallback");
@@ -109,14 +141,7 @@ public class ScenarioRepositoryTest {
return DocumentParseAction.parseDocument(read(uri.toURL())).getObject();
}
- @Test
- public void loadFromJar() throws URISyntaxException {
- this.content = new ContentRepository(ObjectFactory.createProcessor(), Helper.JAR_REPOSITORY.toURI());
- this.repository = new ScenarioRepository(this.content);
- final CheckConfiguration conf = new CheckConfiguration(
- ScenarioRepository.class.getClassLoader().getResource("xrechnung/scenarios.xml").toURI());
- this.repository.initialize(conf);
- assertThat(this.repository.getScenarios()).isNotNull();
+ private static XPathExecutable createXpath(final String expression) {
+ return new ContentRepository(ObjectFactory.createProcessor(), null).createXPath(expression, new HashMap<>());
}
-
}
diff --git a/src/test/java/de/kosit/validationtool/impl/VersioningTest.java b/src/test/java/de/kosit/validationtool/impl/VersioningTest.java
index 661db36..ff67943 100644
--- a/src/test/java/de/kosit/validationtool/impl/VersioningTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/VersioningTest.java
@@ -29,6 +29,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.model.scenarios.Scenarios;
/**
@@ -51,33 +52,35 @@ public class VersioningTest {
private ConversionService service;
+ private ContentRepository repository;
@Before
public void setup() {
+ this.repository = new ContentRepository(ObjectFactory.createProcessor(), Simple.REPOSITORY);
this.service = new ConversionService();
}
@Test
public void testBase() throws URISyntaxException {
- final Scenarios result = this.service.readXml(BASE.toURI(), Scenarios.class, ContentRepository.getScenarioSchema());
+ final Scenarios result = this.service.readXml(BASE.toURI(), Scenarios.class, this.repository.getScenarioSchema());
assertThat(result).isNotNull();
}
@Test
public void testFrameworkIncrement() throws URISyntaxException {
- final Scenarios result = this.service.readXml(INCREMENT.toURI(), Scenarios.class, ContentRepository.getScenarioSchema());
+ final Scenarios result = this.service.readXml(INCREMENT.toURI(), Scenarios.class, this.repository.getScenarioSchema());
assertThat(result).isNotNull();
}
@Test
public void testNewFeature() throws URISyntaxException {
this.exception.expect(ConversionService.ConversionExeption.class);
- this.service.readXml(NEW_FEATURE.toURI(), Scenarios.class, ContentRepository.getScenarioSchema());
+ this.service.readXml(NEW_FEATURE.toURI(), Scenarios.class, this.repository.getScenarioSchema());
}
@Test
public void testNewVersion() throws URISyntaxException {
this.exception.expect(ConversionService.ConversionExeption.class);
- this.service.readXml(NEW_VERSION.toURI(), Scenarios.class, ContentRepository.getScenarioSchema());
+ this.service.readXml(NEW_VERSION.toURI(), Scenarios.class, this.repository.getScenarioSchema());
}
}
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java b/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java
index 53c9885..0065997 100644
--- a/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java
@@ -4,12 +4,17 @@ import static de.kosit.validationtool.impl.tasks.TestBagBuilder.createBag;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Collections;
+import java.util.HashMap;
import org.junit.Test;
import de.kosit.validationtool.api.AcceptRecommendation;
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.ObjectFactory;
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
+import net.sf.saxon.s9api.XPathExecutable;
+
/**
* Tests the 'acceptMatch' functionality.
*
@@ -44,7 +49,7 @@ public class ComputeAcceptanceActionTest {
@Test
public void testValidAcceptMatch() {
final Bag bag = createBag(true, true);
- bag.getScenarioSelectionResult().getObject().setAcceptMatch("count(//doesnotExist) = 0");
+ bag.getScenarioSelectionResult().getObject().setAcceptExecutable(createXpath("count(//doesnotExist) = 0"));
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.ACCEPTABLE);
}
@@ -52,7 +57,7 @@ public class ComputeAcceptanceActionTest {
@Test
public void testAcceptMatchNotSatisfied() {
final Bag bag = createBag(true, true);
- bag.getScenarioSelectionResult().getObject().setAcceptMatch("count(//doesnotExist) = 1");
+ bag.getScenarioSelectionResult().getObject().setAcceptExecutable(createXpath("count(//doesnotExist) = 1"));
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.REJECT);
}
@@ -60,7 +65,7 @@ public class ComputeAcceptanceActionTest {
@Test
public void testAcceptMatchOverridesSchematronErrors() {
final Bag bag = createBag(true, false);
- bag.getScenarioSelectionResult().getObject().setAcceptMatch("count(//doesnotExist) = 0");
+ bag.getScenarioSelectionResult().getObject().setAcceptExecutable(createXpath("count(//doesnotExist) = 0"));
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.ACCEPTABLE);
}
@@ -68,7 +73,7 @@ public class ComputeAcceptanceActionTest {
@Test
public void testValidAcceptMatchOnSchemaFailed() {
final Bag bag = createBag(false, true);
- bag.getScenarioSelectionResult().getObject().setAcceptMatch("count(//doesnotExist) = 0");
+ bag.getScenarioSelectionResult().getObject().setAcceptExecutable(createXpath("count(//doesnotExist) = 0"));
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.REJECT);
}
@@ -98,4 +103,7 @@ public class ComputeAcceptanceActionTest {
}
+ private static XPathExecutable createXpath(final String expression) {
+ return new ContentRepository(ObjectFactory.createProcessor(), null).createXPath(expression, new HashMap<>());
+ }
}
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/SchemaValidatorActionTest.java b/src/test/java/de/kosit/validationtool/impl/tasks/SchemaValidatorActionTest.java
index d720475..354076e 100644
--- a/src/test/java/de/kosit/validationtool/impl/tasks/SchemaValidatorActionTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/SchemaValidatorActionTest.java
@@ -46,9 +46,9 @@ import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.ObjectFactory;
+import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.impl.input.SourceInput;
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
-import de.kosit.validationtool.model.scenarios.ScenarioType;
/**
* Tests die {@link SchemaValidationAction}.
@@ -154,7 +154,7 @@ public class SchemaValidatorActionTest {
@Test
public void testProcessingError() throws IOException, SAXException {
final CheckAction.Bag bag = createBag(InputFactory.read(Simple.SIMPLE_VALID.toURL()));
- final ScenarioType scenario = bag.getScenarioSelectionResult().getObject();
+ final Scenario scenario = bag.getScenarioSelectionResult().getObject();
final Schema schema = mock(Schema.class);
final Validator validator = mock(Validator.class);
when(schema.newValidator()).thenReturn(validator);
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/SchematronValidationActionTest.java b/src/test/java/de/kosit/validationtool/impl/tasks/SchematronValidationActionTest.java
index 6a9a868..80b7176 100644
--- a/src/test/java/de/kosit/validationtool/impl/tasks/SchematronValidationActionTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/SchematronValidationActionTest.java
@@ -17,9 +17,9 @@ import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.ConversionService;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.ObjectFactory;
-import de.kosit.validationtool.impl.model.BaseScenario.Transformation;
+import de.kosit.validationtool.impl.Scenario;
+import de.kosit.validationtool.impl.Scenario.Transformation;
import de.kosit.validationtool.model.scenarios.ResourceType;
-import de.kosit.validationtool.model.scenarios.ScenarioType;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XsltExecutable;
@@ -44,7 +44,7 @@ public class SchematronValidationActionTest {
public void testProcessingError() throws IOException, SaxonApiException {
final CheckAction.Bag bag = createBag(InputFactory.read(Simple.SIMPLE_VALID.toURL()), true);
- final ScenarioType scenario = bag.getScenarioSelectionResult().getObject();
+ final Scenario scenario = bag.getScenarioSelectionResult().getObject();
final XsltExecutable exec = mock(XsltExecutable.class);
final XsltTransformer transformer = mock(XsltTransformer.class);
doThrow(new SaxonApiException("invalid")).when(transformer).transform();
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java b/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java
index 0626a66..78a14f5 100644
--- a/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java
@@ -1,10 +1,14 @@
package de.kosit.validationtool.impl.tasks;
+import java.net.MalformedURLException;
import java.net.URI;
+import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import javax.xml.validation.Schema;
+
import org.oclc.purl.dsdl.svrl.FailedAssert;
import org.oclc.purl.dsdl.svrl.SchematronOutput;
@@ -12,8 +16,8 @@ import de.kosit.validationtool.api.Input;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.Helper;
-import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.ObjectFactory;
+import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
import de.kosit.validationtool.model.reportInput.CreateReportInput;
@@ -50,8 +54,9 @@ public class TestBagBuilder {
return bag;
}
- private static ScenarioType createScenario(final URI schemafile) {
- final ContentRepository repository = new ContentRepository(ObjectFactory.createProcessor(), Simple.REPOSITORY);
+ private static Scenario createScenario(final URI schemafile) {
+
+ try {
final ScenarioType t = new ScenarioType();
final ValidateWithXmlSchema v = new ValidateWithXmlSchema();
final ResourceType r = new ResourceType();
@@ -59,8 +64,16 @@ public class TestBagBuilder {
r.setName("invoice");
v.getResource().add(r);
t.setValidateWithXmlSchema(v);
- t.initialize(repository, true);
- return t;
+ final Scenario scenario = new Scenario(t);
+ scenario.setSchema(createSchema(schemafile.toURL()));
+ return scenario;
+ } catch (final MalformedURLException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private static Schema createSchema(final URL toURL) {
+ return new ContentRepository(ObjectFactory.createProcessor(), null).createSchema(toURL);
}
private static XdmNode createReport() {
From 35c079789876dd6f459cbe824b3f6def94b52483 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Wed, 29 Apr 2020 10:06:00 +0200
Subject: [PATCH 06/36] (enhance) introduce resolving strategy (configurable
xml security); introduce API configuration
---
.idea/misc.xml | 3 +
.../api/CheckConfiguration.java | 26 +-
.../validationtool/api/Configuration.java | 91 ++++--
.../de/kosit/validationtool/api/Input.java | 7 +-
.../validationtool/api/InputFactory.java | 5 -
.../api/ResolvingConfigurationStrategy.java | 64 +++++
.../cmd/CommandLineApplication.java | 28 +-
.../de/kosit/validationtool/cmd/Daemon.java | 199 -------------
.../kosit/validationtool/cmd/DemoBuilder.java | 69 +++++
.../cmd/ExtractHtmlContentAction.java | 12 +-
.../de/kosit/validationtool/cmd/Health.java | 109 -------
.../validationtool/cmd/InternalCheck.java | 4 +-
.../validationtool/cmd/PrintReportAction.java | 8 +-
.../cmd/SerializeReportAction.java | 6 +-
.../config/BaseConfiguration.java | 92 ------
.../kosit/validationtool/config/Builder.java | 12 +
.../config/ConfigurationBuilder.java | 237 ++++++++++++++-
...guration.java => ConfigurationLoader.java} | 114 ++++----
.../config/DefaultConfiguration.java | 40 +++
.../config/FallbackBuilder.java | 95 ++++++
.../validationtool/config/ReportBuilder.java | 116 ++++++++
.../config/ScenarioBuilder.java | 272 +++++++++++++++---
.../validationtool/config/SchemaBuilder.java | 121 ++++++++
.../config/SchematronBuilder.java | 116 ++++++++
.../validationtool/config/XPathBuilder.java | 83 ++++++
.../kosit/validationtool/daemon/Daemon.java | 114 ++++++++
.../validationtool/daemon/HealthHandler.java | 141 +++++++++
.../daemon/HttpServerHandler.java | 61 ++++
.../impl/ClassPathResourceResolver.java | 2 +
.../impl/ContentRepository.java | 72 ++++-
.../impl/ConversionService.java | 36 +--
.../validationtool/impl/DefaultCheck.java | 51 ++--
.../validationtool/impl/HtmlExtractor.java | 40 ++-
.../validationtool/impl/ObjectFactory.java | 68 +----
.../validationtool/impl/ResolvingMode.java | 35 +--
.../kosit/validationtool/impl/Scenario.java | 1 -
.../impl/ScenarioRepository.java | 1 -
.../CreateDocumentIdentificationAction.java | 5 +-
.../impl/tasks/CreateReportAction.java | 131 ++++++++-
.../impl/tasks/DocumentParseAction.java | 7 +-
.../impl/tasks/SchemaValidationAction.java | 28 +-
.../tasks/SchematronValidationAction.java | 19 +-
.../impl/xml/BaseResolvingStrategy.java | 108 +++++++
.../impl/{ => xml}/RelativeUriResolver.java | 17 +-
.../xml/StrictLocalResolvingStrategy.java | 51 ++++
.../xml/StrictRelativeResolvingStrategy.java | 127 ++++++++
.../cmd/CheckAssertionActionTest.java | 14 +-
.../cmd/CommandlineApplicationTest.java | 25 +-
.../cmd/ExtractHtmlActionTest.java | 3 +-
.../cmd/PrintReportActionTest.java | 3 +-
.../cmd/SerializeReportActionTest.java | 3 +-
.../config/SimpleConfigTest.java | 49 ++++
.../impl/ContentRepositoryTest.java | 6 +-
.../impl/ConversionServiceTest.java | 4 +-
.../validationtool/impl/DefaultCheckTest.java | 2 +-
.../impl/DocumentParserTest.java | 7 +-
.../de/kosit/validationtool/impl/Helper.java | 27 +-
.../impl/RelativeUriResolverTest.java | 6 +-
.../impl/SaxonSecurityTest.java | 24 +-
.../impl/ScenarioRepositoryTest.java | 13 +-
.../impl/SimpleScenarioCheckTest.java | 2 +-
.../impl/TestObjectFactory.java | 7 +
.../validationtool/impl/VersioningTest.java | 2 +-
.../tasks/ComputeAcceptanceActionTest.java | 11 +-
.../impl/tasks/SchemaValidatorActionTest.java | 17 +-
.../tasks/SchematronValidationActionTest.java | 6 +-
.../impl/tasks/TestBagBuilder.java | 11 +-
67 files changed, 2441 insertions(+), 845 deletions(-)
create mode 100644 src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java
delete mode 100644 src/main/java/de/kosit/validationtool/cmd/Daemon.java
create mode 100644 src/main/java/de/kosit/validationtool/cmd/DemoBuilder.java
delete mode 100644 src/main/java/de/kosit/validationtool/cmd/Health.java
delete mode 100644 src/main/java/de/kosit/validationtool/config/BaseConfiguration.java
create mode 100644 src/main/java/de/kosit/validationtool/config/Builder.java
rename src/main/java/de/kosit/validationtool/config/{LoadConfiguration.java => ConfigurationLoader.java} (59%)
create mode 100644 src/main/java/de/kosit/validationtool/config/DefaultConfiguration.java
create mode 100644 src/main/java/de/kosit/validationtool/config/FallbackBuilder.java
create mode 100644 src/main/java/de/kosit/validationtool/config/ReportBuilder.java
create mode 100644 src/main/java/de/kosit/validationtool/config/SchemaBuilder.java
create mode 100644 src/main/java/de/kosit/validationtool/config/SchematronBuilder.java
create mode 100644 src/main/java/de/kosit/validationtool/config/XPathBuilder.java
create mode 100644 src/main/java/de/kosit/validationtool/daemon/Daemon.java
create mode 100644 src/main/java/de/kosit/validationtool/daemon/HealthHandler.java
create mode 100644 src/main/java/de/kosit/validationtool/daemon/HttpServerHandler.java
rename src/main/java/de/kosit/validationtool/impl/{ => tasks}/CreateDocumentIdentificationAction.java (82%)
create mode 100644 src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java
rename src/main/java/de/kosit/validationtool/impl/{ => xml}/RelativeUriResolver.java (89%)
create mode 100644 src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java
create mode 100644 src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
create mode 100644 src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java
create mode 100644 src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java
diff --git a/.idea/misc.xml b/.idea/misc.xml
index aebea3c..f207c86 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,9 @@
+
+
+
diff --git a/src/main/java/de/kosit/validationtool/api/CheckConfiguration.java b/src/main/java/de/kosit/validationtool/api/CheckConfiguration.java
index 31f6aff..10b3d56 100644
--- a/src/main/java/de/kosit/validationtool/api/CheckConfiguration.java
+++ b/src/main/java/de/kosit/validationtool/api/CheckConfiguration.java
@@ -21,18 +21,17 @@ package de.kosit.validationtool.api;
import java.net.URI;
import java.util.List;
+import java.util.Map;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
-import de.kosit.validationtool.config.LoadConfiguration;
+import de.kosit.validationtool.config.ConfigurationLoader;
import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.Scenario;
-import net.sf.saxon.s9api.Processor;
-
/**
* Zentrale Konfigration einer Prüf-Instanz.
*
@@ -56,11 +55,13 @@ public class CheckConfiguration implements Configuration {
*/
private URI scenarioRepository;
- private LoadConfiguration delegate;
+ private ConfigurationLoader loader;
- private LoadConfiguration getDelegate() {
+ private Configuration delegate;
+
+ private Configuration getDelegate() {
if (this.delegate == null) {
- this.delegate = Configuration.load(this.scenarioDefinition, this.scenarioRepository);
+ this.delegate = Configuration.load(this.scenarioDefinition, this.scenarioRepository).build();
}
return this.delegate;
}
@@ -76,13 +77,13 @@ public class CheckConfiguration implements Configuration {
}
@Override
- public void build() {
- getDelegate().build();
+ public String getDate() {
+ return getDelegate().getDate();
}
@Override
- public String getDate() {
- return getDelegate().getDate();
+ public Map getAdditionalParameters() {
+ return this.delegate.getAdditionalParameters();
}
@Override
@@ -95,10 +96,7 @@ public class CheckConfiguration implements Configuration {
return getDelegate().getAuthor();
}
- @Override
- public Processor getProcessor() {
- return getDelegate().getProcessor();
- }
+
@Override
public ContentRepository getContentRepository() {
diff --git a/src/main/java/de/kosit/validationtool/api/Configuration.java b/src/main/java/de/kosit/validationtool/api/Configuration.java
index ac99275..dbbb53d 100644
--- a/src/main/java/de/kosit/validationtool/api/Configuration.java
+++ b/src/main/java/de/kosit/validationtool/api/Configuration.java
@@ -2,20 +2,19 @@ package de.kosit.validationtool.api;
import java.net.URI;
import java.util.List;
+import java.util.Map;
import de.kosit.validationtool.config.ConfigurationBuilder;
-import de.kosit.validationtool.config.LoadConfiguration;
+import de.kosit.validationtool.config.ConfigurationLoader;
import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.Scenario;
-import net.sf.saxon.s9api.Processor;
-
/**
- * Configuration of the actual {@link Check} instance. This is a contruct and can be used implemented by custom
+ * Configuration of the actual {@link Check} instance. This is an interface and can be implemented by custom
* configuration classes. There are two implementations supported out of the box:
*
*
- * {@link LoadConfiguration} implements loading {@link Check} configurations from a scenario.xml file
+ * {@link ConfigurationLoader} implements loading {@link Check} configurations from a scenario.xml file
* Using a builder style api {@link de.kosit.validationtool.config.ConfigurationBuilder}to configure the
* {@link Check}
*
@@ -27,33 +26,77 @@ import net.sf.saxon.s9api.Processor;
public interface Configuration {
+ /**
+ * Returns a list of configured scenarios.
+ *
+ * @return the list of scenarios
+ */
List getScenarios();
- static LoadConfiguration load(final URI scenarioDefinition) {
+ /**
+ * Returns the configured fallback scenario to use, in case no configured scenario match.
+ *
+ * @return the fallback scenario
+ */
+ Scenario getFallbackScenario();
+
+ /**
+ * Returns the author of this configuration.
+ *
+ * @return the author
+ */
+ String getAuthor();
+
+ /**
+ * Returns the name of the specification
+ *
+ * @return the name
+ */
+ String getName();
+
+ /**
+ * The creation date of the config
+ *
+ * @return the date
+ */
+ String getDate();
+
+ Map getAdditionalParameters();
+
+ /**
+ * The content repository including resolving strategies.
+ *
+ * @return the configured {@link ContentRepository}
+ */
+ ContentRepository getContentRepository();
+
+ /**
+ * Loads an XML based scenario definition from the file specified via URI.
+ *
+ * @param scenarioDefinition the XML file with scenario definition
+ * @return the loaded configuration
+ */
+ static ConfigurationLoader load(final URI scenarioDefinition) {
return load(scenarioDefinition, null);
}
- static LoadConfiguration load(final URI scenarioDefinition, final URI repository) {
- final LoadConfiguration config = new LoadConfiguration(scenarioDefinition, repository);
- config.build();
- return config;
+ /**
+ * Loads an XML based scenario definition from the file with an specific repository / source location specified via
+ * URIs.
+ *
+ * @param scenarioDefinition the XML file with scenario definition
+ * @return the loaded configuration
+ */
+ static ConfigurationLoader load(final URI scenarioDefinition, final URI repository) {
+ return new ConfigurationLoader(scenarioDefinition, repository);
}
+ /**
+ * Creates a {@link Configuration} based on a builder style API using {@link ConfigurationBuilder}
+ *
+ * @return the Builder
+ */
static ConfigurationBuilder create() {
return new ConfigurationBuilder();
}
-
- Scenario getFallbackScenario();
-
- void build();
-
- String getAuthor();
-
- String getName();
-
- String getDate();
-
- Processor getProcessor();
-
- ContentRepository getContentRepository();
}
diff --git a/src/main/java/de/kosit/validationtool/api/Input.java b/src/main/java/de/kosit/validationtool/api/Input.java
index ca27ebf..83794b6 100644
--- a/src/main/java/de/kosit/validationtool/api/Input.java
+++ b/src/main/java/de/kosit/validationtool/api/Input.java
@@ -20,7 +20,6 @@
package de.kosit.validationtool.api;
import java.io.IOException;
-import java.io.InputStream;
import javax.xml.transform.Source;
@@ -54,10 +53,10 @@ public interface Input {
String getDigestAlgorithm();
/**
- * Opens a new {@link InputStream } for this input which carries the actual data
+ * Creates a new {@link Source } for this input which carries the actual data
*
- * @return an open {@link InputStream}
- * @throws IOException on I/O while opening the stream
+ * @return an open {@link Source}
+ * @throws IOException on I/O while opening the source
*/
Source getSource() throws IOException;
diff --git a/src/main/java/de/kosit/validationtool/api/InputFactory.java b/src/main/java/de/kosit/validationtool/api/InputFactory.java
index 77d99d0..8cf380f 100644
--- a/src/main/java/de/kosit/validationtool/api/InputFactory.java
+++ b/src/main/java/de/kosit/validationtool/api/InputFactory.java
@@ -54,10 +54,6 @@ public class InputFactory {
static final String DEFAULT_ALGORITH = "SHA-256";
- private static final int EOF = -1;
-
- private static final int DEFAULT_BUFFER_SIZE = 4096;
-
private static final String MESSAGE_OPEN_STREAM_ERROR = "Can not open stream from";
@Getter
@@ -108,7 +104,6 @@ public class InputFactory {
return read(file, DEFAULT_ALGORITH);
}
-
/**
* Liest einen Prüfling von der übergebenen URI. Es wird der Default-Prüfsummenalgorithmus zur Ermittlung der Prüfsumme
* genutzt.
diff --git a/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java b/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java
new file mode 100644
index 0000000..b8ec0b6
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java
@@ -0,0 +1,64 @@
+package de.kosit.validationtool.api;
+
+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 net.sf.saxon.s9api.Processor;
+
+/**
+ * Centralized construction and configuration of XML related infrastructore components. The KoSIT Validator provides out
+ * of the box implementaions with various security levels.
+ *
+ * If you decide to implement a custom strategy, please be aware of XML security within your stack. The validator
+ * components beyond this strategy asume secured implementation of the interfaces provided by this strategy. There is no
+ * effort to mitigate or prevent xml related security issues such as XXE, loading external sources etc.
+ *
+ * @see de.kosit.validationtool.impl.ResolvingMode
+ * @author Andreas Penski
+ */
+public interface ResolvingConfigurationStrategy {
+
+ /**
+ * Creates a preconfigured {@link SchemaFactory} for loading {@link javax.xml.validation.Schema} objects. The
+ * implementation is responsible for xml security. Take care
+ *
+ * @return preconfigured {@link SchemaFactory}
+ */
+ SchemaFactory createSchemaFactory();
+
+ /**
+ * Creates a preconfigured {@link Processor Saxon Processor} for various tasks within the Validator. The validator
+ * leverages the saxon s9api for internal processing e.g. xml reading and writing. So this is the main object to secure
+ * for reading, transforming and writing xml files.
+ *
+ * @return a preconfigured {@link Processor}
+ */
+ Processor createProcessor();
+
+ /**
+ * Creates a specific implementation for resolving referenced objects in XML files. The URIResolver, it is used for
+ * dereferencing an absolute URI (after resolution) to return a {@link javax.xml.transform.Source}. It can be
+ * used for resolving relative URIs against a base URI or restrict access to certain URIs.
+ *
+ * This URIResolver is used to dereference the URIs appearing in xsl:import, xsl:include, and
+ * xsl:import-schema declarations.
+ *
+ *
+ * @return a preconfigured {@link URIResolver}
+ */
+ URIResolver createResolver(URI scenarioRepository);
+
+ /**
+ * Creates a preconfigured {@link Validator } instance for a given schema for xml file validation. The implementation
+ * takes care about security and reference resolving strategies.
+ *
+ * @param schema the scheme to create a {@link Validator} for
+ * @return a preconfigured {@link Validator}
+ */
+ Validator createValidator(Schema schema);
+
+}
diff --git a/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java b/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java
index 21527f9..917fe57 100644
--- a/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java
+++ b/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java
@@ -42,12 +42,15 @@ import org.apache.commons.lang3.StringUtils;
import lombok.extern.slf4j.Slf4j;
-import de.kosit.validationtool.api.CheckConfiguration;
+import de.kosit.validationtool.api.Configuration;
import de.kosit.validationtool.api.Input;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.cmd.assertions.Assertions;
+import de.kosit.validationtool.config.ConfigurationLoader;
+import de.kosit.validationtool.daemon.Daemon;
import de.kosit.validationtool.impl.ConversionService;
-import de.kosit.validationtool.impl.ObjectFactory;
+
+import net.sf.saxon.s9api.Processor;
/**
* Commandline Version des Prüftools. Parsed die Kommandozeile und führt die konfigurierten Aktionen aus.
@@ -169,9 +172,9 @@ public class CommandLineApplication {
private static int startDaemonMode(final CommandLine cmd) {
final Option[] unavailable = new Option[] { PRINT, CHECK_ASSERTIONS, DEBUG, OUTPUT, EXTRACT_HTML };
warnUnusedOptions(cmd, unavailable, true);
- final Daemon validDaemon = new Daemon(determineDefinition(cmd), determineRepository(cmd), determineHost(cmd), determinePort(cmd),
- determineThreads(cmd));
- validDaemon.startServer();
+ final ConfigurationLoader config = Configuration.load(determineDefinition(cmd), determineRepository(cmd));
+ final Daemon validDaemon = new Daemon(determineHost(cmd), determinePort(cmd), determineThreads(cmd));
+ validDaemon.startServer(config.build());
return DAEMON_SIGNAL;
}
@@ -203,25 +206,26 @@ public class CommandLineApplication {
long start = System.currentTimeMillis();
final Option[] unavailable = new Option[] { HOST, PORT, WORKER_COUNT };
warnUnusedOptions(cmd, unavailable, false);
- final CheckConfiguration d = new CheckConfiguration(determineDefinition(cmd));
- d.setScenarioRepository(determineRepository(cmd));
- final InternalCheck check = new InternalCheck(d);
+ final Configuration config = Configuration.load(determineDefinition(cmd), determineRepository(cmd)).build();
+
+ final InternalCheck check = new InternalCheck(config);
final Path outputDirectory = determineOutputDirectory(cmd);
+ final Processor processor = config.getContentRepository().getProcessor();
if (cmd.hasOption(EXTRACT_HTML.getOpt())) {
- check.getCheckSteps().add(new ExtractHtmlContentAction(check.getContentRepository(), outputDirectory));
+ check.getCheckSteps().add(new ExtractHtmlContentAction(processor, outputDirectory));
}
- check.getCheckSteps().add(new SerializeReportAction(outputDirectory));
+ check.getCheckSteps().add(new SerializeReportAction(outputDirectory, processor));
if (cmd.hasOption(SERIALIZE_REPORT_INPUT.getOpt())) {
check.getCheckSteps().add(new SerializeReportInputAction(outputDirectory, check.getConversionService()));
}
if (cmd.hasOption(PRINT.getOpt())) {
- check.getCheckSteps().add(new PrintReportAction());
+ check.getCheckSteps().add(new PrintReportAction(processor));
}
if (cmd.hasOption(CHECK_ASSERTIONS.getOpt())) {
final Assertions assertions = loadAssertions(cmd.getOptionValue(CHECK_ASSERTIONS.getOpt()));
- check.getCheckSteps().add(new CheckAssertionAction(assertions, ObjectFactory.createProcessor()));
+ check.getCheckSteps().add(new CheckAssertionAction(assertions, processor));
}
if (cmd.hasOption(PRINT_MEM_STATS.getOpt())) {
check.getCheckSteps().add(new PrintMemoryStats());
diff --git a/src/main/java/de/kosit/validationtool/cmd/Daemon.java b/src/main/java/de/kosit/validationtool/cmd/Daemon.java
deleted file mode 100644
index 91aceda..0000000
--- a/src/main/java/de/kosit/validationtool/cmd/Daemon.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package de.kosit.validationtool.cmd;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.net.URI;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-
-import org.w3c.dom.Document;
-
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpServer;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import lombok.Setter;
-import lombok.extern.slf4j.Slf4j;
-
-import de.kosit.validationtool.api.Check;
-import de.kosit.validationtool.api.CheckConfiguration;
-import de.kosit.validationtool.api.Configuration;
-import de.kosit.validationtool.api.InputFactory;
-import de.kosit.validationtool.impl.DefaultCheck;
-import de.kosit.validationtool.impl.ObjectFactory;
-import de.kosit.validationtool.impl.input.SourceInput;
-
-/**
- * HTTP-Daemon für die Bereitstellung der Prüf-Funktionalität via http.
- *
- * @author Roula Antoun
- */
-@RequiredArgsConstructor
-@Setter
-@Getter
-@Slf4j
-class Daemon {
-
- /**
- * Wir benötigen einen Handler, der zur Verarbeitung von HTTP-Anforderungen aufgerufen wird um hier die Verarbeitung des
- * POST Request zu realisieren.
- */
- @Slf4j
- private static class HttpServerHandler implements HttpHandler {
-
- private static final AtomicLong counter = new AtomicLong(0);
-
- private final Check implemenation;
-
- HttpServerHandler(final Check check) {
- this.implemenation = check;
- }
-
- /**
- * Methode, die eine gegebene Anforderung verarbeitet und eine entsprechende Antwort generiert
- *
- * @param httpExchange kapselt eine empfangene HTTP-Anforderung und eine Antwort, die in einem Exchange generiert werden
- * soll.
- */
- @Override
- public void handle(final HttpExchange httpExchange) throws IOException {
- try {
- log.debug("Incoming request");
- final String requestMethod = httpExchange.getRequestMethod();
- if (requestMethod.equals("POST")) {
- final InputStream inputStream = httpExchange.getRequestBody();
- final SourceInput serverInput = (SourceInput) InputFactory.read(inputStream, "Prüfling" + counter.incrementAndGet());
-
- if (inputStream.available() > 0) {
- writeOutputstreamArray(httpExchange, this.implemenation.check(serverInput));
- } else {
- writeError(httpExchange, 400, "XML-Inhalt erforderlich!");
- }
-
- } else {
- writeError(httpExchange, 405, "Es ist nur die POST-Methode erlaubt!");
- }
- } catch (final Exception e) {
- writeError(httpExchange, 500, "Interner Fehler bei der Verarbeitung des Requests: " + e.getMessage());
- log.error("Es ist ein Fehler aufgetreten. Das Dokument kann nicht geprüft werden", e);
- }
- }
-
- }
-
- /**
- * Wir benötigen einen Handler, der zur Verarbeitung von HTTP-Anforderungen aufgerufen wird , und hier für Verarbeitung
- * das GET Request um Health-Endpunkt zu erstellen. Die Klasse HealthHandler implementiert diese Schnittstelle
- */
- @Slf4j
- static class HealthHandler implements HttpHandler {
-
- private final Configuration scenarios;
-
- HealthHandler(final Configuration config) {
- this.scenarios = config;
- }
-
- @Override
- public void handle(final HttpExchange httpExchange) throws IOException {
- final Health health = new Health(this.scenarios);
- final Document doc = health.writeHealthXml();
- try {
- writeOutputstreamArray(httpExchange, doc);
- } catch (final TransformerException e) {
- writeError(httpExchange, 500, e.getMessage());
- log.error("Fehler beim Erzeugen der Status-Information", e);
- }
- }
- }
-
- private final URI scenarioDefinition;
-
- private final URI repository;
-
- private final String hostName;
-
- private final int port;
-
- private final int threadCount;
-
- /**
- * Methode, die die Antwort als String-Text schreibt
- *
- * @param httpExchange um den Antwort Body zu erhalten
- * @param rCode der Code-Status
- * @param response die String antwort, die ich anzeigen möchte
- */
- private static void writeError(final HttpExchange httpExchange, final int rCode, final String response) throws IOException {
- httpExchange.sendResponseHeaders(rCode, response.length());
- final OutputStream os = httpExchange.getResponseBody();
- os.write(response.getBytes());
- os.close();
- }
-
- /**
- * Methode, die die Antwort als String-Text schreibt
- *
- * @param httpExchange um den Antwort Body zu erhalten
- * @param doc der Report
- */
- private static void writeOutputstreamArray(final HttpExchange httpExchange, final Document doc)
- throws IOException, TransformerException {
- final byte[] bytes = serialize(doc);
- final OutputStream os = httpExchange.getResponseBody();
- httpExchange.getResponseHeaders().add("Content-Type", "application/xml");
- httpExchange.sendResponseHeaders(200, bytes.length);
- os.write(bytes);
- os.close();
- log.debug("Xml File erzeugen ist Fertig ");
- }
-
- /**
- * Methode zum Serialisieren des Dokuments.
- *
- * @param report Vom Typ Dokument, aka Report .
- */
- private static byte[] serialize(final Document report) throws TransformerException {
-
- try ( final ByteArrayOutputStream bArrayOS = new ByteArrayOutputStream() ) {
- final DOMSource source = new DOMSource(report);
- final StreamResult streamResult = new StreamResult(bArrayOS);
- final Transformer transformer = ObjectFactory.createTransformer(true);
- transformer.transform(source, streamResult);
- return bArrayOS.toByteArray();
- } catch (final IOException e) {
- log.error("Report {}", e.getMessage(), e);
- throw new IllegalStateException(e);
- }
- }
-
- /**
- * Methode zum Starten des Servers
- */
- void startServer() {
- final CheckConfiguration config = new CheckConfiguration(this.scenarioDefinition);
- config.setScenarioRepository(this.repository);
- HttpServer server = null;
- try {
- server = HttpServer.create(new InetSocketAddress(this.hostName, this.port), 0);
- final DefaultCheck check = new DefaultCheck(config);
- server.createContext("/", new HttpServerHandler(check));
- server.createContext("/health", new HealthHandler(config));
- server.setExecutor(Executors.newFixedThreadPool(this.threadCount));
- server.start();
- log.info("Server unter Port {} ist erfolgreich gestartet", this.port);
- } catch (final IOException e) {
- log.error("Fehler beim HttpServer erstellen: {}", e.getMessage(), e);
- }
- }
-}
diff --git a/src/main/java/de/kosit/validationtool/cmd/DemoBuilder.java b/src/main/java/de/kosit/validationtool/cmd/DemoBuilder.java
new file mode 100644
index 0000000..074d08d
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/cmd/DemoBuilder.java
@@ -0,0 +1,69 @@
+package de.kosit.validationtool.cmd;
+
+import static de.kosit.validationtool.config.ConfigurationBuilder.defaultFallback;
+import static de.kosit.validationtool.config.ConfigurationBuilder.report;
+import static de.kosit.validationtool.config.ConfigurationBuilder.scenario;
+import static de.kosit.validationtool.config.ConfigurationBuilder.schema;
+import static de.kosit.validationtool.config.ConfigurationBuilder.schematron;
+
+import java.net.URI;
+
+import javax.xml.validation.Schema;
+
+import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.config.FallbackBuilder;
+import de.kosit.validationtool.config.ScenarioBuilder;
+import de.kosit.validationtool.impl.ResolvingMode;
+
+import net.sf.saxon.s9api.XPathExecutable;
+
+/**
+ * @author Andreas Penski
+ */
+public class DemoBuilder {
+
+ public static void main(final String[] args) {
+ final XPathExecutable xpath = null;
+ // @formatter:off
+ Configuration
+ .create()
+ .name("some config")
+ .resolvingMode(ResolvingMode.JDK_SUPPORTED)
+ .with(scenario("s1").match("//name").validate(schema("http://some.schema.url")).description("some desc"))
+ .with(scenario("s2")
+ .match(xpath)
+ .acceptWith(xpath)
+ .validate(schema(URI.create("http://some.other.schema.url")))
+ .validate(schematron("some checks").source("some-schematron.xsl"))
+ .with(report("myReport").source(URI.create("some.xsl")))
+ .description("some desc"))
+ .with(defaultFallback())
+
+ .build();
+
+ Configuration
+ .create()
+ .name("xrechnung")
+ .resolvingMode(ResolvingMode.STRICT_LOCAL)
+ .with( ubl() )
+ .with(cii())
+ .with( myFallback())
+ .build();
+ // @formatter:on
+ }
+
+ private static ScenarioBuilder cii() {
+ return null;
+ }
+
+ private static FallbackBuilder myFallback() {
+ return new FallbackBuilder();
+ }
+
+ private static ScenarioBuilder ubl() {
+ final Schema schema = null; // load somehow
+ final ScenarioBuilder ubl = scenario("ubl");
+ ubl.validate(schema("someSchema", schema));
+ return ubl;
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java b/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java
index 09e84b6..4c472d6 100644
--- a/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java
+++ b/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java
@@ -24,10 +24,10 @@ import java.nio.file.Path;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.HtmlExtractor;
import de.kosit.validationtool.impl.tasks.CheckAction;
+import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
@@ -49,12 +49,12 @@ class ExtractHtmlContentAction implements CheckAction {
private HtmlExtractor htmlExtraction;
- private ContentRepository repository;
+ private Processor processor;
- public ExtractHtmlContentAction(final ContentRepository repository, final Path outputDirectory) {
+ public ExtractHtmlContentAction(final Processor p, final Path outputDirectory) {
this.outputDirectory = outputDirectory;
- this.htmlExtraction = new HtmlExtractor(repository);
- this.repository = repository;
+ this.htmlExtraction = new HtmlExtractor(p);
+ this.processor = p;
}
@Override
@@ -66,7 +66,7 @@ class ExtractHtmlContentAction implements CheckAction {
final XdmNode node = (XdmNode) xdmItem;
final String name = origName + "-" + node.getAttributeValue(NAME_ATTRIBUTE);
final Path file = this.outputDirectory.resolve(name + ".html");
- final Serializer serializer = this.repository.getProcessor().newSerializer(file.toFile());
+ final Serializer serializer = this.processor.newSerializer(file.toFile());
try {
log.info("Writing report html '{}' to {}", name, file.toAbsolutePath());
serializer.serializeNode(node);
diff --git a/src/main/java/de/kosit/validationtool/cmd/Health.java b/src/main/java/de/kosit/validationtool/cmd/Health.java
deleted file mode 100644
index 2694142..0000000
--- a/src/main/java/de/kosit/validationtool/cmd/Health.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package de.kosit.validationtool.cmd;
-
-import javax.xml.parsers.DocumentBuilder;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
-import lombok.extern.slf4j.Slf4j;
-
-import de.kosit.validationtool.api.Configuration;
-import de.kosit.validationtool.impl.ObjectFactory;
-
-/**
- * Klasse zur Erzeugung Health Xml , die optiamle Status.
- *
- * @author Roula Antoun
- */
-@Slf4j
-class Health {
-
- private final long freeMemory;
-
- private final long maxMemory;
-
- private final long totalMemory;
-
- private final Configuration config;
-
- Health(final Configuration config) {
-
- final Runtime runtime = Runtime.getRuntime();
- this.freeMemory = runtime.freeMemory();
- this.maxMemory = runtime.maxMemory();
- this.totalMemory = runtime.totalMemory();
- this.config = config;
- }
-
- /**
- * Methode, die schreibt das Health Xml für optimale Status
- *
- */
- Document writeHealthXml() {
- final DocumentBuilder dBuilder = ObjectFactory.createDocumentBuilder(false);
- final Document doc = dBuilder.newDocument();
- final Element rootElement = doc.createElementNS("https://localhost:8080/Health", "Health");
- doc.appendChild(rootElement);
- rootElement.appendChild(getMemory(doc, this.freeMemory, this.maxMemory, this.totalMemory));
- rootElement.appendChild(getState(doc));
- rootElement.appendChild(getScenario(doc, this.config));
- return doc;
- }
-
- /**
- * Methode, die schreibt das System Status Node im Xml File
- *
- * @param doc Vom Typ Dokument.
- *
- */
- private static Node getState(final Document doc) {
- final Element state = doc.createElement("state");
- state.setAttribute("indicator", "OK");
- final Element stateNode = doc.createElement("message");
- stateNode.appendChild(doc.createTextNode("System is up and running normally"));
- state.appendChild(stateNode);
- return state;
- }
-
- /**
- * Methode, die schreibt das Scnarios Information Node im Xml File
- *
- * @param doc Vom Typ Dokument .
- * @param config Vom Typ {@link Configuration} das verwendete scenario.
- *
- */
- private static Node getScenario(final Document doc, final Configuration config) {
- final Element scenario = doc.createElement("scenario");
- final Element scenarioNameNode = doc.createElement("name");
- scenarioNameNode.appendChild(doc.createTextNode(config.getName()));
- scenario.appendChild(scenarioNameNode);
- return scenario;
- }
-
- /**
- * Methode, die schreibt das Scnarios Information Node im Xml File
- *
- * @param doc Vom Typ Dokument .
- * @param freeMemory Vom Typ long , der freier Speicher.
- * @param maxMemory Vom Typ long , der maximaler Speicher
- * @param totalMemory Vom Typ long , der Gesamte speicher.
- *
- */
- private static Node getMemory(final Document doc, final long freeMemory, final long maxMemory, final long totalMemory) {
- final Element memory = doc.createElement("memoryState");
- final String freeM = Long.toString(freeMemory);
- final Element freeMNode = doc.createElement("freeMemory");
- freeMNode.appendChild(doc.createTextNode(freeM));
- memory.appendChild(freeMNode);
- final String maxM = Long.toString(maxMemory);
- final Element maxMNode = doc.createElement("maxMemory");
- maxMNode.appendChild(doc.createTextNode(maxM));
- memory.appendChild(maxMNode);
- final String totalM = Long.toString(totalMemory);
- final Element totalMNode = doc.createElement("totalMemory");
- totalMNode.appendChild(doc.createTextNode(totalM));
- memory.appendChild(totalMNode);
- return memory;
- }
-}
diff --git a/src/main/java/de/kosit/validationtool/cmd/InternalCheck.java b/src/main/java/de/kosit/validationtool/cmd/InternalCheck.java
index db9a7b1..2a2c007 100644
--- a/src/main/java/de/kosit/validationtool/cmd/InternalCheck.java
+++ b/src/main/java/de/kosit/validationtool/cmd/InternalCheck.java
@@ -21,7 +21,7 @@ package de.kosit.validationtool.cmd;
import lombok.extern.slf4j.Slf4j;
-import de.kosit.validationtool.api.CheckConfiguration;
+import de.kosit.validationtool.api.Configuration;
import de.kosit.validationtool.api.Input;
import de.kosit.validationtool.api.Result;
import de.kosit.validationtool.impl.DefaultCheck;
@@ -45,7 +45,7 @@ class InternalCheck extends DefaultCheck {
*
* @param configuration die Konfiguration
*/
- InternalCheck(final CheckConfiguration configuration) {
+ InternalCheck(final Configuration configuration) {
super(configuration);
}
diff --git a/src/main/java/de/kosit/validationtool/cmd/PrintReportAction.java b/src/main/java/de/kosit/validationtool/cmd/PrintReportAction.java
index 2c3faa2..0659397 100644
--- a/src/main/java/de/kosit/validationtool/cmd/PrintReportAction.java
+++ b/src/main/java/de/kosit/validationtool/cmd/PrintReportAction.java
@@ -21,11 +21,12 @@ package de.kosit.validationtool.cmd;
import java.io.StringWriter;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import de.kosit.validationtool.impl.ObjectFactory;
import de.kosit.validationtool.impl.tasks.CheckAction;
+import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
@@ -35,13 +36,16 @@ import net.sf.saxon.s9api.Serializer;
* @author Andreas Penski
*/
@Slf4j
+@RequiredArgsConstructor
class PrintReportAction implements CheckAction {
+ private final Processor processor;
+
@Override
public void check(Bag results) {
try {
final StringWriter writer = new StringWriter();
- final Serializer serializer = ObjectFactory.createProcessor().newSerializer(writer);
+ final Serializer serializer = processor.newSerializer(writer);
serializer.serializeNode(results.getReport());
System.out.print(writer.toString());
} catch (SaxonApiException e) {
diff --git a/src/main/java/de/kosit/validationtool/cmd/SerializeReportAction.java b/src/main/java/de/kosit/validationtool/cmd/SerializeReportAction.java
index 1c26479..cc2ef04 100644
--- a/src/main/java/de/kosit/validationtool/cmd/SerializeReportAction.java
+++ b/src/main/java/de/kosit/validationtool/cmd/SerializeReportAction.java
@@ -24,9 +24,9 @@ import java.nio.file.Path;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import de.kosit.validationtool.impl.ObjectFactory;
import de.kosit.validationtool.impl.tasks.CheckAction;
+import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
@@ -41,12 +41,14 @@ class SerializeReportAction implements CheckAction {
private final Path outputDirectory;
+ private final Processor processor;
+
@Override
public void check(Bag results) {
final Path file = outputDirectory.resolve(results.getName() + "-report.xml");
try {
log.info("Serializing result to {}", file.toAbsolutePath());
- final Serializer serializer = ObjectFactory.createProcessor().newSerializer(file.toFile());
+ final Serializer serializer = processor.newSerializer(file.toFile());
serializer.serializeNode(results.getReport());
} catch (SaxonApiException e) {
log.error("Can not serialize result report to {}", file.toAbsolutePath(), e);
diff --git a/src/main/java/de/kosit/validationtool/config/BaseConfiguration.java b/src/main/java/de/kosit/validationtool/config/BaseConfiguration.java
deleted file mode 100644
index 167099a..0000000
--- a/src/main/java/de/kosit/validationtool/config/BaseConfiguration.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package de.kosit.validationtool.config;
-
-import java.util.List;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import lombok.Setter;
-import lombok.extern.slf4j.Slf4j;
-
-import de.kosit.validationtool.api.Configuration;
-import de.kosit.validationtool.impl.ObjectFactory;
-import de.kosit.validationtool.impl.Scenario;
-
-import net.sf.saxon.s9api.Processor;
-
-/**
- * Base configuration class.
- *
- * @author Andreas Penski
- */
-@Slf4j
-public abstract class BaseConfiguration implements Configuration {
-
- @RequiredArgsConstructor
- @Getter
- @Setter
- protected static class RuntimeArtefacts {
-
- private final List scenarios;
-
- private final Scenario fallbackScenario;
-
- private String name;
-
- private String author;
-
- private String date;
- }
-
- private RuntimeArtefacts artefacts;
-
- protected abstract RuntimeArtefacts buildArtefacts();
-
- @Override
- public void build() {
- if (this.artefacts != null) {
- log.warn("Configuration already complete. Will drop previous artefacts and build again");
- }
- this.artefacts = buildArtefacts();
- }
-
- @Override
- public List getScenarios() {
- assertBuild();
- return this.artefacts.getScenarios();
- }
-
- @Override
- public Scenario getFallbackScenario() {
- assertBuild();
- return this.artefacts.getFallbackScenario();
- }
-
- private void assertBuild() {
- if (this.artefacts == null) {
- throw new IllegalStateException("Configuration");
- }
- }
-
- @Override
- public Processor getProcessor() {
- return ObjectFactory.createProcessor();
- }
-
- @Override
- public String getAuthor() {
- assertBuild();
- return this.artefacts.getAuthor();
- }
-
- @Override
- public String getName() {
- assertBuild();
- return this.artefacts.getName();
- }
-
- @Override
- public String getDate() {
- assertBuild();
- return this.artefacts.getDate();
- }
-}
diff --git a/src/main/java/de/kosit/validationtool/config/Builder.java b/src/main/java/de/kosit/validationtool/config/Builder.java
new file mode 100644
index 0000000..bdee7fd
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/Builder.java
@@ -0,0 +1,12 @@
+package de.kosit.validationtool.config;
+
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.model.Result;
+
+/**
+ * @author Andreas Penski
+ */
+public interface Builder {
+
+ Result build(ContentRepository repository);
+}
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
index 27dc6b5..e1ec359 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
@@ -1,10 +1,245 @@
package de.kosit.validationtool.config;
+import java.net.URI;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.xml.validation.Schema;
+
+import org.apache.commons.lang3.NotImplementedException;
+
+import lombok.extern.slf4j.Slf4j;
+
+import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.ResolvingMode;
+import de.kosit.validationtool.impl.Scenario;
+import de.kosit.validationtool.impl.model.Result;
+
+import net.sf.saxon.s9api.Processor;
+
/**
+ * Implements a builder style creation of a {@link Configuration}.
+ *
* @author Andreas Penski
*/
+@Slf4j
public class ConfigurationBuilder {
- private ScenarioBuilder scenarioBuilder;
+ private final List scenarios = new ArrayList<>();
+ private FallbackBuilder fallbackBuilder;
+
+ private ResolvingConfigurationStrategy resolvingConfigurationStrategy;
+
+ private ResolvingMode resolvingMode = ResolvingMode.STRICT_RELATIVE;
+
+ private Processor processor;
+
+ private String author = "API";
+
+ private String date = LocalDate.now().toString();
+
+ private String name = "Custom";
+
+ private final Map parameters = new HashMap<>();
+
+ private URI repository;
+
+ public ConfigurationBuilder author(final String authorName) {
+ this.author = authorName;
+ return this;
+ }
+
+ public ConfigurationBuilder date(final LocalDate localDate) {
+ this.date = localDate.toString();
+ return this;
+ }
+
+ public ConfigurationBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ public ConfigurationBuilder date(final Date date) {
+ return date(LocalDate.ofEpochDay(date.getTime()));
+ }
+
+ public ConfigurationBuilder with(final ScenarioBuilder scenarioBuilder) {
+ this.scenarios.add(scenarioBuilder);
+ return this;
+ }
+
+ public ConfigurationBuilder with(final FallbackBuilder builder) {
+ if (this.fallbackBuilder != null) {
+ log.warn("Overriding previously created fallback scenario");
+ }
+ this.fallbackBuilder = builder;
+ return this;
+ }
+
+ /**
+ * Create a fallback scenario configuration.
+ *
+ * @return the builder
+ */
+ public static FallbackBuilder fallback() {
+ return new FallbackBuilder();
+ }
+
+ /**
+ * Create the default fallback configuration if new scenario match. Note: this is public for explicit usage. If no
+ * fallback is configured, this is the still default fallback.
+ *
+ * @return a fallback configuration
+ */
+ public static FallbackBuilder defaultFallback() {
+ throw new NotImplementedException("Not yet defined");
+ }
+
+ public static SchematronBuilder schematron(final String name) {
+ return new SchematronBuilder().name(name);
+ }
+
+ /**
+ * Create a new schema validation configuration.
+ *
+ * @return a configuration builder for schema
+ */
+ public static SchemaBuilder schema() {
+ return new SchemaBuilder();
+ }
+
+ /**
+ * Create a new schema validation configuration.
+ *
+ * @param name the name of the schema
+ * @param schema the actual precompiled schema to use
+ * @return a configuration builder for schema
+ */
+ public static SchemaBuilder schema(final String name, final Schema schema) {
+ return new SchemaBuilder().name(name).schema(schema);
+ }
+
+ /**
+ * Create a new schema validation configuration.
+ *
+ * @param name the name of the schema
+ * @return a configuration builder for schema
+ */
+ public static SchemaBuilder schema(final String name) {
+ return new SchemaBuilder().name(name);
+ }
+
+ /**
+ * Create a new schema validation configuration.
+ *
+ * @param uri the uri location of the schema
+ * @return a configuration builder for schema
+ */
+ public static SchemaBuilder schema(final URI uri) {
+ return new SchemaBuilder().schemaLocation(uri);
+ }
+
+ /**
+ * Create a new named scenario configuration.
+ *
+ * @param name the name of the scenario
+ * @return the scenario configuration builder
+ */
+ public static ScenarioBuilder scenario(final String name) {
+ return new ScenarioBuilder(name);
+ }
+
+ /**
+ * Create named report configuration.
+ *
+ * @param name the name of the report
+ * @return the report configuration builder
+ */
+ public static ReportBuilder report(final String name) {
+ return new ReportBuilder().name(name);
+ }
+
+ public Configuration build() {
+ final ResolvingConfigurationStrategy resolving = getResolvingConfigurationStrategy();
+ if (this.processor == null) {
+ this.processor = resolving.createProcessor();
+ }
+ final ContentRepository contentRepository = new ContentRepository(this.processor, this.repository,
+ resolving.createResolver(this.repository));
+ contentRepository.setSchemaFactory(resolving.createSchemaFactory());
+ contentRepository.setResolvingConfigurationStrategy(resolving);
+
+ final List list = initializeScenarios(contentRepository);
+ final Scenario fallbackScenario = initializeFallback(contentRepository);
+ final DefaultConfiguration configuration = new DefaultConfiguration(list, fallbackScenario);
+ configuration.setAdditionalParameters(this.parameters);
+ configuration.setAuthor(this.author);
+ configuration.setDate(this.date);
+ configuration.setName(this.name);
+ configuration.setContentRepository(contentRepository);
+ return (configuration);
+ }
+
+ private Scenario initializeFallback(final ContentRepository contentRepository) {
+ if (this.fallbackBuilder == null) {
+ throw new IllegalStateException("No fallback configuration specified");
+ }
+ final Result result = this.fallbackBuilder.build(contentRepository);
+ if (result.isInvalid()) {
+ throw new IllegalStateException("Invalid fallback configuration: " + String.join(",", result.getErrors()));
+ }
+ return result.getObject();
+ }
+
+ private List initializeScenarios(final ContentRepository contentRepository) {
+ if (this.scenarios.size() == 0) {
+ throw new IllegalStateException("No scenario specified");
+ }
+ return this.scenarios.stream().map(s -> {
+ final Result result = s.build(contentRepository);
+ if (result.isInvalid()) {
+ final String msg = String.join(",", result.getErrors());
+ throw new IllegalStateException(String.format("Invalid configuration for scenario %s found: %s", s.getName(), msg));
+ }
+ return result.getObject();
+ }).collect(Collectors.toList());
+ }
+
+ private ResolvingConfigurationStrategy getResolvingConfigurationStrategy() {
+ if (this.resolvingConfigurationStrategy != null) {
+ log.info("Custom resolving strategy supplied. Please take care of xml security!");
+ return this.resolvingConfigurationStrategy;
+ }
+ log.info("Using resolving strategy {}", this.resolvingMode);
+ return this.resolvingMode.getStrategy();
+ }
+
+ public ConfigurationBuilder resolvingMode(final ResolvingMode mode) {
+ this.resolvingMode = mode;
+ return this;
+ }
+
+ /**
+ * Sets a specific strategy to use for resolving artefacts for scenarios.
+ *
+ * @param strategy the strategy
+ * @return this
+ */
+ public ConfigurationBuilder resolvingStrategy(final ResolvingConfigurationStrategy strategy) {
+ this.resolvingConfigurationStrategy = strategy;
+ return this;
+ }
+
+ public ConfigurationBuilder useRepository(final URI repository) {
+ this.repository = repository;
+ return this;
+ }
}
diff --git a/src/main/java/de/kosit/validationtool/config/LoadConfiguration.java b/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
similarity index 59%
rename from src/main/java/de/kosit/validationtool/config/LoadConfiguration.java
rename to src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
index e9f52e0..589979a 100644
--- a/src/main/java/de/kosit/validationtool/config/LoadConfiguration.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
@@ -4,28 +4,36 @@ import static org.apache.commons.lang3.StringUtils.startsWith;
import java.net.MalformedURLException;
import java.net.URI;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
+import javax.xml.validation.Schema;
+
import lombok.AccessLevel;
-import lombok.AllArgsConstructor;
import lombok.Getter;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.Check;
+import de.kosit.validationtool.api.Configuration;
import de.kosit.validationtool.api.InputFactory;
+import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
import de.kosit.validationtool.impl.CollectingErrorEventHandler;
import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.ConversionService;
-import de.kosit.validationtool.impl.RelativeUriResolver;
+import de.kosit.validationtool.impl.ResolvingMode;
import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.impl.tasks.DocumentParseAction;
+import de.kosit.validationtool.impl.xml.RelativeUriResolver;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
-import de.kosit.validationtool.model.scenarios.CreateReportType;
+import de.kosit.validationtool.model.scenarios.ResourceType;
import de.kosit.validationtool.model.scenarios.ScenarioType;
import de.kosit.validationtool.model.scenarios.Scenarios;
+import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.s9api.XdmNodeKind;
@@ -36,9 +44,9 @@ import net.sf.saxon.s9api.XdmNodeKind;
*
* @author Andreas Penski
*/
-@AllArgsConstructor
+@RequiredArgsConstructor
@Slf4j
-public class LoadConfiguration extends BaseConfiguration {
+public class ConfigurationLoader {
private static final String SUPPORTED_MAJOR_VERSION = "1";
@@ -55,6 +63,12 @@ public class LoadConfiguration extends BaseConfiguration {
*/
private final URI scenarioRepository;
+ protected ResolvingMode resolvingMode = ResolvingMode.STRICT_RELATIVE;
+
+ protected ResolvingConfigurationStrategy resolvingConfigurationStrategy;
+
+ protected final Map parameters = new HashMap<>();
+
URI getScenarioRepository() {
if (this.scenarioRepository == null) {
log.info("Creating default scenario repository (alongside scenario definition)");
@@ -63,9 +77,10 @@ public class LoadConfiguration extends BaseConfiguration {
return this.scenarioRepository;
}
- private static void checkVersion(final URI scenarioDefinition) {
+ private static void checkVersion(final URI scenarioDefinition, final Processor processor) {
try {
- final Result result = DocumentParseAction.parseDocument(InputFactory.read(scenarioDefinition.toURL()));
+ final Result result = new DocumentParseAction(processor)
+ .parseDocument(InputFactory.read(scenarioDefinition.toURL()));
if (result.isValid() && !isSupportedDocument(result.getObject())) {
throw new IllegalStateException(String.format(
"Specified scenario configuration %s is not supported.%nThis version only supports definitions of '%s'",
@@ -94,54 +109,53 @@ public class LoadConfiguration extends BaseConfiguration {
}
private static Scenario createFallback(final Scenarios scenarios, final ContentRepository repository) {
- final ScenarioType t = new ScenarioType();
- t.setName("Fallback-Scenario");
- t.setMatch("count(/)<0");
- final CreateReportType reportType = new CreateReportType();
- reportType.setResource(scenarios.getNoScenarioReport().getResource());
- // always reject
- t.setAcceptMatch("count(/)<0");
- t.setCreateReport(reportType);
- final Scenario sceanrio = initialize(t, repository);
- sceanrio.setFallback(true);
- return sceanrio;
+ final ResourceType noscenarioResource = scenarios.getNoScenarioReport().getResource();
+ return new FallbackBuilder().source(noscenarioResource.getLocation()).name(noscenarioResource.getName()).build(repository)
+ .getObject();
+
}
- @Override
- protected RuntimeArtefacts buildArtefacts() {
- final ContentRepository contentRepository = buildContentRepository();
- final Scenarios def = loadScenarios();
+ public Configuration build() {
+ final ResolvingConfigurationStrategy resolving = getResolvingConfigurationStrategy();
+ final Processor processor = resolving.createProcessor();
+ final ContentRepository contentRepository = new ContentRepository(processor, getScenarioRepository(),
+ resolving.createResolver(this.getScenarioRepository()));
+ contentRepository.setSchemaFactory(resolving.createSchemaFactory());
+ contentRepository.setResolvingConfigurationStrategy(resolving);
+
+ final Scenarios def = loadScenarios(contentRepository.getScenarioSchema(), processor);
final List scenarios = initializeScenarios(def, contentRepository);
final Scenario fallbackScenario = createFallback(def, contentRepository);
- final RuntimeArtefacts runtimeArtefacts = new RuntimeArtefacts(scenarios, fallbackScenario);
- runtimeArtefacts.setAuthor(def.getAuthor());
- runtimeArtefacts.setDate(def.getDate().toString());
- runtimeArtefacts.setName(def.getName());
- return runtimeArtefacts;
+ final DefaultConfiguration configuration = new DefaultConfiguration(scenarios, fallbackScenario);
+ configuration.setAdditionalParameters(this.parameters);
+ configuration.setAuthor(def.getAuthor());
+ configuration.setDate(def.getDate().toString());
+ configuration.setName(def.getName());
+ configuration.setContentRepository(contentRepository);
+ return (configuration);
}
private static List initializeScenarios(final Scenarios def, final ContentRepository contentRepository) {
return def.getScenario().stream().map(s -> initialize(s, contentRepository)).collect(Collectors.toList());
}
- private ContentRepository buildContentRepository() {
- return new ContentRepository(getProcessor(), getScenarioRepository());
+ private ResolvingConfigurationStrategy getResolvingConfigurationStrategy() {
+ if (this.resolvingConfigurationStrategy != null) {
+ log.info("Custom resolving strategy supplied. Please take care of xml security!");
+ return this.resolvingConfigurationStrategy;
+ }
+ log.info("Using resolving strategy {}", this.resolvingMode);
+ return this.resolvingMode.getStrategy();
}
- @Override
- public ContentRepository getContentRepository() {
- return buildContentRepository();
- }
-
- private Scenarios loadScenarios() {
+ private Scenarios loadScenarios(final Schema scenarioSchema, final Processor processor) {
final ConversionService conversionService = new ConversionService();
- checkVersion(this.scenarioDefinition);
+ checkVersion(this.scenarioDefinition, processor);
log.info("Loading scenarios from {}", this.scenarioDefinition);
final CollectingErrorEventHandler handler = new CollectingErrorEventHandler();
- final Scenarios scenarios = conversionService.readXml(this.scenarioDefinition, Scenarios.class,
- getContentRepository().getScenarioSchema(), handler);
+ final Scenarios scenarios = conversionService.readXml(this.scenarioDefinition, Scenarios.class, scenarioSchema, handler);
if (!handler.hasErrors()) {
- log.info("Loading scenario content from {}", this.scenarioRepository);
+ log.info("Loading scenario content from {}", this.getScenarioRepository());
} else {
throw new IllegalStateException(
String.format("Can not load scenarios from %s due to %s", getScenarioDefinition(), handler.getErrorDescription()));
@@ -152,27 +166,23 @@ public class LoadConfiguration extends BaseConfiguration {
private static Scenario initialize(final ScenarioType def, final ContentRepository repository) {
final Scenario s = new Scenario(def);
- s.setSchema(repository.createSchema(def));
- s.setReportTransformation(repository.createReportTransformation(def));
s.setMatchExecutable(repository.createMatchExecutable(def));
+ s.setSchema(repository.createSchema(def));
+ s.setSchematronValidations(repository.createSchematronTransformations(def));
+ s.setReportTransformation(repository.createReportTransformation(def));
if (def.getAcceptMatch() != null) {
s.setAcceptExecutable(repository.createAccepptExecutable(def));
}
return s;
}
- @Override
- public String getAuthor() {
- return null;
+ public ConfigurationLoader setResolvingMode(final ResolvingMode mode) {
+ this.resolvingMode = mode;
+ return this;
}
- @Override
- public String getName() {
- return null;
- }
-
- @Override
- public String getDate() {
- return null;
+ public ConfigurationLoader addParameter(final String name, final Object value) {
+ this.parameters.put(name, value);
+ return this;
}
}
diff --git a/src/main/java/de/kosit/validationtool/config/DefaultConfiguration.java b/src/main/java/de/kosit/validationtool/config/DefaultConfiguration.java
new file mode 100644
index 0000000..1af8820
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/DefaultConfiguration.java
@@ -0,0 +1,40 @@
+package de.kosit.validationtool.config;
+
+import java.util.List;
+import java.util.Map;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.Scenario;
+
+/**
+ * Default implementation class for {@link Configuration}. This class contains all information to run a
+ * {@link de.kosit.validationtool.impl.DefaultCheck}.
+ *
+ * @author Andreas Penski
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Getter
+@Setter
+public class DefaultConfiguration implements Configuration {
+
+ private final List scenarios;
+
+ private final Scenario fallbackScenario;
+
+ private ContentRepository contentRepository;
+
+ private String name;
+
+ private String author;
+
+ private String date;
+
+ public Map additionalParameters;
+}
diff --git a/src/main/java/de/kosit/validationtool/config/FallbackBuilder.java b/src/main/java/de/kosit/validationtool/config/FallbackBuilder.java
new file mode 100644
index 0000000..5399f39
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/FallbackBuilder.java
@@ -0,0 +1,95 @@
+package de.kosit.validationtool.config;
+
+import java.net.URI;
+import java.nio.file.Path;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.Scenario;
+import de.kosit.validationtool.impl.Scenario.Transformation;
+import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.model.scenarios.CreateReportType;
+import de.kosit.validationtool.model.scenarios.ScenarioType;
+
+/**
+ * @author Andreas Penski
+ */
+public class FallbackBuilder implements Builder {
+
+ private final ReportBuilder internal = new ReportBuilder().name("fallback");
+
+ @Override
+ public Result build(final ContentRepository repository) {
+ final ScenarioType object = createObject();
+ final Result, String> build = this.internal.build(repository);
+ final Result result;
+ if (build.isValid()) {
+ object.setCreateReport(build.getObject().getLeft());
+ final Scenario s = new Scenario(object);
+ s.setFallback(true);
+ s.setReportTransformation(build.getObject().getRight());
+ result = new Result<>(s);
+ } else {
+ result = new Result<>(build.getErrors());
+ }
+ return result;
+ }
+
+ private static ScenarioType createObject() {
+ final ScenarioType t = new ScenarioType();
+ t.setName("Fallback-Scenario");
+ t.setMatch("count(/)<0");
+ // always reject
+ t.setAcceptMatch("count(/)<0");
+ return t;
+ }
+
+ /**
+ * Specifices a source for this report. This is either used to compile the report transformation or as documentation for
+ * a precompiled tranformation.
+ *
+ * @param source the source
+ * @return this
+ */
+ public FallbackBuilder source(final String source) {
+ this.internal.source(source);
+ return this;
+ }
+
+ /**
+ * Specifices a source for this report. This is either used to compile the report transformation or as documentation for
+ * a precompiled tranformation.
+ *
+ * @param source the source
+ * @return this
+ */
+ public FallbackBuilder source(final URI source) {
+ this.internal.source(source);
+ return this;
+ }
+
+ /**
+ * Specifices a source for this report. This is either used to compile the report transformation or as documentation for
+ * a precompiled tranformation.
+ *
+ * @param source the source
+ * @return this
+ */
+ public FallbackBuilder source(final Path source) {
+ this.internal.source(source);
+ return this;
+ }
+
+ /**
+ * Sets the name of the report source to a specific value.
+ *
+ * @param name the name
+ * @return this
+ */
+ public FallbackBuilder name(final String name) {
+ this.internal.name(name);
+ return this;
+ }
+
+}
diff --git a/src/main/java/de/kosit/validationtool/config/ReportBuilder.java b/src/main/java/de/kosit/validationtool/config/ReportBuilder.java
new file mode 100644
index 0000000..1c69053
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/ReportBuilder.java
@@ -0,0 +1,116 @@
+package de.kosit.validationtool.config;
+
+import static org.apache.commons.lang3.ObjectUtils.isNotEmpty;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.util.Collections;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+
+import lombok.extern.slf4j.Slf4j;
+
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.Scenario.Transformation;
+import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.model.scenarios.CreateReportType;
+import de.kosit.validationtool.model.scenarios.ResourceType;
+
+import net.sf.saxon.s9api.XsltExecutable;
+
+/**
+ * Builder style configuration for the report transformation.
+ *
+ * @author Andreas Penski
+ */
+@Slf4j
+public class ReportBuilder implements Builder> {
+
+ private static final String DEFAULT_NAME = "manually created report";
+
+ private XsltExecutable executable;
+
+ private URI source;
+
+ private String name;
+
+ @Override
+ public Result, String> build(final ContentRepository repository) {
+ if (this.executable == null && this.source == null) {
+ return createError("Must supply source location and/or executable");
+ }
+ final CreateReportType object = createObject();
+ Result, String> result;
+
+ try {
+ if (this.executable == null) {
+ this.executable = repository.createTransformation(object.getResource()).getExecutable();
+ }
+ result = new Result<>(new ImmutablePair<>(object, new Transformation(this.executable, object.getResource())));
+ } catch (final IllegalStateException e) {
+ log.error(e.getMessage(), e);
+ result = createError(
+ String.format("Can not create report configuration based on %s. Exception is %s", this.source, e.getMessage()));
+ }
+ return result;
+ }
+
+ private CreateReportType createObject() {
+ final CreateReportType o = new CreateReportType();
+ final ResourceType r = new ResourceType();
+ r.setLocation(this.source.toASCIIString());
+ r.setName(isNotEmpty(this.name) ? this.name : DEFAULT_NAME);
+ o.setResource(r);
+ return o;
+ }
+
+ private static Result, String> createError(final String msg) {
+ return new Result<>(null, Collections.singletonList(msg));
+ }
+
+ /**
+ * Specifices a source for this report. This is either used to compile the report transformation or as documentation for
+ * a precompiled tranformation.
+ *
+ * @param source the source
+ * @return this
+ */
+ public ReportBuilder source(final String source) {
+ return source(URI.create(source));
+ }
+
+ /**
+ * Specifices a source for this report. This is either used to compile the report transformation or as documentation for
+ * a precompiled tranformation.
+ *
+ * @param source the source
+ * @return this
+ */
+ public ReportBuilder source(final URI source) {
+ this.source = source;
+ return this;
+ }
+
+ /**
+ * Specifices a source for this report. This is either used to compile the report transformation or as documentation for
+ * a precompiled tranformation.
+ *
+ * @param source the source
+ * @return this
+ */
+ public ReportBuilder source(final Path source) {
+ return source(source.toUri());
+ }
+
+ /**
+ * Sets the name of the report source to a specific value.
+ *
+ * @param name the name
+ * @return this
+ */
+ public ReportBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java b/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
index 0a2a700..18f6536 100644
--- a/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
@@ -1,72 +1,270 @@
package de.kosit.validationtool.config;
-import java.net.URL;
-import java.util.Collections;
+import static org.apache.commons.lang3.ObjectUtils.isNotEmpty;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import javax.xml.validation.Schema;
-import org.w3c.dom.ls.LSResourceResolver;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
import de.kosit.validationtool.impl.ContentRepository;
-import de.kosit.validationtool.impl.ObjectFactory;
+import de.kosit.validationtool.impl.Scenario;
+import de.kosit.validationtool.impl.Scenario.Transformation;
+import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.model.scenarios.CreateReportType;
+import de.kosit.validationtool.model.scenarios.DescriptionType;
+import de.kosit.validationtool.model.scenarios.NamespaceType;
+import de.kosit.validationtool.model.scenarios.ObjectFactory;
import de.kosit.validationtool.model.scenarios.ScenarioType;
+import de.kosit.validationtool.model.scenarios.ValidateWithSchematron;
+import de.kosit.validationtool.model.scenarios.ValidateWithXmlSchema;
-import net.sf.saxon.s9api.XsltExecutable;
+import net.sf.saxon.s9api.XPathExecutable;
/**
+ * Builder for {@link Scenario} configuration.
+ *
* @author Andreas Penski
*/
-public class ScenarioBuilder {
+@RequiredArgsConstructor
+public class ScenarioBuilder implements Builder {
- private final ScenarioType scenario;
+ private static int nameCount = 0;
- private final ContentRepository contentRepository = new ContentRepository(ObjectFactory.createProcessor(), null);
+ private static final String DEFAULT_DESCRIPTION = "Dieses Scenario wurde per API erstellt";
- ScenarioBuilder(final String name) {
- this.scenario = new ScenarioType();
- this.scenario.setName(name);
+ private final Map namespaces = new HashMap<>();
+
+ private final XPathBuilder matchConfig = new XPathBuilder();
+
+ private final XPathBuilder acceptConfig = new XPathBuilder();
+
+ @Getter(AccessLevel.PACKAGE)
+ private final String name;
+
+ private SchemaBuilder schemaBuilder;
+
+ private final List schematronBuilders = new ArrayList<>();
+
+ private ReportBuilder reportBuilder;
+
+ private String description;
+
+ @Override
+ public Result build(final ContentRepository repository) {
+ final List errors = new ArrayList<>();
+ final Scenario scenario = new Scenario(createType());
+ buildMatch(repository, errors, scenario);
+ buildSchema(repository, errors, scenario);
+ buildSchematron(repository, errors, scenario);
+ buildReport(repository, errors, scenario);
+ buildAccept(repository, errors, scenario);
+ buildNamespaces(scenario);
+ return new Result<>(scenario, errors);
}
- public ScenarioBuilder matches(final String xpath) {
- return matches(xpath, Collections.emptyMap());
- }
-
- private ScenarioBuilder matches(final String xpath, final Map namespaces) {
- // final XPathExecutable matchExecutable = this.contentRepository.createXPath(xpath, namespaces);
- // this.scenario.setMatchExecutable(matchExecutable);
- // this.scenario.setMatch(xpath);
- // if (namespaces != null) {
- // this.scenario.getNamespace().addAll(namespaces.entrySet().stream().map(e -> {
- // NamespaceType t = new NamespaceType();
- // t.setPrefix(e.getKey());
- // t.setValue(e.getValue());
- // return t;
- // }).collect(Collectors.toList()));
- // } else {
- // this.scenario.getNamespace().clear();
- // }
+ /**
+ * Add a preconfiguration {@link XPathExecutable} to match the scenario
+ *
+ * @param executable the xpath executable
+ * @return this
+ */
+ public ScenarioBuilder match(final XPathExecutable executable) {
+ this.matchConfig.setExecutable(executable);
return this;
}
- public ScenarioBuilder schemaValidation(final Schema schema) {
+ /**
+ * Add an xpath expression to match the scenario. You can leverage declared namespaces.
+ *
+ * @param xpath the expression
+ * @return this
+ */
+ public ScenarioBuilder match(final String xpath) {
+ this.matchConfig.setXpath(xpath);
return this;
}
- public ScenarioBuilder schemaValidation(final URL url) {
- return schemalidation(url, null);
- }
-
- private ScenarioBuilder schemalidation(final URL url, final LSResourceResolver resolver) {
+ /**
+ * Declare a namespace to use for match and accept configurations.
+ *
+ * @param prefix the prefix to use
+ * @param uri the uri of this namespace
+ * @return this
+ */
+ public ScenarioBuilder declareNamespace(final String prefix, final String uri) {
+ this.namespaces.put(prefix, uri);
return this;
}
- public ScenarioBuilder addSchematronValidation(final XsltExecutable executable) {
+ /**
+ * Add a preconfiguration {@link XPathExecutable} to compute acceptance for the scenario
+ *
+ * @param executable the xpath executable
+ * @return this
+ */
+ public ScenarioBuilder acceptWith(final XPathExecutable executable) {
+ this.acceptConfig.setExecutable(executable);
return this;
}
- public ScenarioBuilder withReportGenerator(final XsltExecutable executable) {
+ /**
+ * Add an xpath expression to compute acceptance for the scenario. You can leverage declared namespaces.
+ *
+ * @param acceptXpath the xpath expresison
+ * @return this
+ */
+ public ScenarioBuilder acceptWith(final String acceptXpath) {
+ this.acceptConfig.setXpath(acceptXpath);
return this;
}
+ /**
+ * Add a schematron validation configuration for this scenario.
+ *
+ * @param schematron the schematron configuration
+ * @return this
+ */
+ public ScenarioBuilder validate(final SchematronBuilder schematron) {
+ if (schematron != null) {
+ this.schematronBuilders.add(schematron);
+ }
+ return this;
+ }
+
+ /**
+ * Validate matching {@link de.kosit.validationtool.api.Input Inputs} with the specified schema configuration.
+ *
+ * @param schema the schema configuration
+ * @return this
+ */
+ public ScenarioBuilder validate(final SchemaBuilder schema) {
+ this.schemaBuilder = schema;
+ return this;
+ }
+
+ /**
+ * Add description for this scenario. This is part of the
+ * {@link de.kosit.validationtool.model.reportInput.CreateReportInput} configuration and can be used while creating the
+ * report
+ *
+ * @param description the description
+ * @return this
+ */
+ public ScenarioBuilder description(final String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Add a configuration for generating the final report for the {@link de.kosit.validationtool.api.Input}.
+ *
+ * @param reportBuilder the report configuration
+ * @return this
+ */
+ public ScenarioBuilder with(final ReportBuilder reportBuilder) {
+ this.reportBuilder = reportBuilder;
+ return this;
+ }
+
+ private static String generateName() {
+ return "manually created scenario " + nameCount++;
+ }
+
+ private void buildNamespaces(final Scenario scenario) {
+ this.namespaces.putAll(this.acceptConfig.getNamespaces());
+ this.namespaces.putAll(this.matchConfig.getNamespaces());
+ final List all = this.namespaces.entrySet().stream().map(e -> {
+ final NamespaceType n = new NamespaceType();
+ n.setPrefix(e.getKey());
+ n.setValue(e.getValue());
+ return n;
+ }).collect(Collectors.toList());
+ scenario.getConfiguration().getNamespace().addAll(all);
+ }
+
+ private void buildMatch(final ContentRepository repository, final List errors, final Scenario scenario) {
+ this.matchConfig.setNamespaces(this.namespaces);
+ final Result result = this.matchConfig.build(repository);
+ if (result.isValid()) {
+ scenario.setMatchExecutable(result.getObject());
+ scenario.getConfiguration().setMatch(this.matchConfig.getXPath());
+ this.namespaces.putAll(this.matchConfig.getNamespaces());
+ } else {
+ errors.addAll(result.getErrors());
+ }
+ }
+
+ private void buildAccept(final ContentRepository repository, final List errors, final Scenario scenario) {
+ this.acceptConfig.setNamespaces(this.namespaces);
+ final Result result = this.acceptConfig.build(repository);
+ if (result.isValid()) {
+ scenario.setAcceptExecutable(result.getObject());
+ scenario.getConfiguration().setAcceptMatch(this.acceptConfig.getXPath());
+ this.namespaces.putAll(this.acceptConfig.getNamespaces());
+ } else {
+ errors.addAll(result.getErrors());
+ }
+ }
+
+ private void buildReport(final ContentRepository repository, final List errors, final Scenario scenario) {
+ if (this.reportBuilder == null) {
+ errors.add("Must supply report configuration");
+ } else {
+ final Result, String> result = this.reportBuilder.build(repository);
+ if (result.isValid()) {
+ scenario.setReportTransformation(result.getObject().getRight());
+ scenario.getConfiguration().setCreateReport(result.getObject().getLeft());
+ } else {
+ errors.addAll(result.getErrors());
+ }
+ }
+ }
+
+ private void buildSchematron(final ContentRepository repository, final List errors, final Scenario scenario) {
+ this.schematronBuilders.forEach(e -> {
+ final Result, String> result = e.build(repository);
+ if (result.isValid()) {
+ scenario.getConfiguration().getValidateWithSchematron().add(result.getObject().getLeft());
+ scenario.getSchematronValidations().add(result.getObject().getRight());
+ } else {
+ errors.addAll(result.getErrors());
+ }
+ });
+ }
+
+ private void buildSchema(final ContentRepository repository, final List errors, final Scenario scenario) {
+ if (this.schemaBuilder == null) {
+ errors.add("Must supply schema for validation");
+ } else {
+ final Result, String> result = this.schemaBuilder.build(repository);
+ if (result.isValid()) {
+ scenario.setSchema(result.getObject().getRight());
+ scenario.getConfiguration().setValidateWithXmlSchema(result.getObject().getLeft());
+ } else {
+ errors.addAll(result.getErrors());
+ }
+ }
+ }
+
+ private ScenarioType createType() {
+ final ScenarioType type = new ScenarioType();
+ type.setName(isNotEmpty(this.name) ? this.name : generateName());
+ final DescriptionType desc = new DescriptionType();
+ desc.getPOrOlOrUl()
+ .add(new ObjectFactory().createDescriptionTypeP(StringUtils.defaultIfBlank(this.description, DEFAULT_DESCRIPTION)));
+ type.setDescription(desc);
+ return type;
+ }
+
}
diff --git a/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java b/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java
new file mode 100644
index 0000000..49dd722
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java
@@ -0,0 +1,121 @@
+package de.kosit.validationtool.config;
+
+import static org.apache.commons.lang3.ObjectUtils.isNotEmpty;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.util.Collections;
+
+import javax.xml.validation.Schema;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+
+import lombok.extern.slf4j.Slf4j;
+
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.model.scenarios.ResourceType;
+import de.kosit.validationtool.model.scenarios.ValidateWithXmlSchema;
+
+/**
+ * Builder for Schema validation configuration.
+ *
+ * @author Andreas Penski
+ */
+@Slf4j
+public class SchemaBuilder implements Builder> {
+
+ private static final String DEFAULT_NAME = "manually configured";
+
+ private Schema schema;
+
+ private URI schemaLocation;
+
+ private String name;
+
+ @Override
+ public Result, String> build(final ContentRepository repository) {
+ if (this.schema == null && this.schemaLocation == null) {
+ return createError("Must supply schema location and/or schema");
+ }
+ Result, String> result;
+ try {
+ if (this.schema == null) {
+ this.schema = repository.createSchema(this.schemaLocation);
+ }
+ result = new Result<>(new ImmutablePair<>(createObject(), this.schema));
+ } catch (final IllegalStateException e) {
+ log.error(e.getMessage(), e);
+ result = createError(String.format("Can not create schema based %s. Exception is %s", this.schemaLocation, e.getMessage()));
+ }
+
+ return result;
+ }
+
+ private ValidateWithXmlSchema createObject() {
+ final ValidateWithXmlSchema o = new ValidateWithXmlSchema();
+ final ResourceType r = new ResourceType();
+ r.setName(isNotEmpty(this.name) ? this.name : DEFAULT_NAME);
+ r.setLocation(this.schemaLocation.toASCIIString());
+ o.getResource().add(r);
+ return o;
+ }
+
+ private static Result, String> createError(final String msg) {
+ return new Result<>(null, Collections.singletonList(msg));
+ }
+
+ /**
+ * Set a specific precompiled schema to check.
+ *
+ * @param schema the {@link Schema}
+ * @return this
+ */
+ public SchemaBuilder schema(final Schema schema) {
+ this.schema = schema;
+ return this;
+ }
+
+ /**
+ * Set a specific schema location either to compile or to document the precompiled one .
+ *
+ * @param schemaLocation the schema location as uri
+ * @return this
+ */
+ public SchemaBuilder schemaLocation(final URI schemaLocation) {
+ this.schemaLocation = schemaLocation;
+ return this;
+ }
+
+ /**
+ * Set a specific schema location either to compile or to document the precompiled one .
+ *
+ * @param schemaLocation the schema location as uri
+ * @return this
+ */
+ public SchemaBuilder schemaLocation(final String schemaLocation) {
+ return schemaLocation(URI.create(schemaLocation));
+ }
+
+ /**
+ * Set a specific schema location either to compile or to document the precompiled one .
+ *
+ * @param schemaLocation the schema location as uri
+ * @return this
+ */
+ public SchemaBuilder schemaLocation(final Path schemaLocation) {
+ return schemaLocation(schemaLocation.toUri());
+ }
+
+ /**
+ * Set a specific name to identify this schema.
+ *
+ * @param name the name of the schema
+ * @return this
+ */
+ public SchemaBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/config/SchematronBuilder.java b/src/main/java/de/kosit/validationtool/config/SchematronBuilder.java
new file mode 100644
index 0000000..ad74f43
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/SchematronBuilder.java
@@ -0,0 +1,116 @@
+package de.kosit.validationtool.config;
+
+import static org.apache.commons.lang3.ObjectUtils.isNotEmpty;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.util.Collections;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+
+import lombok.extern.slf4j.Slf4j;
+
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.Scenario.Transformation;
+import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.model.scenarios.ResourceType;
+import de.kosit.validationtool.model.scenarios.ValidateWithSchematron;
+
+import net.sf.saxon.s9api.XsltExecutable;
+
+/**
+ * Builder for schematron validation configuration.
+ *
+ * @author Andreas Penski
+ */
+@Slf4j
+public class SchematronBuilder implements Builder> {
+
+ private static final String DEFAULT_NAME = "manually configured";
+
+ private XsltExecutable executable;
+
+ private URI source;
+
+ private String name;
+
+ @Override
+ public Result, String> build(final ContentRepository repository) {
+ if (this.executable == null && this.source == null) {
+ return createError("Must supply source location and/or executable");
+ }
+ final ValidateWithSchematron object = createObject();
+ Result, String> result;
+
+ try {
+ if (this.executable == null) {
+ this.executable = repository.createSchematronTransformation(object).getExecutable();
+ }
+ result = new Result<>(new ImmutablePair<>(object, new Transformation(this.executable, object.getResource())));
+ } catch (final IllegalStateException e) {
+ log.error(e.getMessage(), e);
+ result = createError(
+ String.format("Can not create schematron configuration based on %s. Exception is %s", this.source, e.getMessage()));
+ }
+ return result;
+ }
+
+ private ValidateWithSchematron createObject() {
+ final ValidateWithSchematron o = new ValidateWithSchematron();
+ final ResourceType r = new ResourceType();
+ r.setLocation(this.source.toASCIIString());
+ r.setName(isNotEmpty(this.name) ? this.name : DEFAULT_NAME);
+ o.setResource(r);
+ return o;
+ }
+
+ private static Result, String> createError(final String msg) {
+ return new Result<>(null, Collections.singletonList(msg));
+ }
+
+ /**
+ * Specifices a source for this schematron validation. This is either used to compile the schematron transformation or
+ * as documentation for a precompiled tranformation.
+ *
+ * @param source the source
+ * @return this
+ */
+ public SchematronBuilder source(final String source) {
+ return source(URI.create(source));
+ }
+
+ /**
+ * Specifices a source for this schematron validation. This is either used to compile the schematron transformation or
+ * as documentation for a precompiled tranformation.
+ *
+ * @param source the source
+ * @return this
+ */
+ public SchematronBuilder source(final URI source) {
+ this.source = source;
+ return this;
+ }
+
+ /**
+ * Specifices a source for this schematron validation. This is either used to compile the schematron transformation or
+ * as documentation for a precompiled tranformation.
+ *
+ * @param source the source
+ * @return this
+ */
+ public SchematronBuilder source(final Path source) {
+ return source(source.toUri());
+ }
+
+ /**
+ * Sets the name of the schematron source to a specific value.
+ *
+ * @param name the name
+ * @return this
+ */
+ public SchematronBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
new file mode 100644
index 0000000..464d745
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
@@ -0,0 +1,83 @@
+package de.kosit.validationtool.config;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.stream.StreamSupport;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.model.Result;
+
+import net.sf.saxon.s9api.XPathExecutable;
+
+/**
+ * Internal class to represent xpath configuration.
+ *
+ * @author Andreas Penski
+ */
+@Data
+class XPathBuilder implements Builder {
+
+ private static final String[] IGNORED_PREFIXES = new String[] { "xsd" };
+
+ private String xpath;
+
+ private XPathExecutable executable;
+
+ @Setter(AccessLevel.PACKAGE)
+ @Getter(AccessLevel.PACKAGE)
+ private Map namespaces;
+
+ /**
+ * Returns the xpath expression.
+ *
+ * @return xpath expression
+ */
+ public String getXPath() {
+ return this.xpath == null && this.executable != null ? this.executable.getUnderlyingExpression().getInternalExpression().toString()
+ : this.xpath;
+
+ }
+
+ @Override
+ public Result build(final ContentRepository repository) {
+ if (this.executable == null && this.xpath == null) {
+ return createError("No configuration for xpath expression found");
+ }
+ if (this.executable == null) {
+ this.executable = repository.createXPath(this.xpath, this.namespaces);
+ } else {
+ this.xpath = extractExpression();
+ this.namespaces = extractNamespaces();
+ }
+ return new Result<>(this.executable);
+ }
+
+ private Map extractNamespaces() {
+ final Map ns = new HashMap<>();
+ final Iterator iterator = this.executable.getUnderlyingExpression().getInternalExpression().getRetainedStaticContext()
+ .iteratePrefixes();
+ final Iterable iterable = () -> iterator;
+ StreamSupport.stream(iterable.spliterator(), false).filter(e -> !ArrayUtils.contains(IGNORED_PREFIXES, e)).forEach(e -> {
+ ns.put(e,
+ this.executable.getUnderlyingExpression().getInternalExpression().getRetainedStaticContext().getURIForPrefix(e, false));
+ });
+ return ns;
+ }
+
+ private String extractExpression() {
+ return this.executable.getUnderlyingExpression().getInternalExpression().toString();
+ }
+
+ private static Result createError(final String msg) {
+ return new Result<>(null, Collections.singletonList(msg));
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/daemon/Daemon.java b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
new file mode 100644
index 0000000..dc4ca80
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
@@ -0,0 +1,114 @@
+package de.kosit.validationtool.daemon;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.util.concurrent.Executors;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Document;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpServer;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.impl.DefaultCheck;
+import de.kosit.validationtool.impl.ObjectFactory;
+
+/**
+ * HTTP-Daemon für die Bereitstellung der Prüf-Funktionalität via http.
+ *
+ * @author Roula Antoun
+ */
+@RequiredArgsConstructor
+@Setter
+@Getter
+@Slf4j
+public class Daemon {
+
+ private final String hostName;
+
+ private final int port;
+
+ private final int threadCount;
+
+ /**
+ * Methode, die die Antwort als String-Text schreibt
+ *
+ * @param httpExchange um den Antwort Body zu erhalten
+ * @param rCode der Code-Status
+ * @param response die String antwort, die ich anzeigen möchte
+ */
+ static void writeError(final HttpExchange httpExchange, final int rCode, final String response) throws IOException {
+ httpExchange.sendResponseHeaders(rCode, response.length());
+ final OutputStream os = httpExchange.getResponseBody();
+ os.write(response.getBytes());
+ os.close();
+ }
+
+ /**
+ * Methode, die die Antwort als String-Text schreibt
+ *
+ * @param httpExchange um den Antwort Body zu erhalten
+ * @param doc der Report
+ */
+ static void writeOutputstreamArray(final HttpExchange httpExchange, final Document doc)
+ throws IOException, TransformerException {
+ final byte[] bytes = serialize(doc);
+ final OutputStream os = httpExchange.getResponseBody();
+ httpExchange.getResponseHeaders().add("Content-Type", "application/xml");
+ httpExchange.sendResponseHeaders(200, bytes.length);
+ os.write(bytes);
+ os.close();
+ log.debug("Xml File erzeugen ist Fertig ");
+ }
+
+ /**
+ * Methode zum Serialisieren des Dokuments.
+ *
+ * @param report Vom Typ Dokument, aka Report .
+ */
+ static byte[] serialize(final Document report) throws TransformerException {
+
+ try ( final ByteArrayOutputStream bArrayOS = new ByteArrayOutputStream() ) {
+ final DOMSource source = new DOMSource(report);
+ final StreamResult streamResult = new StreamResult(bArrayOS);
+ final Transformer transformer = ObjectFactory.createTransformer(true);
+ transformer.transform(source, streamResult);
+ return bArrayOS.toByteArray();
+ } catch (final IOException e) {
+ log.error("Report {}", e.getMessage(), e);
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Methode zum Starten des Servers
+ *
+ * @param config the configuration to use
+ */
+ public void startServer(final Configuration config) {
+ HttpServer server = null;
+ try {
+ server = HttpServer.create(new InetSocketAddress(this.hostName, this.port), 0);
+ final DefaultCheck check = new DefaultCheck(config);
+ server.createContext("/", new HttpServerHandler(check));
+ server.createContext("/health", new HealthHandler(config));
+ server.setExecutor(Executors.newFixedThreadPool(this.threadCount));
+ server.start();
+ log.info("Server unter Port {} ist erfolgreich gestartet", this.port);
+ } catch (final IOException e) {
+ log.error("Fehler beim HttpServer erstellen: {}", e.getMessage(), e);
+ }
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/daemon/HealthHandler.java b/src/main/java/de/kosit/validationtool/daemon/HealthHandler.java
new file mode 100644
index 0000000..28b1ff7
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/daemon/HealthHandler.java
@@ -0,0 +1,141 @@
+package de.kosit.validationtool.daemon;
+
+import java.io.IOException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.transform.TransformerException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+import lombok.extern.slf4j.Slf4j;
+
+import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.impl.ObjectFactory;
+
+/**
+ * Wir benötigen einen Handler, der zur Verarbeitung von HTTP-Anforderungen aufgerufen wird , und hier für Verarbeitung
+ * das GET Request um Health-Endpunkt zu erstellen. Die Klasse HealthHandler implementiert diese Schnittstelle
+ */
+@Slf4j
+class HealthHandler implements HttpHandler {
+
+ /**
+ * Klasse zur Erzeugung Health Xml , die optiamle Status.
+ *
+ * @author Roula Antoun
+ */
+ @Slf4j
+ static class Health {
+
+ private final long freeMemory;
+
+ private final long maxMemory;
+
+ private final long totalMemory;
+
+ private final Configuration config;
+
+ Health(final Configuration config) {
+
+ final Runtime runtime = Runtime.getRuntime();
+ this.freeMemory = runtime.freeMemory();
+ this.maxMemory = runtime.maxMemory();
+ this.totalMemory = runtime.totalMemory();
+ this.config = config;
+ }
+
+ /**
+ * Methode, die schreibt das Health Xml für optimale Status
+ *
+ */
+ Document writeHealthXml() {
+ final DocumentBuilder dBuilder = ObjectFactory.createDocumentBuilder(false);
+ final Document doc = dBuilder.newDocument();
+ final Element rootElement = doc.createElementNS("https://localhost:8080/Health", "Health");
+ doc.appendChild(rootElement);
+ rootElement.appendChild(getMemory(doc, this.freeMemory, this.maxMemory, this.totalMemory));
+ rootElement.appendChild(getState(doc));
+ rootElement.appendChild(getScenario(doc, this.config));
+ return doc;
+ }
+
+ /**
+ * Methode, die schreibt das System Status Node im Xml File
+ *
+ * @param doc Vom Typ Dokument.
+ *
+ */
+ private static Node getState(final Document doc) {
+ final Element state = doc.createElement("state");
+ state.setAttribute("indicator", "OK");
+ final Element stateNode = doc.createElement("message");
+ stateNode.appendChild(doc.createTextNode("System is up and running normally"));
+ state.appendChild(stateNode);
+ return state;
+ }
+
+ /**
+ * Methode, die schreibt das Scnarios Information Node im Xml File
+ *
+ * @param doc Vom Typ Dokument .
+ * @param config Vom Typ {@link Configuration} das verwendete scenario.
+ *
+ */
+ private static Node getScenario(final Document doc, final Configuration config) {
+ final Element scenario = doc.createElement("scenario");
+ final Element scenarioNameNode = doc.createElement("name");
+ scenarioNameNode.appendChild(doc.createTextNode(config.getName()));
+ scenario.appendChild(scenarioNameNode);
+ return scenario;
+ }
+
+ /**
+ * Methode, die schreibt das Scnarios Information Node im Xml File
+ *
+ * @param doc Vom Typ Dokument .
+ * @param freeMemory Vom Typ long , der freier Speicher.
+ * @param maxMemory Vom Typ long , der maximaler Speicher
+ * @param totalMemory Vom Typ long , der Gesamte speicher.
+ *
+ */
+ private static Node getMemory(final Document doc, final long freeMemory, final long maxMemory, final long totalMemory) {
+ final Element memory = doc.createElement("memoryState");
+ final String freeM = Long.toString(freeMemory);
+ final Element freeMNode = doc.createElement("freeMemory");
+ freeMNode.appendChild(doc.createTextNode(freeM));
+ memory.appendChild(freeMNode);
+ final String maxM = Long.toString(maxMemory);
+ final Element maxMNode = doc.createElement("maxMemory");
+ maxMNode.appendChild(doc.createTextNode(maxM));
+ memory.appendChild(maxMNode);
+ final String totalM = Long.toString(totalMemory);
+ final Element totalMNode = doc.createElement("totalMemory");
+ totalMNode.appendChild(doc.createTextNode(totalM));
+ memory.appendChild(totalMNode);
+ return memory;
+ }
+ }
+
+ private final Configuration scenarios;
+
+ HealthHandler(final Configuration config) {
+ this.scenarios = config;
+ }
+
+ @Override
+ public void handle(final HttpExchange httpExchange) throws IOException {
+ final Health health = new Health(this.scenarios);
+ final Document doc = health.writeHealthXml();
+ try {
+ Daemon.writeOutputstreamArray(httpExchange, doc);
+ } catch (final TransformerException e) {
+ Daemon.writeError(httpExchange, 500, e.getMessage());
+ log.error("Fehler beim Erzeugen der Status-Information", e);
+ }
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/daemon/HttpServerHandler.java b/src/main/java/de/kosit/validationtool/daemon/HttpServerHandler.java
new file mode 100644
index 0000000..126f7e4
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/daemon/HttpServerHandler.java
@@ -0,0 +1,61 @@
+package de.kosit.validationtool.daemon;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+import lombok.extern.slf4j.Slf4j;
+
+import de.kosit.validationtool.api.Check;
+import de.kosit.validationtool.api.InputFactory;
+import de.kosit.validationtool.impl.input.SourceInput;
+
+/**
+ * Wir benötigen einen Handler, der zur Verarbeitung von HTTP-Anforderungen aufgerufen wird um hier die Verarbeitung des
+ * POST Request zu realisieren.
+ */
+@Slf4j
+class HttpServerHandler implements HttpHandler {
+
+ private static final AtomicLong counter = new AtomicLong(0);
+
+ private final Check implemenation;
+
+ HttpServerHandler(final Check check) {
+ this.implemenation = check;
+ }
+
+ /**
+ * Methode, die eine gegebene Anforderung verarbeitet und eine entsprechende Antwort generiert
+ *
+ * @param httpExchange kapselt eine empfangene HTTP-Anforderung und eine Antwort, die in einem Exchange generiert werden
+ * soll.
+ */
+ @Override
+ public void handle(final HttpExchange httpExchange) throws IOException {
+ try {
+ log.debug("Incoming request");
+ final String requestMethod = httpExchange.getRequestMethod();
+ if (requestMethod.equals("POST")) {
+ final InputStream inputStream = httpExchange.getRequestBody();
+
+ if (inputStream.available() > 0) {
+ final SourceInput serverInput = (SourceInput) InputFactory.read(inputStream, "Prüfling" + counter.incrementAndGet());
+ Daemon.writeOutputstreamArray(httpExchange, this.implemenation.check(serverInput));
+ } else {
+ Daemon.writeError(httpExchange, 400, "XML-Inhalt erforderlich!");
+ }
+
+ } else {
+ Daemon.writeError(httpExchange, 405, "Es ist nur die POST-Methode erlaubt!");
+ }
+ } catch (final Exception e) {
+ Daemon.writeError(httpExchange, 500, "Interner Fehler bei der Verarbeitung des Requests: " + e.getMessage());
+ log.error("Es ist ein Fehler aufgetreten. Das Dokument kann nicht geprüft werden", e);
+ }
+ }
+
+}
diff --git a/src/main/java/de/kosit/validationtool/impl/ClassPathResourceResolver.java b/src/main/java/de/kosit/validationtool/impl/ClassPathResourceResolver.java
index 50d96d6..46ff31f 100644
--- a/src/main/java/de/kosit/validationtool/impl/ClassPathResourceResolver.java
+++ b/src/main/java/de/kosit/validationtool/impl/ClassPathResourceResolver.java
@@ -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.
*
diff --git a/src/main/java/de/kosit/validationtool/impl/ContentRepository.java b/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
index 4e56e4a..139e776 100644
--- a/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
+++ b/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
@@ -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 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 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());
+ }
}
diff --git a/src/main/java/de/kosit/validationtool/impl/ConversionService.java b/src/main/java/de/kosit/validationtool/impl/ConversionService.java
index 2bc4185..0fc6cd0 100644
--- a/src/main/java/de/kosit/validationtool/impl/ConversionService.java
+++ b/src/main/java/de/kosit/validationtool/impl/ConversionService.java
@@ -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 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 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 readDocument(final Source source, final Class type) {
try {
diff --git a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java
index 2d9fa78..b337854 100644
--- a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java
+++ b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java
@@ -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 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) {
diff --git a/src/main/java/de/kosit/validationtool/impl/HtmlExtractor.java b/src/main/java/de/kosit/validationtool/impl/HtmlExtractor.java
index 59bdc38..bde98a1 100644
--- a/src/main/java/de/kosit/validationtool/impl/HtmlExtractor.java
+++ b/src/main/java/de/kosit/validationtool/impl/HtmlExtractor.java
@@ -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 extract(XdmNode xdmSource) {
+ public List 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 ns = new HashMap<>();
+ if (this.executable == null) {
+ final Map 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 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 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 extractAsElement(final XdmNode node) {
diff --git a/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java b/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
index 76b66ee..8c96eae 100644
--- a/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
+++ b/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
@@ -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;
- }
}
diff --git a/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java b/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java
index 618c74a..885674b 100644
--- a/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java
+++ b/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java
@@ -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");
- }
}
diff --git a/src/main/java/de/kosit/validationtool/impl/Scenario.java b/src/main/java/de/kosit/validationtool/impl/Scenario.java
index 159fa87..0e0ef5c 100644
--- a/src/main/java/de/kosit/validationtool/impl/Scenario.java
+++ b/src/main/java/de/kosit/validationtool/impl/Scenario.java
@@ -22,7 +22,6 @@ import net.sf.saxon.s9api.XsltExecutable;
* @author Andreas Penski
*/
@RequiredArgsConstructor
-
@Setter
@Getter
public class Scenario {
diff --git a/src/main/java/de/kosit/validationtool/impl/ScenarioRepository.java b/src/main/java/de/kosit/validationtool/impl/ScenarioRepository.java
index de5b7de..a8ddc26 100644
--- a/src/main/java/de/kosit/validationtool/impl/ScenarioRepository.java
+++ b/src/main/java/de/kosit/validationtool/impl/ScenarioRepository.java
@@ -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());
}
diff --git a/src/main/java/de/kosit/validationtool/impl/CreateDocumentIdentificationAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/CreateDocumentIdentificationAction.java
similarity index 82%
rename from src/main/java/de/kosit/validationtool/impl/CreateDocumentIdentificationAction.java
rename to src/main/java/de/kosit/validationtool/impl/tasks/CreateDocumentIdentificationAction.java
index d0b87f0..3608b5c 100644
--- a/src/main/java/de/kosit/validationtool/impl/CreateDocumentIdentificationAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/CreateDocumentIdentificationAction.java
@@ -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) {
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
index cfd9ad2..444aeec 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
@@ -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 errors) throws SaxonApiException, SAXException {
- final BuildingContentHandler contentHandler = ObjectFactory.createProcessor().newDocumentBuilder().newBuildingContentHandler();
+ private XdmNode createErrorInformation(final Collection 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());
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/DocumentParseAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/DocumentParseAction.java
index 0a166f5..c5559a7 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/DocumentParseAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/DocumentParseAction.java
@@ -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 parseDocument(final Input content) {
+ public Result parseDocument(final Input content) {
if (content == null) {
throw new IllegalArgumentException("Input may not be null");
}
Result 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());
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/SchemaValidationAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/SchemaValidationAction.java
index 89ddd7c..a2396c8 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/SchemaValidationAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/SchemaValidationAction.java
@@ -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;
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java
index 377e195..8f52cfe 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/SchematronValidationAction.java
@@ -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) {
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java
new file mode 100644
index 0000000..fe8c1e3
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java
@@ -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;
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/impl/RelativeUriResolver.java b/src/main/java/de/kosit/validationtool/impl/xml/RelativeUriResolver.java
similarity index 89%
rename from src/main/java/de/kosit/validationtool/impl/RelativeUriResolver.java
rename to src/main/java/de/kosit/validationtool/impl/xml/RelativeUriResolver.java
index e57a05e..1f1d4c2 100644
--- a/src/main/java/de/kosit/validationtool/impl/RelativeUriResolver.java
+++ b/src/main/java/de/kosit/validationtool/impl/xml/RelativeUriResolver.java
@@ -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 {
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java
new file mode 100644
index 0000000..35c19b1
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java
@@ -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;
+ }
+
+}
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
new file mode 100644
index 0000000..66824d6
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
@@ -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;
+
+ }
+
+}
diff --git a/src/test/java/de/kosit/validationtool/cmd/CheckAssertionActionTest.java b/src/test/java/de/kosit/validationtool/cmd/CheckAssertionActionTest.java
index 6e24b3c..a170d84 100644
--- a/src/test/java/de/kosit/validationtool/cmd/CheckAssertionActionTest.java
+++ b/src/test/java/de/kosit/validationtool/cmd/CheckAssertionActionTest.java
@@ -31,7 +31,7 @@ import org.junit.Test;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.cmd.assertions.Assertions;
import de.kosit.validationtool.impl.Helper;
-import de.kosit.validationtool.impl.ObjectFactory;
+import de.kosit.validationtool.impl.TestObjectFactory;
import de.kosit.validationtool.impl.tasks.CheckAction;
import de.kosit.validationtool.model.reportInput.CreateReportInput;
@@ -52,15 +52,15 @@ public class CheckAssertionActionTest {
@Before
public void setup() throws IOException {
- commandLine = new CommandLine();
- commandLine.activate();
+ this.commandLine = new CommandLine();
+ this.commandLine.activate();
}
@Test
public void testEmptyInput() {
- CheckAssertionAction a = new CheckAssertionAction(new Assertions(), ObjectFactory.createProcessor());
+ final CheckAssertionAction a = new CheckAssertionAction(new Assertions(), TestObjectFactory.createProcessor());
a.check(new CheckAction.Bag(InputFactory.read(SAMPLE), new CreateReportInput()));
- assertThat(commandLine.getErrorOutput()).contains("Can not find assertions for");
+ assertThat(this.commandLine.getErrorOutput()).contains("Can not find assertions for");
}
@Test
@@ -69,9 +69,9 @@ public class CheckAssertionActionTest {
bag.setReport(Helper.load(SAMPLE_REPORT));
final Assertions assertions = Helper.load(SAMPLE_ASSERTIONS, Assertions.class);
- CheckAssertionAction a = new CheckAssertionAction(assertions, ObjectFactory.createProcessor());
+ final CheckAssertionAction a = new CheckAssertionAction(assertions, TestObjectFactory.createProcessor());
a.check(bag);
- assertThat(commandLine.getErrorOutput()).contains("Assertion mismatch");
+ assertThat(this.commandLine.getErrorOutput()).contains("Assertion mismatch");
}
}
diff --git a/src/test/java/de/kosit/validationtool/cmd/CommandlineApplicationTest.java b/src/test/java/de/kosit/validationtool/cmd/CommandlineApplicationTest.java
index 1207a1a..c692172 100644
--- a/src/test/java/de/kosit/validationtool/cmd/CommandlineApplicationTest.java
+++ b/src/test/java/de/kosit/validationtool/cmd/CommandlineApplicationTest.java
@@ -105,12 +105,13 @@ public class CommandlineApplicationTest {
final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), Paths.get(Simple.NOT_EXISTING).toString() };
CommandLineApplication.mainProgram(args);
assertThat(this.commandLine.getErrorOutput()).isNotEmpty();
- assertThat(this.commandLine.getErrorOutput()).contains("Can not load schema from sources");
+ assertThat(this.commandLine.getErrorOutput()).contains("Can not resolve");
}
@Test
public void testNotExistingTestTarget() {
- final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-r", Paths.get(Simple.REPOSITORY).toString(),
+ final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-r",
+ Paths.get(Simple.REPOSITORY_URI).toString(),
Paths.get(Simple.NOT_EXISTING).toString() };
CommandLineApplication.mainProgram(args);
assertThat(this.commandLine.getErrorOutput()).isNotEmpty();
@@ -119,7 +120,8 @@ public class CommandlineApplicationTest {
@Test
public void testValidMinimalConfiguration() {
- final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-r", Paths.get(Simple.REPOSITORY).toString(),
+ final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-r",
+ Paths.get(Simple.REPOSITORY_URI).toString(),
Paths.get(Simple.SIMPLE_VALID).toString() };
CommandLineApplication.mainProgram(args);
assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT);
@@ -128,7 +130,7 @@ public class CommandlineApplicationTest {
@Test
public void testValidMultipleInput() {
final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-o", this.output.toString(), "-r",
- Paths.get(Simple.REPOSITORY).toString(), Paths.get(Simple.SIMPLE_VALID).toString(), Paths.get(Simple.FOO).toString() };
+ Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.SIMPLE_VALID).toString(), Paths.get(Simple.FOO).toString() };
CommandLineApplication.mainProgram(args);
assertThat(this.commandLine.getErrorOutput()).contains("Processing 2 object(s) completed");
}
@@ -136,7 +138,7 @@ public class CommandlineApplicationTest {
@Test
public void testValidDirectoryInput() {
final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-o", this.output.toString(), "-r",
- Paths.get(Simple.REPOSITORY).toString(), Paths.get(Simple.EXAMPLES).toString() };
+ Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.EXAMPLES).toString() };
CommandLineApplication.mainProgram(args);
assertThat(this.commandLine.getErrorOutput()).contains("Processing 6 object(s) completed");
}
@@ -145,7 +147,7 @@ public class CommandlineApplicationTest {
public void testValidOutputConfiguration() throws IOException {
final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-o", this.output.toString(), "-r",
- Paths.get(Simple.REPOSITORY).toString(), Paths.get(Simple.SIMPLE_VALID).toString() };
+ Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.SIMPLE_VALID).toString() };
CommandLineApplication.mainProgram(args);
assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT);
assertThat(this.output).exists();
@@ -155,7 +157,8 @@ public class CommandlineApplicationTest {
@Test
public void testNoInput() {
// assertThat(output).doesNotExist();
- final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-r", Paths.get(Simple.REPOSITORY).toString(), };
+ final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-r",
+ Paths.get(Simple.REPOSITORY_URI).toString(), };
CommandLineApplication.mainProgram(args);
checkForHelp(this.commandLine.getOutputLines());
}
@@ -164,7 +167,7 @@ public class CommandlineApplicationTest {
public void testPrint() {
final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-p", "-r",
- Paths.get(Simple.REPOSITORY).toString(), "-o", this.output.toString(), Paths.get(Simple.SIMPLE_VALID).toString() };
+ Paths.get(Simple.REPOSITORY_URI).toString(), "-o", this.output.toString(), Paths.get(Simple.SIMPLE_VALID).toString() };
CommandLineApplication.mainProgram(args);
assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT);
assertThat(this.commandLine.getOutputLines().get(0)).contains("");
@@ -174,7 +177,7 @@ public class CommandlineApplicationTest {
public void testHtmlExtraktion() throws IOException {
final String[] args = new String[] { "-s", Paths.get(Simple.SCENARIOS).toString(), "-h", "-o",
this.output.toAbsolutePath().toString(),
- "-r", Paths.get(Simple.REPOSITORY).toString(), Paths.get(Simple.SIMPLE_VALID).toString() };
+ "-r", Paths.get(Simple.REPOSITORY_URI).toString(), Paths.get(Simple.SIMPLE_VALID).toString() };
CommandLineApplication.mainProgram(args);
assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT);
assertThat(Files.list(this.output).filter(f -> f.toString().endsWith(".html")).count()).isGreaterThan(0);
@@ -183,8 +186,8 @@ public class CommandlineApplicationTest {
@Test
public void testAssertionsExtraktion() {
final String[] args = new String[] { "-d", "-s", Paths.get(Simple.SCENARIOS).toString(), "-r",
- Paths.get(Simple.REPOSITORY).toString(), "-o", this.output.toString(), "-c", Paths.get(ASSERTIONS).toString(),
- Paths.get(Simple.REPOSITORY).toString(),
+ Paths.get(Simple.REPOSITORY_URI).toString(), "-o", this.output.toString(), "-c", Paths.get(ASSERTIONS).toString(),
+ Paths.get(Simple.REPOSITORY_URI).toString(),
Paths.get(Simple.SIMPLE_VALID).toString() };
CommandLineApplication.mainProgram(args);
assertThat(this.commandLine.getErrorOutput()).contains(RESULT_OUTPUT);
diff --git a/src/test/java/de/kosit/validationtool/cmd/ExtractHtmlActionTest.java b/src/test/java/de/kosit/validationtool/cmd/ExtractHtmlActionTest.java
index 75c0449..b392d27 100644
--- a/src/test/java/de/kosit/validationtool/cmd/ExtractHtmlActionTest.java
+++ b/src/test/java/de/kosit/validationtool/cmd/ExtractHtmlActionTest.java
@@ -34,6 +34,7 @@ import org.junit.Test;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
+import de.kosit.validationtool.impl.TestObjectFactory;
import de.kosit.validationtool.impl.tasks.CheckAction;
/**
@@ -51,7 +52,7 @@ public class ExtractHtmlActionTest {
@Before
public void setup() throws IOException {
this.tmpDirectory = Files.createTempDirectory("checktool");
- this.action = new ExtractHtmlContentAction(Helper.loadTestRepository(), this.tmpDirectory);
+ this.action = new ExtractHtmlContentAction(TestObjectFactory.createProcessor(), this.tmpDirectory);
}
@After
diff --git a/src/test/java/de/kosit/validationtool/cmd/PrintReportActionTest.java b/src/test/java/de/kosit/validationtool/cmd/PrintReportActionTest.java
index e1b16f4..303eff1 100644
--- a/src/test/java/de/kosit/validationtool/cmd/PrintReportActionTest.java
+++ b/src/test/java/de/kosit/validationtool/cmd/PrintReportActionTest.java
@@ -30,6 +30,7 @@ import org.junit.Test;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
+import de.kosit.validationtool.impl.TestObjectFactory;
import de.kosit.validationtool.impl.tasks.CheckAction;
/**
@@ -46,7 +47,7 @@ public class PrintReportActionTest {
public void setup() {
this.commandLine = new CommandLine();
this.commandLine.activate();
- this.action = new PrintReportAction();
+ this.action = new PrintReportAction(TestObjectFactory.createProcessor());
}
@After
diff --git a/src/test/java/de/kosit/validationtool/cmd/SerializeReportActionTest.java b/src/test/java/de/kosit/validationtool/cmd/SerializeReportActionTest.java
index d0373f2..6807665 100644
--- a/src/test/java/de/kosit/validationtool/cmd/SerializeReportActionTest.java
+++ b/src/test/java/de/kosit/validationtool/cmd/SerializeReportActionTest.java
@@ -34,6 +34,7 @@ import org.junit.Test;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
+import de.kosit.validationtool.impl.TestObjectFactory;
import de.kosit.validationtool.impl.tasks.CheckAction;
/**
@@ -49,7 +50,7 @@ public class SerializeReportActionTest {
@Before
public void setup() throws IOException {
this.tmpDirectory = Files.createTempDirectory("checktool");
- this.action = new SerializeReportAction(this.tmpDirectory);
+ this.action = new SerializeReportAction(this.tmpDirectory, TestObjectFactory.createProcessor());
}
@After
diff --git a/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java b/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java
new file mode 100644
index 0000000..1fadac4
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java
@@ -0,0 +1,49 @@
+package de.kosit.validationtool.config;
+
+import static de.kosit.validationtool.config.ConfigurationBuilder.fallback;
+import static de.kosit.validationtool.config.ConfigurationBuilder.report;
+import static de.kosit.validationtool.config.ConfigurationBuilder.scenario;
+import static de.kosit.validationtool.config.ConfigurationBuilder.schema;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.net.URI;
+
+import org.junit.Test;
+
+import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.api.InputFactory;
+import de.kosit.validationtool.api.Result;
+import de.kosit.validationtool.impl.DefaultCheck;
+import de.kosit.validationtool.impl.Helper.Simple;
+import de.kosit.validationtool.impl.ResolvingMode;
+
+/**
+ * @author Andreas Penski
+ */
+public class SimpleConfigTest {
+
+ @Test
+ public void testSimpleWithApi() {
+ //@formatter:off
+ final Configuration config =
+ Configuration.create().name("Simple-API")
+ .with(scenario("simple")
+ .validate(schema("Sample Schema").schemaLocation(URI.create("simple.xsd")))
+ .with(report("Report für eRechnung").source("report.xsl"))
+ .acceptWith("count(//test:rejected) = 0")
+ .declareNamespace("cri", "http://www.xoev.de/de/validator/framework/1/createreportinput")
+ .declareNamespace("rpt", "http://validator.kosit.de/test-report")
+ .declareNamespace("test", "http://validator.kosit.de/test-sample")
+ .match("/test:simple")
+// .description("awesome api")
+ )
+ .with(fallback().name("default").source("report.xsl"))
+
+ .resolvingMode(ResolvingMode.STRICT_RELATIVE)
+ .useRepository(Simple.REPOSITORY_URI).build();
+ //@formatter:on
+ final DefaultCheck check = new DefaultCheck(config);
+ final Result result = check.checkInput(InputFactory.read(Simple.SIMPLE_VALID));
+ assertThat(result).isNotNull();
+ }
+}
diff --git a/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java b/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java
index 4eb20cb..9a54bee 100644
--- a/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java
@@ -55,7 +55,7 @@ public class ContentRepositoryTest {
@Before
public void setup() {
- this.repository = new ContentRepository(ObjectFactory.createProcessor(), Simple.REPOSITORY);
+ this.repository = Simple.createContentRepository();
}
@Test
@@ -114,7 +114,7 @@ public class ContentRepositoryTest {
@Test
public void loadFromJar() throws URISyntaxException {
- this.repository = new ContentRepository(ObjectFactory.createProcessor(), Helper.JAR_REPOSITORY.toURI());
+ this.repository = new ContentRepository(TestObjectFactory.createProcessor(), Helper.JAR_REPOSITORY.toURI(), null);
final XsltExecutable xsltExecutable = this.repository.loadXsltScript(URI.create("resources/eRechnung/report.xsl"));
assertThat(xsltExecutable).isNotNull();
}
@@ -136,7 +136,7 @@ public class ContentRepositoryTest {
// @Test
// public void loadFromJar() throws URISyntaxException {
- // this.content = new ContentRepository(ObjectFactory.createProcessor(), Helper.JAR_REPOSITORY.toURI());
+ // this.content = new ContentRepository(TestObjectFactory.createProcessor(), Helper.JAR_REPOSITORY.toURI());
// this.repository = new ScenarioRepository(this.content);
// final CheckConfiguration conf = new CheckConfiguration(
// ScenarioRepository.class.getClassLoader().getResource("xrechnung/scenarios.xml").toURI());
diff --git a/src/test/java/de/kosit/validationtool/impl/ConversionServiceTest.java b/src/test/java/de/kosit/validationtool/impl/ConversionServiceTest.java
index 60500c5..2bbef72 100644
--- a/src/test/java/de/kosit/validationtool/impl/ConversionServiceTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/ConversionServiceTest.java
@@ -21,7 +21,6 @@ package de.kosit.validationtool.impl;
import static org.assertj.core.api.Java6Assertions.assertThat;
-import java.io.File;
import java.io.Serializable;
import java.net.URISyntaxException;
import java.net.URL;
@@ -54,8 +53,7 @@ public class ConversionServiceTest {
@Before
public void setup() {
this.service = new ConversionService();
- this.repository = new ContentRepository(ObjectFactory.createProcessor(),
- new File("src/test/resources/examples/repository").toURI());
+ this.repository = Simple.createContentRepository();
}
@Test
diff --git a/src/test/java/de/kosit/validationtool/impl/DefaultCheckTest.java b/src/test/java/de/kosit/validationtool/impl/DefaultCheckTest.java
index 0792d48..1b86c24 100644
--- a/src/test/java/de/kosit/validationtool/impl/DefaultCheckTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/DefaultCheckTest.java
@@ -57,7 +57,7 @@ public class DefaultCheckTest {
@Before
public void setup() {
final CheckConfiguration d = new CheckConfiguration(Simple.SCENARIOS);
- d.setScenarioRepository(new File(Simple.REPOSITORY).toURI());
+ d.setScenarioRepository(new File(Simple.REPOSITORY_URI).toURI());
this.implementation = new DefaultCheck(d);
}
diff --git a/src/test/java/de/kosit/validationtool/impl/DocumentParserTest.java b/src/test/java/de/kosit/validationtool/impl/DocumentParserTest.java
index d914d9e..7a39c60 100644
--- a/src/test/java/de/kosit/validationtool/impl/DocumentParserTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/DocumentParserTest.java
@@ -28,7 +28,6 @@ import org.junit.rules.ExpectedException;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.model.Result;
-import de.kosit.validationtool.impl.tasks.DocumentParseAction;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
import net.sf.saxon.s9api.XdmNode;
@@ -45,7 +44,7 @@ public class DocumentParserTest {
@Test
public void testSimple() {
- final Result result = DocumentParseAction.parseDocument(read(Simple.SIMPLE_VALID));
+ final Result result = Helper.parseDocument(read(Simple.SIMPLE_VALID));
assertThat(result).isNotNull();
assertThat(result.getObject()).isNotNull();
assertThat(result.getErrors()).isEmpty();
@@ -54,7 +53,7 @@ public class DocumentParserTest {
@Test
public void testIllformed() {
- final Result result = DocumentParseAction.parseDocument(read(Simple.NOT_WELLFORMED));
+ final Result result = Helper.parseDocument(read(Simple.NOT_WELLFORMED));
assertThat(result).isNotNull();
assertThat(result.getErrors()).isNotEmpty();
assertThat(result.getObject()).isNull();
@@ -64,7 +63,7 @@ public class DocumentParserTest {
@Test
public void testNullInput() {
this.exception.expect(IllegalArgumentException.class);
- DocumentParseAction.parseDocument(null);
+ Helper.parseDocument(null);
}
diff --git a/src/test/java/de/kosit/validationtool/impl/Helper.java b/src/test/java/de/kosit/validationtool/impl/Helper.java
index 1d50fc4..ad78c94 100644
--- a/src/test/java/de/kosit/validationtool/impl/Helper.java
+++ b/src/test/java/de/kosit/validationtool/impl/Helper.java
@@ -36,6 +36,12 @@ import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
+import de.kosit.validationtool.api.Input;
+import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
+import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.impl.tasks.DocumentParseAction;
+import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
+
import net.sf.saxon.dom.NodeOverNodeInfo;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XdmNode;
@@ -61,7 +67,7 @@ public class Helper {
public static final URI SCENARIOS = ROOT.resolve("scenarios.xml");
- public static final URI REPOSITORY = ROOT.resolve("repository/");
+ public static final URI REPOSITORY_URI = ROOT.resolve("repository/");
public static final URI INVALID = ROOT.resolve("input/simple-invalid.xml");
@@ -73,8 +79,16 @@ public class Helper {
public static final URI NOT_EXISTING = EXAMPLES_DIR.resolve("doesnotexist");
- public static final URI REPORT_XSL = REPOSITORY.resolve("report.xsl");
+ public static final URI REPORT_XSL = REPOSITORY_URI.resolve("report.xsl");
+ public static final ContentRepository createContentRepository() {
+ final ResolvingConfigurationStrategy strategy = ResolvingMode.STRICT_RELATIVE.getStrategy();
+ final ContentRepository rep = new ContentRepository(TestObjectFactory.createProcessor(), Simple.REPOSITORY_URI,
+ strategy.createResolver(Simple.REPOSITORY_URI));
+ rep.setResolvingConfigurationStrategy(strategy);
+ rep.setSchemaFactory(strategy.createSchemaFactory());
+ return rep;
+ }
public static URI getSchemaLocation() {
return ROOT.resolve("repository/simple.xsd");
}
@@ -118,7 +132,7 @@ public class Helper {
*/
public static XdmNode load(final URL url) {
try ( final InputStream input = url.openStream() ) {
- return ObjectFactory.createProcessor().newDocumentBuilder().build(new StreamSource(input));
+ return TestObjectFactory.createProcessor().newDocumentBuilder().build(new StreamSource(input));
} catch (final SaxonApiException | IOException e) {
throw new IllegalStateException("Fehler beim Laden der XML-Datei", e);
@@ -140,12 +154,12 @@ public class Helper {
* @return ein {@link ContentRepository}
*/
public static ContentRepository loadTestRepository() {
- return new ContentRepository(ObjectFactory.createProcessor(), new File("src/test/resources/examples/repository").toURI());
+ return new ContentRepository(TestObjectFactory.createProcessor(), new File("src/test/resources/examples/repository").toURI(), null);
}
public static String serialize(final Document doc) {
try ( final StringWriter writer = new StringWriter() ) {
- final Transformer transformer = ObjectFactory.createTransformer(true);
+ final Transformer transformer = TestObjectFactory.createTransformer(true);
transformer.transform(new DOMSource(doc), new StreamResult(writer));
return writer.toString();
} catch (final IOException | TransformerException e) {
@@ -157,4 +171,7 @@ public class Helper {
return serialize((Document) NodeOverNodeInfo.wrap(node.getUnderlyingNode()));
}
+ public static Result parseDocument(final Input input) {
+ return new DocumentParseAction(TestObjectFactory.createProcessor()).parseDocument(input);
+ }
}
diff --git a/src/test/java/de/kosit/validationtool/impl/RelativeUriResolverTest.java b/src/test/java/de/kosit/validationtool/impl/RelativeUriResolverTest.java
index c7b83d6..2106f65 100644
--- a/src/test/java/de/kosit/validationtool/impl/RelativeUriResolverTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/RelativeUriResolverTest.java
@@ -33,6 +33,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import de.kosit.validationtool.impl.xml.RelativeUriResolver;
+
/**
* Testet den Uri-Resolver der relative auflösen soll
*
@@ -63,13 +65,13 @@ public class RelativeUriResolverTest {
@Test
public void testNotExisting() throws TransformerException {
- this.exception.expect(IllegalStateException.class);
+ this.exception.expect(TransformerException.class);
this.resolver.resolve("ubl-0001", BASE.toASCIIString());
}
@Test
public void testOutOfPath() throws TransformerException {
- this.exception.expect(IllegalStateException.class);
+ this.exception.expect(TransformerException.class);
this.resolver.resolve("../results/report.xml", BASE.toASCIIString());
}
diff --git a/src/test/java/de/kosit/validationtool/impl/SaxonSecurityTest.java b/src/test/java/de/kosit/validationtool/impl/SaxonSecurityTest.java
index 0729446..1c79f51 100644
--- a/src/test/java/de/kosit/validationtool/impl/SaxonSecurityTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/SaxonSecurityTest.java
@@ -35,21 +35,19 @@ import org.w3c.dom.Document;
import lombok.extern.slf4j.Slf4j;
+import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.Helper.Simple;
+import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.impl.xml.RelativeUriResolver;
+import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
import net.sf.saxon.s9api.DOMDestination;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
+import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.s9api.XsltCompiler;
import net.sf.saxon.s9api.XsltExecutable;
import net.sf.saxon.s9api.XsltTransformer;
-import de.kosit.validationtool.api.InputFactory;
-import de.kosit.validationtool.impl.model.Result;
-import de.kosit.validationtool.impl.tasks.DocumentParseAction;
-import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
-
-
-import net.sf.saxon.s9api.XdmNode;
/**
@@ -62,19 +60,19 @@ public class SaxonSecurityTest {
@Test
public void testEvilStylesheets() throws IOException {
- final Processor p = ObjectFactory.createProcessor();
+ final Processor p = TestObjectFactory.createProcessor();
for (int i = 1; i <= 5; i++) {
try {
final URL resource = SaxonSecurityTest.class.getResource(String.format("/evil/evil%s.xsl", i));
final XsltCompiler compiler = p.newXsltCompiler();
- final RelativeUriResolver resolver = new RelativeUriResolver(Simple.REPOSITORY);
+ final RelativeUriResolver resolver = new RelativeUriResolver(Simple.REPOSITORY_URI);
compiler.setURIResolver(resolver);
final XsltExecutable exetuable = compiler.compile(new StreamSource(resource.openStream()));
final XsltTransformer transformer = exetuable.load();
- final Document document = ObjectFactory.createDocumentBuilder(false).newDocument();
+ final Document document = TestObjectFactory.createDocumentBuilder(false).newDocument();
document.createElement("root");
- final Document result = ObjectFactory.createDocumentBuilder(false).newDocument();
- transformer.getUnderlyingController().setUnparsedTextURIResolver(resolver);
+ final Document result = TestObjectFactory.createDocumentBuilder(false).newDocument();
+ // transformer.getUnderlyingController().setUnparsedTextURIResolver(resolver);
transformer.setURIResolver(resolver);
transformer.setSource(new DOMSource(document));
transformer.setDestination(new DOMDestination(result));
@@ -94,7 +92,7 @@ public class SaxonSecurityTest {
@Test
public void testXxe() {
final URL resource = SaxonSecurityTest.class.getResource("/evil/xxe.xml");
- final Result result = DocumentParseAction.parseDocument(InputFactory.read(resource));
+ final Result result = Helper.parseDocument(InputFactory.read(resource));
assertThat(result.isValid()).isFalse();
assertThat(result.getObject()).isNull();
assertThat(result.getErrors().stream().map(XMLSyntaxError::getMessage).collect(Collectors.joining()))
diff --git a/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java b/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
index e450050..ceb8e85 100644
--- a/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
@@ -27,6 +27,7 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.junit.Before;
import org.junit.Rule;
@@ -38,7 +39,6 @@ import lombok.Data;
import de.kosit.validationtool.api.Configuration;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.model.Result;
-import de.kosit.validationtool.impl.tasks.DocumentParseAction;
import de.kosit.validationtool.model.scenarios.ScenarioType;
import net.sf.saxon.s9api.Processor;
@@ -70,10 +70,8 @@ public class ScenarioRepositoryTest {
private ContentRepository contentRepository;
- @Override
- public void build() {
- // nothing
- }
+ private Map additionalParameters;
+
}
@Rule
@@ -137,11 +135,10 @@ public class ScenarioRepositoryTest {
}
private static XdmNode load(final URI uri) throws IOException {
- final DocumentParseAction p = new DocumentParseAction();
- return DocumentParseAction.parseDocument(read(uri.toURL())).getObject();
+ return Helper.parseDocument(read(uri.toURL())).getObject();
}
private static XPathExecutable createXpath(final String expression) {
- return new ContentRepository(ObjectFactory.createProcessor(), null).createXPath(expression, new HashMap<>());
+ return new ContentRepository(TestObjectFactory.createProcessor(), null, null).createXPath(expression, new HashMap<>());
}
}
diff --git a/src/test/java/de/kosit/validationtool/impl/SimpleScenarioCheckTest.java b/src/test/java/de/kosit/validationtool/impl/SimpleScenarioCheckTest.java
index 23b1c5f..0040d31 100644
--- a/src/test/java/de/kosit/validationtool/impl/SimpleScenarioCheckTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/SimpleScenarioCheckTest.java
@@ -25,7 +25,7 @@ public class SimpleScenarioCheckTest {
@Before
public void setup() {
final CheckConfiguration d = new CheckConfiguration(Simple.SCENARIOS);
- d.setScenarioRepository(Simple.REPOSITORY);
+ d.setScenarioRepository(Simple.REPOSITORY_URI);
this.implementation = new DefaultCheck(d);
}
diff --git a/src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java b/src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java
new file mode 100644
index 0000000..1c47ad7
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java
@@ -0,0 +1,7 @@
+package de.kosit.validationtool.impl;
+
+/**
+ * @author Andreas Penski
+ */
+public class TestObjectFactory extends ObjectFactory {
+}
diff --git a/src/test/java/de/kosit/validationtool/impl/VersioningTest.java b/src/test/java/de/kosit/validationtool/impl/VersioningTest.java
index ff67943..38cb77a 100644
--- a/src/test/java/de/kosit/validationtool/impl/VersioningTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/VersioningTest.java
@@ -56,7 +56,7 @@ public class VersioningTest {
@Before
public void setup() {
- this.repository = new ContentRepository(ObjectFactory.createProcessor(), Simple.REPOSITORY);
+ this.repository = Simple.createContentRepository();
this.service = new ConversionService();
}
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java b/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java
index 0065997..541d1c9 100644
--- a/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java
@@ -10,7 +10,7 @@ import org.junit.Test;
import de.kosit.validationtool.api.AcceptRecommendation;
import de.kosit.validationtool.impl.ContentRepository;
-import de.kosit.validationtool.impl.ObjectFactory;
+import de.kosit.validationtool.impl.TestObjectFactory;
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
import net.sf.saxon.s9api.XPathExecutable;
@@ -22,6 +22,7 @@ import net.sf.saxon.s9api.XPathExecutable;
*/
public class ComputeAcceptanceActionTest {
+ private static final String DOESNOT_EXIST = "count(//doesnotExist) = 0";
private final ComputeAcceptanceAction action = new ComputeAcceptanceAction();
@Test
@@ -49,7 +50,7 @@ public class ComputeAcceptanceActionTest {
@Test
public void testValidAcceptMatch() {
final Bag bag = createBag(true, true);
- bag.getScenarioSelectionResult().getObject().setAcceptExecutable(createXpath("count(//doesnotExist) = 0"));
+ bag.getScenarioSelectionResult().getObject().setAcceptExecutable(createXpath(DOESNOT_EXIST));
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.ACCEPTABLE);
}
@@ -65,7 +66,7 @@ public class ComputeAcceptanceActionTest {
@Test
public void testAcceptMatchOverridesSchematronErrors() {
final Bag bag = createBag(true, false);
- bag.getScenarioSelectionResult().getObject().setAcceptExecutable(createXpath("count(//doesnotExist) = 0"));
+ bag.getScenarioSelectionResult().getObject().setAcceptExecutable(createXpath(DOESNOT_EXIST));
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.ACCEPTABLE);
}
@@ -73,7 +74,7 @@ public class ComputeAcceptanceActionTest {
@Test
public void testValidAcceptMatchOnSchemaFailed() {
final Bag bag = createBag(false, true);
- bag.getScenarioSelectionResult().getObject().setAcceptExecutable(createXpath("count(//doesnotExist) = 0"));
+ bag.getScenarioSelectionResult().getObject().setAcceptExecutable(createXpath(DOESNOT_EXIST));
this.action.check(bag);
assertThat(bag.getAcceptStatus()).isEqualTo(AcceptRecommendation.REJECT);
}
@@ -104,6 +105,6 @@ public class ComputeAcceptanceActionTest {
private static XPathExecutable createXpath(final String expression) {
- return new ContentRepository(ObjectFactory.createProcessor(), null).createXPath(expression, new HashMap<>());
+ return new ContentRepository(TestObjectFactory.createProcessor(), null, null).createXPath(expression, new HashMap<>());
}
}
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/SchemaValidatorActionTest.java b/src/test/java/de/kosit/validationtool/impl/tasks/SchemaValidatorActionTest.java
index 354076e..bdc6248 100644
--- a/src/test/java/de/kosit/validationtool/impl/tasks/SchemaValidatorActionTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/SchemaValidatorActionTest.java
@@ -43,12 +43,13 @@ import org.xml.sax.SAXException;
import de.kosit.validationtool.api.Input;
import de.kosit.validationtool.api.InputFactory;
-import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
-import de.kosit.validationtool.impl.ObjectFactory;
import de.kosit.validationtool.impl.Scenario;
+import de.kosit.validationtool.impl.TestObjectFactory;
import de.kosit.validationtool.impl.input.SourceInput;
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
+import de.kosit.validationtool.impl.xml.StrictRelativeResolvingStrategy;
/**
* Tests die {@link SchemaValidationAction}.
@@ -63,7 +64,7 @@ public class SchemaValidatorActionTest {
@Before
public void setup() {
- this.service = new SchemaValidationAction();
+ this.service = new SchemaValidationAction(new StrictRelativeResolvingStrategy(), TestObjectFactory.createProcessor());
}
@Test
@@ -89,7 +90,7 @@ public class SchemaValidatorActionTest {
@Test
public void testSchemaReferences() {
- final Schema reportInputSchema = new ContentRepository(ObjectFactory.createProcessor(), Simple.REPOSITORY).getReportInputSchema();
+ final Schema reportInputSchema = Simple.createContentRepository().getReportInputSchema();
assertThat(reportInputSchema).isNotNull();
}
@@ -98,7 +99,7 @@ public class SchemaValidatorActionTest {
try ( final InputStream inputStream = Simple.SIMPLE_VALID.toURL().openStream() ) {
final Bag bag = createBag(InputFactory.read(new StreamSource(inputStream)));
// don't read the real inputstream here!
- bag.setParserResult(DocumentParseAction.parseDocument(InputFactory.read(Simple.SIMPLE_VALID.toURL())));
+ bag.setParserResult(Helper.parseDocument(InputFactory.read(Simple.SIMPLE_VALID.toURL())));
this.service.check(bag);
assertThat(bag.getSchemaValidationResult()).isNotNull();
assertThat(bag.getSchemaValidationResult().isValid()).isTrue();
@@ -114,7 +115,7 @@ public class SchemaValidatorActionTest {
this.service.setInMemoryLimit(5L);
input.setLength(6L);
- bag.setParserResult(DocumentParseAction.parseDocument(InputFactory.read(Simple.SIMPLE_VALID.toURL())));
+ bag.setParserResult(Helper.parseDocument(InputFactory.read(Simple.SIMPLE_VALID.toURL())));
this.service.check(bag);
assertThat(bag.getSchemaValidationResult()).isNotNull();
assertThat(bag.getSchemaValidationResult().isValid()).isTrue();
@@ -127,7 +128,7 @@ public class SchemaValidatorActionTest {
final Reader reader = new InputStreamReader(inputStream) ) {
final SourceInput input = (SourceInput) InputFactory.read(new StreamSource(reader));
final Bag bag = createBag(input);
- bag.setParserResult(DocumentParseAction.parseDocument(InputFactory.read(Simple.SIMPLE_VALID.toURL())));
+ bag.setParserResult(Helper.parseDocument(InputFactory.read(Simple.SIMPLE_VALID.toURL())));
this.service.check(bag);
this.service.check(bag);
assertThat(bag.getSchemaValidationResult()).isNotNull();
@@ -143,7 +144,7 @@ public class SchemaValidatorActionTest {
final Bag bag = createBag(input);
// set limit and length for serialization to 5 bytes
this.service.setInMemoryLimit(5L);
- bag.setParserResult(DocumentParseAction.parseDocument(InputFactory.read(Simple.SIMPLE_VALID.toURL())));
+ bag.setParserResult(Helper.parseDocument(InputFactory.read(Simple.SIMPLE_VALID.toURL())));
this.service.check(bag);
this.service.check(bag);
assertThat(bag.getSchemaValidationResult()).isNotNull();
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/SchematronValidationActionTest.java b/src/test/java/de/kosit/validationtool/impl/tasks/SchematronValidationActionTest.java
index 80b7176..809a412 100644
--- a/src/test/java/de/kosit/validationtool/impl/tasks/SchematronValidationActionTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/SchematronValidationActionTest.java
@@ -13,12 +13,11 @@ import org.junit.Before;
import org.junit.Test;
import de.kosit.validationtool.api.InputFactory;
-import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.ConversionService;
import de.kosit.validationtool.impl.Helper.Simple;
-import de.kosit.validationtool.impl.ObjectFactory;
import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.impl.Scenario.Transformation;
+import de.kosit.validationtool.impl.xml.RelativeUriResolver;
import de.kosit.validationtool.model.scenarios.ResourceType;
import net.sf.saxon.s9api.SaxonApiException;
@@ -36,8 +35,7 @@ public class SchematronValidationActionTest {
@Before
public void setup() {
- final ContentRepository repository = new ContentRepository(ObjectFactory.createProcessor(), Simple.REPOSITORY);
- this.action = new SchematronValidationAction(repository, new ConversionService());
+ this.action = new SchematronValidationAction(new RelativeUriResolver(Simple.REPOSITORY_URI), new ConversionService());
}
@Test
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java b/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java
index 78a14f5..f7a5d34 100644
--- a/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java
@@ -16,8 +16,9 @@ import de.kosit.validationtool.api.Input;
import de.kosit.validationtool.api.InputFactory;
import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.Helper;
-import de.kosit.validationtool.impl.ObjectFactory;
+import de.kosit.validationtool.impl.ResolvingMode;
import de.kosit.validationtool.impl.Scenario;
+import de.kosit.validationtool.impl.TestObjectFactory;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
import de.kosit.validationtool.model.reportInput.CreateReportInput;
@@ -48,7 +49,7 @@ public class TestBagBuilder {
public static Bag createBag(final Input input, final boolean parse, final CreateReportInput reportInput) {
final Bag bag = new Bag(input, reportInput);
if (parse) {
- bag.setParserResult(DocumentParseAction.parseDocument(bag.getInput()));
+ bag.setParserResult(Helper.parseDocument(bag.getInput()));
}
bag.setScenarioSelectionResult(new Result<>(createScenario(Helper.Simple.getSchemaLocation())));
return bag;
@@ -73,11 +74,13 @@ public class TestBagBuilder {
}
private static Schema createSchema(final URL toURL) {
- return new ContentRepository(ObjectFactory.createProcessor(), null).createSchema(toURL);
+ final ContentRepository contentRepository = new ContentRepository(TestObjectFactory.createProcessor(), null, null);
+ contentRepository.setSchemaFactory(ResolvingMode.STRICT_RELATIVE.getStrategy().createSchemaFactory());
+ return contentRepository.createSchema(toURL);
}
private static XdmNode createReport() {
- return DocumentParseAction.parseDocument(InputFactory.read("xml ".getBytes(), "someXml")).getObject();
+ return Helper.parseDocument(InputFactory.read("xml ".getBytes(), "someXml")).getObject();
}
static Bag createBag(final boolean schemaValid, final boolean schematronValid) {
From 7ca3ef90f3b1585d51c94a89a52d5ec47c30b9b7 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Wed, 29 Apr 2020 10:29:30 +0200
Subject: [PATCH 07/36] (enhance) ContentRepository is only based on uri and
strategy
---
.../config/ConfigurationBuilder.java | 5 +---
.../config/ConfigurationLoader.java | 5 +---
.../impl/ContentRepository.java | 30 +++++++++++++------
.../validationtool/impl/ObjectFactory.java | 5 ++--
.../impl/ContentRepositoryTest.java | 5 +++-
.../de/kosit/validationtool/impl/Helper.java | 20 ++++---------
.../impl/ScenarioRepositoryTest.java | 2 +-
.../tasks/ComputeAcceptanceActionTest.java | 4 +--
.../impl/tasks/TestBagBuilder.java | 4 +--
9 files changed, 38 insertions(+), 42 deletions(-)
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
index e1ec359..f81e967 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
@@ -172,10 +172,7 @@ public class ConfigurationBuilder {
if (this.processor == null) {
this.processor = resolving.createProcessor();
}
- final ContentRepository contentRepository = new ContentRepository(this.processor, this.repository,
- resolving.createResolver(this.repository));
- contentRepository.setSchemaFactory(resolving.createSchemaFactory());
- contentRepository.setResolvingConfigurationStrategy(resolving);
+ final ContentRepository contentRepository = new ContentRepository(this.resolvingConfigurationStrategy, this.repository);
final List list = initializeScenarios(contentRepository);
final Scenario fallbackScenario = initializeFallback(contentRepository);
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java b/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
index 589979a..cff00fc 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
@@ -118,10 +118,7 @@ public class ConfigurationLoader {
public Configuration build() {
final ResolvingConfigurationStrategy resolving = getResolvingConfigurationStrategy();
final Processor processor = resolving.createProcessor();
- final ContentRepository contentRepository = new ContentRepository(processor, getScenarioRepository(),
- resolving.createResolver(this.getScenarioRepository()));
- contentRepository.setSchemaFactory(resolving.createSchemaFactory());
- contentRepository.setResolvingConfigurationStrategy(resolving);
+ final ContentRepository contentRepository = new ContentRepository(resolving, getScenarioRepository());
final Scenarios def = loadScenarios(contentRepository.getScenarioSchema(), processor);
final List scenarios = initializeScenarios(def, contentRepository);
diff --git a/src/main/java/de/kosit/validationtool/impl/ContentRepository.java b/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
index 139e776..9d69706 100644
--- a/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
+++ b/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
@@ -42,7 +42,6 @@ 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;
@@ -78,13 +77,27 @@ public class ContentRepository {
private final URIResolver resolver;
- @Setter
- private SchemaFactory schemaFactory;
+ private final SchemaFactory schemaFactory;
- @Setter
@Getter
- private ResolvingConfigurationStrategy resolvingConfigurationStrategy;
+ private final ResolvingConfigurationStrategy resolvingConfigurationStrategy;
+ /**
+ * Creates a new {@link ContentRepository} based on configured security and resolving strategy and the specified
+ * repository location.
+ *
+ * @param strategy the security and resolving strategy
+ * @param repository the repository.
+ */
+ public ContentRepository(final ResolvingConfigurationStrategy strategy, final URI repository) {
+ this.repository = repository;
+ this.resolvingConfigurationStrategy = strategy;
+ this.processor = this.resolvingConfigurationStrategy.createProcessor();
+ this.resolver = this.resolvingConfigurationStrategy.createResolver(repository);
+ this.schemaFactory = this.resolvingConfigurationStrategy.createSchemaFactory();
+ }
+
+ @SuppressWarnings("java:S2095")
private static Source resolve(final URL resource) {
try {
return new StreamSource(resource.openStream(), resource.toURI().getRawPath());
@@ -119,10 +132,9 @@ public class ContentRepository {
final CollectingErrorEventHandler listener = new CollectingErrorEventHandler();
try {
xsltCompiler.setErrorListener(listener);
- final URIResolver resolver = getResolver();
- if (resolver != null) {
+ if (getResolver() != null) {
// otherwise use default resolver
- xsltCompiler.setURIResolver(resolver);
+ xsltCompiler.setURIResolver(getResolver());
}
return xsltCompiler.compile(resolveInRepository(uri));
@@ -275,7 +287,7 @@ public class ContentRepository {
}
public List createSchematronTransformations(final ScenarioType s) {
- return s.getValidateWithSchematron() != null && s.getValidateWithSchematron().isEmpty() ? Collections.emptyList()
+ return s.getValidateWithSchematron().isEmpty() ? Collections.emptyList()
: s.getValidateWithSchematron().stream().map(this::createSchematronTransformation).collect(Collectors.toList());
}
diff --git a/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java b/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
index 8c96eae..f1917ba 100644
--- a/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
+++ b/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
@@ -25,7 +25,6 @@ import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
-import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -144,8 +143,8 @@ public class ObjectFactory {
Transformer transformer = null;
try {
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
- transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
- transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); // Compliant
+ // 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");
diff --git a/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java b/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java
index 9a54bee..fe1959c 100644
--- a/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/ContentRepositoryTest.java
@@ -114,7 +114,8 @@ public class ContentRepositoryTest {
@Test
public void loadFromJar() throws URISyntaxException {
- this.repository = new ContentRepository(TestObjectFactory.createProcessor(), Helper.JAR_REPOSITORY.toURI(), null);
+ assert Helper.JAR_REPOSITORY != null;
+ this.repository = new ContentRepository(ResolvingMode.STRICT_RELATIVE.getStrategy(), Helper.JAR_REPOSITORY.toURI());
final XsltExecutable xsltExecutable = this.repository.loadXsltScript(URI.create("resources/eRechnung/report.xsl"));
assertThat(xsltExecutable).isNotNull();
}
@@ -122,6 +123,7 @@ public class ContentRepositoryTest {
@Test
public void testLoadSchema() {
final URL main = RelativeUriResolverTest.class.getClassLoader().getResource("loading/main.xsd");
+ assert main != null;
final Schema schema = this.repository.createSchema(main, new ClassPathResourceResolver("/loading"));
assertThat(schema).isNotNull();
}
@@ -129,6 +131,7 @@ public class ContentRepositoryTest {
@Test
public void testLoadSchemaPackaged() throws URISyntaxException {
final URL main = RelativeUriResolverTest.class.getClassLoader().getResource("packaged/main.xsd");
+ assert main != null;
final Schema schema = this.repository.createSchema(main,
new ClassPathResourceResolver(RelativeUriResolverTest.class.getClassLoader().getResource("packaged/").toURI()));
assertThat(schema).isNotNull();
diff --git a/src/test/java/de/kosit/validationtool/impl/Helper.java b/src/test/java/de/kosit/validationtool/impl/Helper.java
index ad78c94..941429d 100644
--- a/src/test/java/de/kosit/validationtool/impl/Helper.java
+++ b/src/test/java/de/kosit/validationtool/impl/Helper.java
@@ -53,6 +53,7 @@ import net.sf.saxon.s9api.XdmNode;
*/
public class Helper {
+
public static class Simple {
public static final URI ROOT = EXAMPLES_DIR.resolve("simple/");
@@ -83,12 +84,9 @@ public class Helper {
public static final ContentRepository createContentRepository() {
final ResolvingConfigurationStrategy strategy = ResolvingMode.STRICT_RELATIVE.getStrategy();
- final ContentRepository rep = new ContentRepository(TestObjectFactory.createProcessor(), Simple.REPOSITORY_URI,
- strategy.createResolver(Simple.REPOSITORY_URI));
- rep.setResolvingConfigurationStrategy(strategy);
- rep.setSchemaFactory(strategy.createSchemaFactory());
- return rep;
+ return new ContentRepository(strategy, Simple.REPOSITORY_URI);
}
+
public static URI getSchemaLocation() {
return ROOT.resolve("repository/simple.xsd");
}
@@ -103,27 +101,18 @@ public class Helper {
public static final URI SCENARIOS_ILLFORMED = ROOT.resolve("scenarios-illformed.xml");
}
-
public static final URI MODEL_ROOT = Paths.get("src/main/model").toUri();
public static final URI ASSERTION_SCHEMA = MODEL_ROOT.resolve("xsd/assertions.xsd");
-
public static final URI TEST_ROOT = Paths.get("src/test/resources").toUri();
public static final URI EXAMPLES_DIR = TEST_ROOT.resolve("examples/");
public static final URI ASSERTIONS = EXAMPLES_DIR.resolve("assertions/tests-xrechnung.xml");
-
-
public static final URL JAR_REPOSITORY = Helper.class.getClassLoader().getResource("xrechnung/repository/");
-
-
-
-
-
/**
* Lädt ein XML-Dokument von der gegebenen URL
*
@@ -154,7 +143,8 @@ public class Helper {
* @return ein {@link ContentRepository}
*/
public static ContentRepository loadTestRepository() {
- return new ContentRepository(TestObjectFactory.createProcessor(), new File("src/test/resources/examples/repository").toURI(), null);
+ return new ContentRepository(ResolvingMode.STRICT_RELATIVE.getStrategy(),
+ new File("src/test/resources/examples/repository").toURI());
}
public static String serialize(final Document doc) {
diff --git a/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java b/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
index ceb8e85..cb11310 100644
--- a/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
@@ -139,6 +139,6 @@ public class ScenarioRepositoryTest {
}
private static XPathExecutable createXpath(final String expression) {
- return new ContentRepository(TestObjectFactory.createProcessor(), null, null).createXPath(expression, new HashMap<>());
+ return new ContentRepository(ResolvingMode.STRICT_RELATIVE.getStrategy(), null).createXPath(expression, new HashMap<>());
}
}
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java b/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java
index 541d1c9..3bb04b2 100644
--- a/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceActionTest.java
@@ -10,7 +10,7 @@ import org.junit.Test;
import de.kosit.validationtool.api.AcceptRecommendation;
import de.kosit.validationtool.impl.ContentRepository;
-import de.kosit.validationtool.impl.TestObjectFactory;
+import de.kosit.validationtool.impl.ResolvingMode;
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
import net.sf.saxon.s9api.XPathExecutable;
@@ -105,6 +105,6 @@ public class ComputeAcceptanceActionTest {
private static XPathExecutable createXpath(final String expression) {
- return new ContentRepository(TestObjectFactory.createProcessor(), null, null).createXPath(expression, new HashMap<>());
+ return new ContentRepository(ResolvingMode.STRICT_RELATIVE.getStrategy(), null).createXPath(expression, new HashMap<>());
}
}
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java b/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java
index f7a5d34..716023f 100644
--- a/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/TestBagBuilder.java
@@ -18,7 +18,6 @@ import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.ResolvingMode;
import de.kosit.validationtool.impl.Scenario;
-import de.kosit.validationtool.impl.TestObjectFactory;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
import de.kosit.validationtool.model.reportInput.CreateReportInput;
@@ -74,8 +73,7 @@ public class TestBagBuilder {
}
private static Schema createSchema(final URL toURL) {
- final ContentRepository contentRepository = new ContentRepository(TestObjectFactory.createProcessor(), null, null);
- contentRepository.setSchemaFactory(ResolvingMode.STRICT_RELATIVE.getStrategy().createSchemaFactory());
+ final ContentRepository contentRepository = new ContentRepository(ResolvingMode.STRICT_RELATIVE.getStrategy(), null);
return contentRepository.createSchema(toURL);
}
From 5b1d0cd467d6ed90eb268af3795f2f8d628619b1 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Wed, 29 Apr 2020 13:55:12 +0200
Subject: [PATCH 08/36] (fix) tests
---
.../api/ResolvingConfigurationStrategy.java | 4 +-
.../config/ConfigurationBuilder.java | 4 +-
.../config/ConfigurationLoader.java | 2 +-
.../impl/ContentRepository.java | 2 +-
.../impl/tasks/ComputeAcceptanceAction.java | 4 +-
.../impl/xml/BaseResolvingStrategy.java | 14 ++++++
.../xml/StrictLocalResolvingStrategy.java | 7 ---
.../xml/StrictRelativeResolvingStrategy.java | 2 +-
.../config/TestConfiguration.java | 31 +++++++++++++
...Test.java => DocumentParseActionTest.java} | 17 +++++--
.../de/kosit/validationtool/impl/Helper.java | 16 ++++++-
.../impl/ScenarioRepositoryTest.java | 45 +++++--------------
12 files changed, 92 insertions(+), 56 deletions(-)
create mode 100644 src/test/java/de/kosit/validationtool/config/TestConfiguration.java
rename src/test/java/de/kosit/validationtool/impl/{DocumentParserTest.java => DocumentParseActionTest.java} (78%)
diff --git a/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java b/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java
index b8ec0b6..525499a 100644
--- a/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java
+++ b/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java
@@ -31,13 +31,13 @@ public interface ResolvingConfigurationStrategy {
SchemaFactory createSchemaFactory();
/**
- * Creates a preconfigured {@link Processor Saxon Processor} for various tasks within the Validator. The validator
+ * Returns a preconfigured {@link Processor Saxon Processor} for various tasks within the Validator. The validator
* leverages the saxon s9api for internal processing e.g. xml reading and writing. So this is the main object to secure
* for reading, transforming and writing xml files.
*
* @return a preconfigured {@link Processor}
*/
- Processor createProcessor();
+ Processor getProcessor();
/**
* Creates a specific implementation for resolving referenced objects in XML files. The URIResolver, it is used for
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
index f81e967..5572ee6 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
@@ -170,9 +170,9 @@ public class ConfigurationBuilder {
public Configuration build() {
final ResolvingConfigurationStrategy resolving = getResolvingConfigurationStrategy();
if (this.processor == null) {
- this.processor = resolving.createProcessor();
+ this.processor = resolving.getProcessor();
}
- final ContentRepository contentRepository = new ContentRepository(this.resolvingConfigurationStrategy, this.repository);
+ final ContentRepository contentRepository = new ContentRepository(resolving, this.repository);
final List list = initializeScenarios(contentRepository);
final Scenario fallbackScenario = initializeFallback(contentRepository);
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java b/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
index cff00fc..00589c8 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
@@ -117,7 +117,7 @@ public class ConfigurationLoader {
public Configuration build() {
final ResolvingConfigurationStrategy resolving = getResolvingConfigurationStrategy();
- final Processor processor = resolving.createProcessor();
+ final Processor processor = resolving.getProcessor();
final ContentRepository contentRepository = new ContentRepository(resolving, getScenarioRepository());
final Scenarios def = loadScenarios(contentRepository.getScenarioSchema(), processor);
diff --git a/src/main/java/de/kosit/validationtool/impl/ContentRepository.java b/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
index 9d69706..6fd7a5b 100644
--- a/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
+++ b/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
@@ -92,7 +92,7 @@ public class ContentRepository {
public ContentRepository(final ResolvingConfigurationStrategy strategy, final URI repository) {
this.repository = repository;
this.resolvingConfigurationStrategy = strategy;
- this.processor = this.resolvingConfigurationStrategy.createProcessor();
+ this.processor = this.resolvingConfigurationStrategy.getProcessor();
this.resolver = this.resolvingConfigurationStrategy.createResolver(repository);
this.schemaFactory = this.resolvingConfigurationStrategy.createSchemaFactory();
}
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java
index 3e5db25..4e7f37b 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java
@@ -58,8 +58,8 @@ public class ComputeAcceptanceAction implements CheckAction {
selector.setContextItem(results.getReport());
results.setAcceptStatus(selector.effectiveBooleanValue() ? AcceptRecommendation.ACCEPTABLE : AcceptRecommendation.REJECT);
} catch (final SaxonApiException e) {
- final String msg = "Error evaluating accept recommendation: %s";
- log.error(msg);
+ final String msg = String.format("Error evaluating accept recommendation: %s", selector.getUnderlyingXPathContext().toString());
+ log.error(msg, e);
results.addProcessingError(msg);
}
}
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java
index fe8c1e3..e8ea274 100644
--- a/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java
+++ b/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java
@@ -13,6 +13,8 @@ import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
+import net.sf.saxon.s9api.Processor;
+
/**
* @author Andreas Penski
*/
@@ -27,6 +29,18 @@ public abstract class BaseResolvingStrategy implements ResolvingConfigurationStr
private static final String ORACLE_XERCES_CLASS = "com.sun.org.apache.xerces.internal.impl.Constants";
+ private Processor processor;
+
+ @Override
+ public Processor getProcessor() {
+ if (this.processor == null) {
+ this.processor = createProcessor();
+ }
+ return this.processor;
+ }
+
+ protected abstract Processor createProcessor();
+
public static void forceOpenJdkXmlImplementation() {
if (!isOpenJdkXmlImplementationAvailable()) {
throw new IllegalStateException("No OpenJDK version of XERCES found");
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java
index 35c19b1..90750b6 100644
--- a/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java
+++ b/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java
@@ -9,8 +9,6 @@ import javax.xml.validation.Validator;
import lombok.extern.slf4j.Slf4j;
-import net.sf.saxon.s9api.Processor;
-
/**
*
*
@@ -30,11 +28,6 @@ public class StrictLocalResolvingStrategy extends StrictRelativeResolvingStrateg
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
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
index 66824d6..bb6aa29 100644
--- a/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
+++ b/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
@@ -77,7 +77,7 @@ public class StrictRelativeResolvingStrategy extends BaseResolvingStrategy {
}
@Override
- public Processor createProcessor() {
+ protected Processor createProcessor() {
final Processor processor = new Processor(false);
// verhindere global im Prinzip alle resolving strategien
final SecureUriResolver resolver = new SecureUriResolver();
diff --git a/src/test/java/de/kosit/validationtool/config/TestConfiguration.java b/src/test/java/de/kosit/validationtool/config/TestConfiguration.java
new file mode 100644
index 0000000..4de67c1
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/config/TestConfiguration.java
@@ -0,0 +1,31 @@
+package de.kosit.validationtool.config;
+
+import java.util.List;
+import java.util.Map;
+
+import lombok.Data;
+
+import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.Scenario;
+
+/**
+ * @author Andreas Penski
+ */
+@Data
+public class TestConfiguration implements Configuration {
+
+ private List scenarios;
+
+ private Scenario fallbackScenario;
+
+ private String author;
+
+ private String name;
+
+ private String date;
+
+ private ContentRepository contentRepository;
+
+ private Map additionalParameters;
+}
diff --git a/src/test/java/de/kosit/validationtool/impl/DocumentParserTest.java b/src/test/java/de/kosit/validationtool/impl/DocumentParseActionTest.java
similarity index 78%
rename from src/test/java/de/kosit/validationtool/impl/DocumentParserTest.java
rename to src/test/java/de/kosit/validationtool/impl/DocumentParseActionTest.java
index 7a39c60..5f98181 100644
--- a/src/test/java/de/kosit/validationtool/impl/DocumentParserTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/DocumentParseActionTest.java
@@ -22,12 +22,14 @@ package de.kosit.validationtool.impl;
import static de.kosit.validationtool.api.InputFactory.read;
import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.impl.tasks.DocumentParseAction;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
import net.sf.saxon.s9api.XdmNode;
@@ -37,14 +39,21 @@ import net.sf.saxon.s9api.XdmNode;
*
* @author Andreas Penski
*/
-public class DocumentParserTest {
+public class DocumentParseActionTest {
@Rule
public ExpectedException exception = ExpectedException.none();
+ private DocumentParseAction action;
+
+ @Before
+ public void setup() {
+ this.action = new DocumentParseAction(Helper.createProcessor());
+ }
+
@Test
public void testSimple() {
- final Result result = Helper.parseDocument(read(Simple.SIMPLE_VALID));
+ final Result result = this.action.parseDocument(read(Simple.SIMPLE_VALID));
assertThat(result).isNotNull();
assertThat(result.getObject()).isNotNull();
assertThat(result.getErrors()).isEmpty();
@@ -53,7 +62,7 @@ public class DocumentParserTest {
@Test
public void testIllformed() {
- final Result result = Helper.parseDocument(read(Simple.NOT_WELLFORMED));
+ final Result result = this.action.parseDocument(read(Simple.NOT_WELLFORMED));
assertThat(result).isNotNull();
assertThat(result.getErrors()).isNotEmpty();
assertThat(result.getObject()).isNull();
@@ -63,7 +72,7 @@ public class DocumentParserTest {
@Test
public void testNullInput() {
this.exception.expect(IllegalArgumentException.class);
- Helper.parseDocument(null);
+ this.action.parseDocument(null);
}
diff --git a/src/test/java/de/kosit/validationtool/impl/Helper.java b/src/test/java/de/kosit/validationtool/impl/Helper.java
index 941429d..735f9a3 100644
--- a/src/test/java/de/kosit/validationtool/impl/Helper.java
+++ b/src/test/java/de/kosit/validationtool/impl/Helper.java
@@ -43,6 +43,7 @@ import de.kosit.validationtool.impl.tasks.DocumentParseAction;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
import net.sf.saxon.dom.NodeOverNodeInfo;
+import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XdmNode;
@@ -161,7 +162,20 @@ public class Helper {
return serialize((Document) NodeOverNodeInfo.wrap(node.getUnderlyingNode()));
}
+ public static Result parseDocument(final Processor processor, final Input input) {
+ return new DocumentParseAction(processor).parseDocument(input);
+ }
+
public static Result parseDocument(final Input input) {
- return new DocumentParseAction(TestObjectFactory.createProcessor()).parseDocument(input);
+ return new DocumentParseAction(getTestProcessor()).parseDocument(input);
+ }
+
+ public static Processor getTestProcessor() {
+ // is always the same at the moment
+ return createProcessor();
+ }
+
+ public static Processor createProcessor() {
+ return ResolvingMode.STRICT_RELATIVE.getStrategy().getProcessor();
}
}
diff --git a/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java b/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
index cb11310..9cf456f 100644
--- a/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/ScenarioRepositoryTest.java
@@ -26,22 +26,17 @@ import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import lombok.Data;
-
-import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.config.TestConfiguration;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.model.scenarios.ScenarioType;
-import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.XPathExecutable;
import net.sf.saxon.s9api.XdmNode;
@@ -53,45 +48,25 @@ import net.sf.saxon.s9api.XdmNode;
public class ScenarioRepositoryTest {
- @Data
- private static class DummyConfiguration implements Configuration {
-
- private List scenarios;
-
- private Scenario fallbackScenario;
-
- private String author;
-
- private String name;
-
- private String date;
-
- private Processor processor;
-
- private ContentRepository contentRepository;
-
- private Map additionalParameters;
-
- }
-
@Rule
public ExpectedException expectedException = ExpectedException.none();
private ScenarioRepository repository;
- private DummyConfiguration configInstance;
+ private TestConfiguration configInstance;
@Before
public void setup() {
- final Scenario s = createScenario();
+ this.configInstance = new TestConfiguration();
+ this.configInstance.setContentRepository(new ContentRepository(ResolvingMode.STRICT_RELATIVE.getStrategy(), null));
- this.configInstance = new DummyConfiguration();
+ final Scenario s = createScenario();
this.configInstance.setScenarios(new ArrayList<>());
this.configInstance.getScenarios().add(s);
this.repository = new ScenarioRepository(this.configInstance);
}
- private static Scenario createScenario() {
+ private Scenario createScenario() {
final Scenario s = new Scenario(new ScenarioType());
s.setMatchExecutable(createXpath("//*:name"));
return s;
@@ -134,11 +109,11 @@ public class ScenarioRepositoryTest {
assertThat(scenario.getObject().getName()).isEqualTo("fallback");
}
- private static XdmNode load(final URI uri) throws IOException {
- return Helper.parseDocument(read(uri.toURL())).getObject();
+ private XdmNode load(final URI uri) throws IOException {
+ return Helper.parseDocument(this.configInstance.getContentRepository().getProcessor(), read(uri.toURL())).getObject();
}
- private static XPathExecutable createXpath(final String expression) {
- return new ContentRepository(ResolvingMode.STRICT_RELATIVE.getStrategy(), null).createXPath(expression, new HashMap<>());
+ private XPathExecutable createXpath(final String expression) {
+ return this.configInstance.getContentRepository().createXPath(expression, new HashMap<>());
}
}
From d0000fc698b3223c3feaa44b4a7027fe9b396d81 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Wed, 29 Apr 2020 16:57:15 +0200
Subject: [PATCH 09/36] get rid of ObjectFactory.java
---
.idea/compiler.xml | 1 +
.idea/encodings.xml | 1 +
.../model/daemon/ObjectFactory.java | 80 +++++++
.../cmd/ExtractHtmlContentAction.java | 2 +-
.../config/ConfigurationBuilder.java | 34 +++
.../config/ConfigurationLoader.java | 2 +
.../de/kosit/validationtool/config/Keys.java | 20 ++
.../validationtool/daemon/BaseHandler.java | 34 +++
...tpServerHandler.java => CheckHandler.java} | 40 +++-
.../validationtool/daemon/ConfigHandler.java | 77 +++++++
.../kosit/validationtool/daemon/Daemon.java | 71 +-----
.../validationtool/daemon/HealthHandler.java | 148 +++----------
.../validationtool/impl/DateFactory.java | 25 +++
.../validationtool/impl/DefaultCheck.java | 17 +-
.../validationtool/impl/ObjectFactory.java | 205 ------------------
src/main/model/binding/global.xjb | 9 +-
src/main/model/xsd/daemon.xsd | 42 ++++
.../de/kosit/validationtool/impl/Helper.java | 21 +-
.../impl/SaxonSecurityTest.java | 20 +-
.../impl/TestObjectFactory.java | 90 +++++++-
20 files changed, 494 insertions(+), 445 deletions(-)
create mode 100644 src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java
create mode 100644 src/main/java/de/kosit/validationtool/config/Keys.java
create mode 100644 src/main/java/de/kosit/validationtool/daemon/BaseHandler.java
rename src/main/java/de/kosit/validationtool/daemon/{HttpServerHandler.java => CheckHandler.java} (53%)
create mode 100644 src/main/java/de/kosit/validationtool/daemon/ConfigHandler.java
create mode 100644 src/main/java/de/kosit/validationtool/impl/DateFactory.java
delete mode 100644 src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
create mode 100644 src/main/model/xsd/daemon.xsd
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index a26cfb6..9f64100 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -2,6 +2,7 @@
+
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index 63fc954..ecaea3d 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -2,6 +2,7 @@
+
diff --git a/src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java b/src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java
new file mode 100644
index 0000000..548fd2d
--- /dev/null
+++ b/src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java
@@ -0,0 +1,80 @@
+//
+// Diese Datei wurde mit der JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0 generiert
+// Siehe https://javaee.github.io/jaxb-v2/
+// Änderungen an dieser Datei gehen bei einer Neukompilierung des Quellschemas verloren.
+// Generiert: 2020.04.29 um 03:45:08 PM CEST
+//
+
+
+package de.kosit.validationtool.model.daemon;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.annotation.XmlElementDecl;
+import javax.xml.bind.annotation.XmlRegistry;
+import javax.xml.namespace.QName;
+
+
+/**
+ * This object contains factory methods for each
+ * Java content interface and Java element interface
+ * generated in the de.xoev.de.validator.framework._1.daemon package.
+ * An ObjectFactory allows you to programatically
+ * construct new instances of the Java representation
+ * for XML content. The Java representation of XML
+ * content can consist of schema derived interfaces
+ * and classes representing the binding of schema
+ * type definitions, element declarations and model
+ * groups. Factory methods for each of these are
+ * provided in this class.
+ *
+ */
+@XmlRegistry
+public class ObjectFactory {
+
+ private final static QName _Health_QNAME = new QName("http://www.xoev.de/de/validator/framework/1/daemon", "health");
+
+ /**
+ * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: de.xoev.de.validator.framework._1.daemon
+ *
+ */
+ public ObjectFactory() {
+ }
+
+ /**
+ * Create an instance of {@link HealthType }
+ *
+ */
+ public static HealthType createHealthType() {
+ return new HealthType();
+ }
+
+ /**
+ * Create an instance of {@link ApplicationType }
+ *
+ */
+ public static ApplicationType createApplicationType() {
+ return new ApplicationType();
+ }
+
+ /**
+ * Create an instance of {@link MemoryType }
+ *
+ */
+ public static MemoryType createMemoryType() {
+ return new MemoryType();
+ }
+
+ /**
+ * Create an instance of {@link JAXBElement }{@code <}{@link HealthType }{@code >}
+ *
+ * @param value
+ * Java instance representing xml element's value.
+ * @return
+ * the new instance of {@link JAXBElement }{@code <}{@link HealthType }{@code >}
+ */
+ @XmlElementDecl(namespace = "http://www.xoev.de/de/validator/framework/1/daemon", name = "health")
+ public static JAXBElement createHealth(final HealthType value) {
+ return new JAXBElement(_Health_QNAME, HealthType.class, null, value);
+ }
+
+}
diff --git a/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java b/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java
index 4c472d6..537a3be 100644
--- a/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java
+++ b/src/main/java/de/kosit/validationtool/cmd/ExtractHtmlContentAction.java
@@ -71,7 +71,7 @@ class ExtractHtmlContentAction implements CheckAction {
log.info("Writing report html '{}' to {}", name, file.toAbsolutePath());
serializer.serializeNode(node);
} catch (final SaxonApiException e) {
- log.info("Error extracting html content to {}", file.toAbsolutePath(), e);
+ log.error("Error extracting html content to {}", file.toAbsolutePath(), e);
}
}
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
index 5572ee6..d6e91f5 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
@@ -1,5 +1,7 @@
package de.kosit.validationtool.config;
+import static de.kosit.validationtool.impl.DateFactory.createTimestamp;
+
import java.net.URI;
import java.time.LocalDate;
import java.util.ArrayList;
@@ -12,6 +14,7 @@ import java.util.stream.Collectors;
import javax.xml.validation.Schema;
import org.apache.commons.lang3.NotImplementedException;
+import org.apache.commons.lang3.StringUtils;
import lombok.extern.slf4j.Slf4j;
@@ -21,6 +24,10 @@ import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.ResolvingMode;
import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.model.scenarios.DescriptionType;
+import de.kosit.validationtool.model.scenarios.NoScenarioReportType;
+import de.kosit.validationtool.model.scenarios.ObjectFactory;
+import de.kosit.validationtool.model.scenarios.Scenarios;
import net.sf.saxon.s9api.Processor;
@@ -52,6 +59,8 @@ public class ConfigurationBuilder {
private URI repository;
+ private String description;
+
public ConfigurationBuilder author(final String authorName) {
this.author = authorName;
return this;
@@ -84,6 +93,11 @@ public class ConfigurationBuilder {
return this;
}
+ public ConfigurationBuilder description(final String description) {
+ this.description = description;
+ return this;
+ }
+
/**
* Create a fallback scenario configuration.
*
@@ -182,9 +196,29 @@ public class ConfigurationBuilder {
configuration.setDate(this.date);
configuration.setName(this.name);
configuration.setContentRepository(contentRepository);
+ configuration.getAdditionalParameters().put(Keys.SCENARIO_DEFINITION, createDefinition(configuration));
return (configuration);
}
+ private Scenarios createDefinition(final DefaultConfiguration configuration) {
+ final Scenarios s = new Scenarios();
+ s.setAuthor(configuration.getAuthor());
+ s.setDate(createTimestamp());
+ final DescriptionType d = new DescriptionType();
+ d.getPOrOlOrUl().add(new ObjectFactory().createDescriptionTypeP(StringUtils.defaultIfBlank(this.description, "")));
+ s.setDescription(d);
+ s.setName(configuration.getName());
+ s.getScenario().addAll(configuration.getScenarios().stream().map(Scenario::getConfiguration).collect(Collectors.toList()));
+ s.setNoScenarioReport(createNoScenarioReportType(configuration.getFallbackScenario()));
+ return s;
+ }
+
+ private static NoScenarioReportType createNoScenarioReportType(final Scenario fallbackScenario) {
+ final NoScenarioReportType no = new NoScenarioReportType();
+ no.setResource(fallbackScenario.getConfiguration().getCreateReport().getResource());
+ return no;
+ }
+
private Scenario initializeFallback(final ContentRepository contentRepository) {
if (this.fallbackBuilder == null) {
throw new IllegalStateException("No fallback configuration specified");
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java b/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
index 00589c8..684695d 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
@@ -129,6 +129,8 @@ public class ConfigurationLoader {
configuration.setDate(def.getDate().toString());
configuration.setName(def.getName());
configuration.setContentRepository(contentRepository);
+ configuration.getAdditionalParameters().put(Keys.SCENARIOS_FILE, this.scenarioDefinition);
+ configuration.getAdditionalParameters().put(Keys.SCENARIO_DEFINITION, def);
return (configuration);
}
diff --git a/src/main/java/de/kosit/validationtool/config/Keys.java b/src/main/java/de/kosit/validationtool/config/Keys.java
new file mode 100644
index 0000000..6670e71
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/config/Keys.java
@@ -0,0 +1,20 @@
+package de.kosit.validationtool.config;
+
+/**
+ * Defines some keys used for supplying additional parameters internally.
+ *
+ * @author Andreas Penski
+ */
+public class Keys {
+
+ /**
+ * The actual scenarios file location as used with {@link ConfigurationLoader}.
+ */
+ public static final String SCENARIOS_FILE = "scenarios_file";
+
+ /**
+ * The actual scenarios configuration represented as serializable tree. This either loaded from file or build manually
+ * via {@link ConfigurationBuilder}
+ */
+ public static final String SCENARIO_DEFINITION = "scenario_definition";
+}
diff --git a/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java b/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java
new file mode 100644
index 0000000..014652f
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java
@@ -0,0 +1,34 @@
+package de.kosit.validationtool.daemon;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+/**
+ * Simple base implemenation for http handlers. Doing I/O stuff.
+ *
+ * @author Andreas Penski
+ */
+public abstract class BaseHandler implements HttpHandler {
+
+ protected static final String APPLICATION_XML = "application/xml";
+
+ protected static void write(final HttpExchange exchange, final byte[] content, final String contentType) throws IOException {
+ final OutputStream os = exchange.getResponseBody();
+ exchange.getResponseHeaders().add("Content-Type", contentType);
+ exchange.sendResponseHeaders(200, content.length);
+ os.write(content);
+ os.close();
+ }
+
+ protected static void error(final HttpExchange httpExchange, final int statusCode, final String message) throws IOException {
+ final byte[] bytes = message.getBytes();
+ httpExchange.sendResponseHeaders(statusCode, bytes.length);
+ final OutputStream os = httpExchange.getResponseBody();
+ os.write(bytes);
+ os.close();
+ }
+
+}
diff --git a/src/main/java/de/kosit/validationtool/daemon/HttpServerHandler.java b/src/main/java/de/kosit/validationtool/daemon/CheckHandler.java
similarity index 53%
rename from src/main/java/de/kosit/validationtool/daemon/HttpServerHandler.java
rename to src/main/java/de/kosit/validationtool/daemon/CheckHandler.java
index 126f7e4..1476d8c 100644
--- a/src/main/java/de/kosit/validationtool/daemon/HttpServerHandler.java
+++ b/src/main/java/de/kosit/validationtool/daemon/CheckHandler.java
@@ -1,32 +1,37 @@
package de.kosit.validationtool.daemon;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicLong;
import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.Check;
import de.kosit.validationtool.api.InputFactory;
+import de.kosit.validationtool.api.Result;
import de.kosit.validationtool.impl.input.SourceInput;
+import net.sf.saxon.s9api.Processor;
+import net.sf.saxon.s9api.SaxonApiException;
+import net.sf.saxon.s9api.Serializer;
+
/**
* Wir benötigen einen Handler, der zur Verarbeitung von HTTP-Anforderungen aufgerufen wird um hier die Verarbeitung des
* POST Request zu realisieren.
*/
@Slf4j
-class HttpServerHandler implements HttpHandler {
+@RequiredArgsConstructor
+class CheckHandler extends BaseHandler {
private static final AtomicLong counter = new AtomicLong(0);
private final Check implemenation;
- HttpServerHandler(final Check check) {
- this.implemenation = check;
- }
+ private final Processor processor;
/**
* Methode, die eine gegebene Anforderung verarbeitet und eine entsprechende Antwort generiert
@@ -41,20 +46,31 @@ class HttpServerHandler implements HttpHandler {
final String requestMethod = httpExchange.getRequestMethod();
if (requestMethod.equals("POST")) {
final InputStream inputStream = httpExchange.getRequestBody();
-
if (inputStream.available() > 0) {
- final SourceInput serverInput = (SourceInput) InputFactory.read(inputStream, "Prüfling" + counter.incrementAndGet());
- Daemon.writeOutputstreamArray(httpExchange, this.implemenation.check(serverInput));
+ final SourceInput serverInput = (SourceInput) InputFactory.read(inputStream,
+ "supplied_instance_" + counter.incrementAndGet());
+ final Result result = this.implemenation.checkInput(serverInput);
+ write(httpExchange, serialize(result), APPLICATION_XML);
} else {
- Daemon.writeError(httpExchange, 400, "XML-Inhalt erforderlich!");
+ error(httpExchange, 400, "No content supplied");
}
} else {
- Daemon.writeError(httpExchange, 405, "Es ist nur die POST-Methode erlaubt!");
+ error(httpExchange, 405, "Method not supported");
}
} catch (final Exception e) {
- Daemon.writeError(httpExchange, 500, "Interner Fehler bei der Verarbeitung des Requests: " + e.getMessage());
- log.error("Es ist ein Fehler aufgetreten. Das Dokument kann nicht geprüft werden", e);
+ error(httpExchange, 500, "Internal error: " + e.getMessage());
+ }
+ }
+
+ private byte[] serialize(final Result result) {
+ try ( final ByteArrayOutputStream out = new ByteArrayOutputStream() ) {
+ final Serializer serializer = this.processor.newSerializer(out);
+ serializer.serializeNode(result.getReport());
+ return out.toByteArray();
+ } catch (final SaxonApiException | IOException e) {
+ log.error("Error serializing result", e);
+ throw new IllegalStateException("Can not serialize result", e);
}
}
diff --git a/src/main/java/de/kosit/validationtool/daemon/ConfigHandler.java b/src/main/java/de/kosit/validationtool/daemon/ConfigHandler.java
new file mode 100644
index 0000000..57b71ae
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/daemon/ConfigHandler.java
@@ -0,0 +1,77 @@
+package de.kosit.validationtool.daemon;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.net.URI;
+import java.util.Optional;
+
+import org.apache.commons.io.IOUtils;
+
+import com.sun.net.httpserver.HttpExchange;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.config.Keys;
+import de.kosit.validationtool.impl.ConversionService;
+import de.kosit.validationtool.model.scenarios.Scenarios;
+
+/**
+ * Handler that returns the actual configuration used for this daemon instance.
+ *
+ * @author Andreas Penski
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class ConfigHandler extends BaseHandler {
+
+ private final Configuration configuration;
+
+ private final ConversionService conversionService;
+
+ @Override
+ public void handle(final HttpExchange exchange) throws IOException {
+ try {
+ final Optional xml = getSource();
+ if (xml.isPresent()) {
+ write(exchange, xml.get().getBytes(), APPLICATION_XML);
+ } else {
+ error(exchange, 404, "No configuration found");
+ }
+ } catch (final Exception e) {
+ log.error("Error grabbing configuration", e);
+ error(exchange, 500, "Error grabbing configuration: " + e.getMessage());
+ }
+ }
+
+ private Optional getSource() {
+ final URI fileUri = (URI) this.configuration.getAdditionalParameters().get(Keys.SCENARIOS_FILE);
+ return fileUri != null ? loadFile(fileUri) : loadFromConfig();
+ }
+
+ private static Optional loadFile(final URI fileUri) {
+ try ( final Reader in = new InputStreamReader(fileUri.toURL().openStream());
+ final StringWriter out = new StringWriter() ) {
+ IOUtils.copy(in, out);
+ return Optional.of(out.toString());
+ } catch (final IOException e) {
+ return Optional.empty();
+ }
+ }
+
+ private Optional loadFromConfig() {
+ final Optional result;
+ final Scenarios scenarios = (Scenarios) this.configuration.getAdditionalParameters().get(Keys.SCENARIO_DEFINITION);
+ if (scenarios != null) {
+ final String s = this.conversionService.writeXml(scenarios);
+ result = Optional.of(s);
+ } else {
+ result = Optional.empty();
+ }
+ return result;
+ }
+
+}
diff --git a/src/main/java/de/kosit/validationtool/daemon/Daemon.java b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
index dc4ca80..6eeda08 100644
--- a/src/main/java/de/kosit/validationtool/daemon/Daemon.java
+++ b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
@@ -1,19 +1,9 @@
package de.kosit.validationtool.daemon;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-
-import org.w3c.dom.Document;
-
-import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;
import lombok.Getter;
@@ -22,8 +12,9 @@ import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.Configuration;
+import de.kosit.validationtool.impl.ConversionService;
import de.kosit.validationtool.impl.DefaultCheck;
-import de.kosit.validationtool.impl.ObjectFactory;
+import de.kosit.validationtool.model.daemon.HealthType;
/**
* HTTP-Daemon für die Bereitstellung der Prüf-Funktionalität via http.
@@ -42,56 +33,6 @@ public class Daemon {
private final int threadCount;
- /**
- * Methode, die die Antwort als String-Text schreibt
- *
- * @param httpExchange um den Antwort Body zu erhalten
- * @param rCode der Code-Status
- * @param response die String antwort, die ich anzeigen möchte
- */
- static void writeError(final HttpExchange httpExchange, final int rCode, final String response) throws IOException {
- httpExchange.sendResponseHeaders(rCode, response.length());
- final OutputStream os = httpExchange.getResponseBody();
- os.write(response.getBytes());
- os.close();
- }
-
- /**
- * Methode, die die Antwort als String-Text schreibt
- *
- * @param httpExchange um den Antwort Body zu erhalten
- * @param doc der Report
- */
- static void writeOutputstreamArray(final HttpExchange httpExchange, final Document doc)
- throws IOException, TransformerException {
- final byte[] bytes = serialize(doc);
- final OutputStream os = httpExchange.getResponseBody();
- httpExchange.getResponseHeaders().add("Content-Type", "application/xml");
- httpExchange.sendResponseHeaders(200, bytes.length);
- os.write(bytes);
- os.close();
- log.debug("Xml File erzeugen ist Fertig ");
- }
-
- /**
- * Methode zum Serialisieren des Dokuments.
- *
- * @param report Vom Typ Dokument, aka Report .
- */
- static byte[] serialize(final Document report) throws TransformerException {
-
- try ( final ByteArrayOutputStream bArrayOS = new ByteArrayOutputStream() ) {
- final DOMSource source = new DOMSource(report);
- final StreamResult streamResult = new StreamResult(bArrayOS);
- final Transformer transformer = ObjectFactory.createTransformer(true);
- transformer.transform(source, streamResult);
- return bArrayOS.toByteArray();
- } catch (final IOException e) {
- log.error("Report {}", e.getMessage(), e);
- throw new IllegalStateException(e);
- }
- }
-
/**
* Methode zum Starten des Servers
*
@@ -100,10 +41,14 @@ public class Daemon {
public void startServer(final Configuration config) {
HttpServer server = null;
try {
+ final ConversionService converter = new ConversionService();
+ converter.initialize(HealthType.class.getPackage());
+
server = HttpServer.create(new InetSocketAddress(this.hostName, this.port), 0);
final DefaultCheck check = new DefaultCheck(config);
- server.createContext("/", new HttpServerHandler(check));
- server.createContext("/health", new HealthHandler(config));
+ server.createContext("/", new CheckHandler(check, config.getContentRepository().getProcessor()));
+ server.createContext("/server/health", new HealthHandler(config, converter));
+ server.createContext("/server/config", new ConfigHandler(config, new ConversionService()));
server.setExecutor(Executors.newFixedThreadPool(this.threadCount));
server.start();
log.info("Server unter Port {} ist erfolgreich gestartet", this.port);
diff --git a/src/main/java/de/kosit/validationtool/daemon/HealthHandler.java b/src/main/java/de/kosit/validationtool/daemon/HealthHandler.java
index 28b1ff7..d11dabc 100644
--- a/src/main/java/de/kosit/validationtool/daemon/HealthHandler.java
+++ b/src/main/java/de/kosit/validationtool/daemon/HealthHandler.java
@@ -2,140 +2,50 @@ package de.kosit.validationtool.daemon;
import java.io.IOException;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.transform.TransformerException;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-
import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.Configuration;
-import de.kosit.validationtool.impl.ObjectFactory;
+import de.kosit.validationtool.impl.ConversionService;
+import de.kosit.validationtool.model.daemon.HealthType;
+import de.kosit.validationtool.model.daemon.MemoryType;
/**
- * Wir benötigen einen Handler, der zur Verarbeitung von HTTP-Anforderungen aufgerufen wird , und hier für Verarbeitung
- * das GET Request um Health-Endpunkt zu erstellen. Die Klasse HealthHandler implementiert diese Schnittstelle
+ * Handler that implements a simple health check. Useful for monitoring the service.
+ *
+ * @author Andreas Penski`
*/
@Slf4j
-class HealthHandler implements HttpHandler {
+@RequiredArgsConstructor
+class HealthHandler extends BaseHandler {
- /**
- * Klasse zur Erzeugung Health Xml , die optiamle Status.
- *
- * @author Roula Antoun
- */
- @Slf4j
- static class Health {
-
- private final long freeMemory;
-
- private final long maxMemory;
-
- private final long totalMemory;
-
- private final Configuration config;
-
- Health(final Configuration config) {
-
- final Runtime runtime = Runtime.getRuntime();
- this.freeMemory = runtime.freeMemory();
- this.maxMemory = runtime.maxMemory();
- this.totalMemory = runtime.totalMemory();
- this.config = config;
- }
-
- /**
- * Methode, die schreibt das Health Xml für optimale Status
- *
- */
- Document writeHealthXml() {
- final DocumentBuilder dBuilder = ObjectFactory.createDocumentBuilder(false);
- final Document doc = dBuilder.newDocument();
- final Element rootElement = doc.createElementNS("https://localhost:8080/Health", "Health");
- doc.appendChild(rootElement);
- rootElement.appendChild(getMemory(doc, this.freeMemory, this.maxMemory, this.totalMemory));
- rootElement.appendChild(getState(doc));
- rootElement.appendChild(getScenario(doc, this.config));
- return doc;
- }
-
- /**
- * Methode, die schreibt das System Status Node im Xml File
- *
- * @param doc Vom Typ Dokument.
- *
- */
- private static Node getState(final Document doc) {
- final Element state = doc.createElement("state");
- state.setAttribute("indicator", "OK");
- final Element stateNode = doc.createElement("message");
- stateNode.appendChild(doc.createTextNode("System is up and running normally"));
- state.appendChild(stateNode);
- return state;
- }
-
- /**
- * Methode, die schreibt das Scnarios Information Node im Xml File
- *
- * @param doc Vom Typ Dokument .
- * @param config Vom Typ {@link Configuration} das verwendete scenario.
- *
- */
- private static Node getScenario(final Document doc, final Configuration config) {
- final Element scenario = doc.createElement("scenario");
- final Element scenarioNameNode = doc.createElement("name");
- scenarioNameNode.appendChild(doc.createTextNode(config.getName()));
- scenario.appendChild(scenarioNameNode);
- return scenario;
- }
-
- /**
- * Methode, die schreibt das Scnarios Information Node im Xml File
- *
- * @param doc Vom Typ Dokument .
- * @param freeMemory Vom Typ long , der freier Speicher.
- * @param maxMemory Vom Typ long , der maximaler Speicher
- * @param totalMemory Vom Typ long , der Gesamte speicher.
- *
- */
- private static Node getMemory(final Document doc, final long freeMemory, final long maxMemory, final long totalMemory) {
- final Element memory = doc.createElement("memoryState");
- final String freeM = Long.toString(freeMemory);
- final Element freeMNode = doc.createElement("freeMemory");
- freeMNode.appendChild(doc.createTextNode(freeM));
- memory.appendChild(freeMNode);
- final String maxM = Long.toString(maxMemory);
- final Element maxMNode = doc.createElement("maxMemory");
- maxMNode.appendChild(doc.createTextNode(maxM));
- memory.appendChild(maxMNode);
- final String totalM = Long.toString(totalMemory);
- final Element totalMNode = doc.createElement("totalMemory");
- totalMNode.appendChild(doc.createTextNode(totalM));
- memory.appendChild(totalMNode);
- return memory;
- }
- }
private final Configuration scenarios;
- HealthHandler(final Configuration config) {
- this.scenarios = config;
- }
+ private final ConversionService conversionService;
@Override
public void handle(final HttpExchange httpExchange) throws IOException {
- final Health health = new Health(this.scenarios);
- final Document doc = health.writeHealthXml();
- try {
- Daemon.writeOutputstreamArray(httpExchange, doc);
- } catch (final TransformerException e) {
- Daemon.writeError(httpExchange, 500, e.getMessage());
- log.error("Fehler beim Erzeugen der Status-Information", e);
- }
+ final HealthType health = createHealth();
+ final String xml = this.conversionService.writeXml(health);
+ write(httpExchange, xml.getBytes(), APPLICATION_XML);
+
+ }
+
+ private static HealthType createHealth() {
+ final HealthType h = new HealthType();
+ h.setMemory(createMemory());
+ return h;
+ }
+
+ private static MemoryType createMemory() {
+ final MemoryType m = new MemoryType();
+ final Runtime runtime = Runtime.getRuntime();
+ m.setFreeMemory(runtime.freeMemory());
+ m.setMaxMemory(runtime.maxMemory());
+ m.setTotalMemory(runtime.totalMemory());
+ return m;
}
}
diff --git a/src/main/java/de/kosit/validationtool/impl/DateFactory.java b/src/main/java/de/kosit/validationtool/impl/DateFactory.java
new file mode 100644
index 0000000..4665c97
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/impl/DateFactory.java
@@ -0,0 +1,25 @@
+package de.kosit.validationtool.impl;
+
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+
+/**
+ * @author Andreas Penski
+ */
+public class DateFactory {
+
+ 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);
+ }
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java
index b337854..f87fb00 100644
--- a/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java
+++ b/src/main/java/de/kosit/validationtool/impl/DefaultCheck.java
@@ -19,17 +19,13 @@
package de.kosit.validationtool.impl;
+import static de.kosit.validationtool.impl.DateFactory.createTimestamp;
+
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;
@@ -103,16 +99,7 @@ public class DefaultCheck implements Check {
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) {
diff --git a/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java b/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
deleted file mode 100644
index f1917ba..0000000
--- a/src/main/java/de/kosit/validationtool/impl/ObjectFactory.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Licensed to the Koordinierungsstelle für IT-Standards (KoSIT) under
- * one or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. KoSIT licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package de.kosit.validationtool.impl;
-
-import java.io.Reader;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Result;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerConfigurationException;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-
-import lombok.extern.slf4j.Slf4j;
-
-import net.sf.saxon.Configuration;
-import net.sf.saxon.expr.XPathContext;
-import net.sf.saxon.lib.CollectionFinder;
-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;
-
-/**
- * Eine Factory für häufig verwendete Objekte mit XML. Zentralisiert die XML Security Konfiguration. Die Konfiguration
- * basiert auf den OWASP-Empfehlungen .
- *
- * Diese Klasse ist stark abhängig von der Verwendung eines Oracle JDK. Alternative JDKs haben u.U. eine andere SAX- /
- * StAX- / XML-Implementierug und profitieren entsprechend NICHT von den hier getroffenen Einstellungen.
- *
- * @author Andreas Penski
- */
-@Slf4j
-@Deprecated
-public class ObjectFactory {
-
- 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);
- }
- }
- private static final String ORACLE_XERCES_CLASS = "com.sun.org.apache.xerces.internal.impl.Constants";
- private static final String DISSALLOW_DOCTYPE_DECL_FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
- private static final String LOAD_EXTERNAL_DTD_FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
- private static final String FEATURE_SECURE_PROCESSING = "http://javax.xml.XMLConstants/feature/secure-processing";
- private static Processor processor;
-
- static {
- try {
- Class.forName(ORACLE_XERCES_CLASS);
- } 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");
- }
- }
-
- ObjectFactory() {
- // hide, it's a factory
- }
-
- private static DocumentBuilderFactory createDocumentBuilderFactory(final boolean validating) {
- final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- try {
- dbf.setValidating(validating);
- dbf.setNamespaceAware(true);
-
- // explicitly enable secure processing
- dbf.setFeature(FEATURE_SECURE_PROCESSING, true);
-
- // This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented
- dbf.setFeature(DISSALLOW_DOCTYPE_DECL_FEATURE, true);
-
- // Disable external DTDs as well
- dbf.setFeature(LOAD_EXTERNAL_DTD_FEATURE, false);
-
- // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
- dbf.setXIncludeAware(false);
- dbf.setExpandEntityReferences(false);
- return dbf;
- } catch (final ParserConfigurationException e) {
- throw new IllegalStateException("Can not create DocumentBuilderFactory due to underlying configuration error", e);
- }
-
- }
-
- /**
- * Transformer für die Ausgabe. Nutzt nicht Saxon!
- *
- * @param prettyPrint pretty-printing der Ausgabe
- * @return einen vorkonfigurierten Transformer
- */
- public static Transformer createTransformer(final boolean prettyPrint) {
- Transformer transformer = null;
- try {
- 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) {
- throw new IllegalStateException("Can not create Transformer due to underlying configuration error", e);
- }
- }
-
-
- public static DocumentBuilder createDocumentBuilder(final boolean validating) {
- try {
- return createDocumentBuilderFactory(validating).newDocumentBuilder();
- } catch (final ParserConfigurationException e) {
- throw new IllegalStateException("Can not create DocumentFactory due to underlying configuration error", e);
- }
- }
-
- 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);
- }
- }
-
- public static Processor createProcessor() {
- if (processor == null) {
- 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(FeatureKeys.DTD_VALIDATION, false);
- processor.setConfigurationProperty(FeatureKeys.ENTITY_RESOLVER_CLASS, "");
- processor.setConfigurationProperty(FeatureKeys.XINCLUDE, false);
- processor.setConfigurationProperty(FeatureKeys.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;
- }
-
-
-
-}
diff --git a/src/main/model/binding/global.xjb b/src/main/model/binding/global.xjb
index 33eced9..be1e39c 100644
--- a/src/main/model/binding/global.xjb
+++ b/src/main/model/binding/global.xjb
@@ -45,15 +45,18 @@
-
+
-
-
de.kosit.validationtool.impl.model.BaseOutput
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/model/xsd/daemon.xsd b/src/main/model/xsd/daemon.xsd
new file mode 100644
index 0000000..5d1186a
--- /dev/null
+++ b/src/main/model/xsd/daemon.xsd
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/de/kosit/validationtool/impl/Helper.java b/src/test/java/de/kosit/validationtool/impl/Helper.java
index 735f9a3..f06d755 100644
--- a/src/test/java/de/kosit/validationtool/impl/Helper.java
+++ b/src/test/java/de/kosit/validationtool/impl/Helper.java
@@ -28,23 +28,17 @@ import java.net.URISyntaxException;
import java.net.URL;
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 org.w3c.dom.Document;
-
import de.kosit.validationtool.api.Input;
import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.impl.tasks.DocumentParseAction;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
-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.XdmNode;
/**
@@ -148,20 +142,17 @@ public class Helper {
new File("src/test/resources/examples/repository").toURI());
}
- public static String serialize(final Document doc) {
+ public static String serialize(final XdmNode node) {
try ( final StringWriter writer = new StringWriter() ) {
- final Transformer transformer = TestObjectFactory.createTransformer(true);
- transformer.transform(new DOMSource(doc), new StreamResult(writer));
+ final Processor processor = Helper.getTestProcessor();
+ final Serializer serializer = processor.newSerializer(writer);
+ serializer.serializeNode(node);
return writer.toString();
- } catch (final IOException | TransformerException e) {
+ } catch (final SaxonApiException | IOException e) {
throw new IllegalStateException("Can not serialize document", e);
}
}
- public static String serialize(final XdmNode node) {
- return serialize((Document) NodeOverNodeInfo.wrap(node.getUnderlyingNode()));
- }
-
public static Result parseDocument(final Processor processor, final Input input) {
return new DocumentParseAction(processor).parseDocument(input);
}
diff --git a/src/test/java/de/kosit/validationtool/impl/SaxonSecurityTest.java b/src/test/java/de/kosit/validationtool/impl/SaxonSecurityTest.java
index 1c79f51..70ac28c 100644
--- a/src/test/java/de/kosit/validationtool/impl/SaxonSecurityTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/SaxonSecurityTest.java
@@ -26,12 +26,11 @@ import java.io.IOException;
import java.net.URL;
import java.util.stream.Collectors;
-import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
-import org.w3c.dom.Document;
import lombok.extern.slf4j.Slf4j;
@@ -41,9 +40,9 @@ import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.impl.xml.RelativeUriResolver;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
-import net.sf.saxon.s9api.DOMDestination;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
+import net.sf.saxon.s9api.XdmDestination;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.s9api.XsltCompiler;
import net.sf.saxon.s9api.XsltExecutable;
@@ -67,19 +66,18 @@ public class SaxonSecurityTest {
final XsltCompiler compiler = p.newXsltCompiler();
final RelativeUriResolver resolver = new RelativeUriResolver(Simple.REPOSITORY_URI);
compiler.setURIResolver(resolver);
- final XsltExecutable exetuable = compiler.compile(new StreamSource(resource.openStream()));
- final XsltTransformer transformer = exetuable.load();
- final Document document = TestObjectFactory.createDocumentBuilder(false).newDocument();
- document.createElement("root");
- final Document result = TestObjectFactory.createDocumentBuilder(false).newDocument();
+ final XsltExecutable executable = compiler.compile(new StreamSource(resource.openStream()));
+ final XsltTransformer transformer = executable.load();
+ final Source document = InputFactory.read(" ".getBytes(), "dummy").getSource();
// transformer.getUnderlyingController().setUnparsedTextURIResolver(resolver);
transformer.setURIResolver(resolver);
- transformer.setSource(new DOMSource(document));
- transformer.setDestination(new DOMDestination(result));
+ transformer.setSource(document);
+ final XdmDestination result = new XdmDestination();
+ transformer.setDestination(result);
transformer.transform();
// wenn der Punkt erreicht wird, sollte wenigstens, das Element evil nicht mit 'bösen' Inhalten gefüllt sein!
- if (StringUtils.isNotBlank(result.getDocumentElement().getTextContent())) {
+ if (StringUtils.isNotBlank(result.getXdmNode().getStringValue())) {
fail(String.format("Saxon configuration should prevent expansion within %s", resource));
}
diff --git a/src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java b/src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java
index 1c47ad7..f239d5b 100644
--- a/src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java
+++ b/src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java
@@ -1,7 +1,95 @@
package de.kosit.validationtool.impl;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.TransformerException;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.CollectionFinder;
+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
*/
-public class TestObjectFactory extends ObjectFactory {
+public class TestObjectFactory {
+
+ 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);
+ }
+ }
+
+ private static final String DISSALLOW_DOCTYPE_DECL_FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
+
+ private static final String LOAD_EXTERNAL_DTD_FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
+
+ private static final String FEATURE_SECURE_PROCESSING = "http://javax.xml.XMLConstants/feature/secure-processing";
+
+ private static Processor 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);
+ }
+ }
+
+ public static Processor createProcessor() {
+ if (processor == null) {
+ 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(FeatureKeys.DTD_VALIDATION, false);
+ processor.setConfigurationProperty(FeatureKeys.ENTITY_RESOLVER_CLASS, "");
+ processor.setConfigurationProperty(FeatureKeys.XINCLUDE, false);
+ processor.setConfigurationProperty(FeatureKeys.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;
+ }
}
From a739868c3bd560d05f3ff4b77e96a4846e325326 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Wed, 29 Apr 2020 17:10:30 +0200
Subject: [PATCH 10/36] remove generated code on clean; sonar fixes
---
pom.xml | 12 +++++++
.../model/daemon/ObjectFactory.java | 31 +++++++++----------
.../config/DefaultConfiguration.java | 2 +-
.../impl/input/SourceInput.java | 1 +
4 files changed, 28 insertions(+), 18 deletions(-)
diff --git a/pom.xml b/pom.xml
index ab51b8e..022da02 100644
--- a/pom.xml
+++ b/pom.xml
@@ -145,6 +145,18 @@
+
+ maven-clean-plugin
+ 3.1.0
+
+
+
+ src/generated/java
+ false
+
+
+
+
org.apache.maven.plugins
maven-enforcer-plugin
diff --git a/src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java b/src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java
index 548fd2d..b5a58c5 100644
--- a/src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java
+++ b/src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java
@@ -2,7 +2,7 @@
// Diese Datei wurde mit der JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0 generiert
// Siehe https://javaee.github.io/jaxb-v2/
// Änderungen an dieser Datei gehen bei einer Neukompilierung des Quellschemas verloren.
-// Generiert: 2020.04.29 um 03:45:08 PM CEST
+// Generiert: 2020.04.29 um 05:05:04 PM CEST
//
@@ -15,17 +15,13 @@ import javax.xml.namespace.QName;
/**
- * This object contains factory methods for each
- * Java content interface and Java element interface
- * generated in the de.xoev.de.validator.framework._1.daemon package.
- * An ObjectFactory allows you to programatically
- * construct new instances of the Java representation
- * for XML content. The Java representation of XML
- * content can consist of schema derived interfaces
- * and classes representing the binding of schema
- * type definitions, element declarations and model
- * groups. Factory methods for each of these are
- * provided in this class.
+ * This object contains factory methods for each Java content interface and Java element interface generated in the
+ * de.kosit.validationtool.model.daemon package.
+ *
+ * An ObjectFactory allows you to programatically construct new instances of the Java representation for XML content.
+ * The Java representation of XML content can consist of schema derived interfaces and classes representing the binding
+ * of schema type definitions, element declarations and model groups. Factory methods for each of these are provided in
+ * this class.
*
*/
@XmlRegistry
@@ -34,7 +30,8 @@ public class ObjectFactory {
private final static QName _Health_QNAME = new QName("http://www.xoev.de/de/validator/framework/1/daemon", "health");
/**
- * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: de.xoev.de.validator.framework._1.daemon
+ * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package:
+ * de.kosit.validationtool.model.daemon
*
*/
public ObjectFactory() {
@@ -44,7 +41,7 @@ public class ObjectFactory {
* Create an instance of {@link HealthType }
*
*/
- public static HealthType createHealthType() {
+ public HealthType createHealthType() {
return new HealthType();
}
@@ -52,7 +49,7 @@ public class ObjectFactory {
* Create an instance of {@link ApplicationType }
*
*/
- public static ApplicationType createApplicationType() {
+ public ApplicationType createApplicationType() {
return new ApplicationType();
}
@@ -60,7 +57,7 @@ public class ObjectFactory {
* Create an instance of {@link MemoryType }
*
*/
- public static MemoryType createMemoryType() {
+ public MemoryType createMemoryType() {
return new MemoryType();
}
@@ -73,7 +70,7 @@ public class ObjectFactory {
* the new instance of {@link JAXBElement }{@code <}{@link HealthType }{@code >}
*/
@XmlElementDecl(namespace = "http://www.xoev.de/de/validator/framework/1/daemon", name = "health")
- public static JAXBElement createHealth(final HealthType value) {
+ public JAXBElement createHealth(HealthType value) {
return new JAXBElement(_Health_QNAME, HealthType.class, null, value);
}
diff --git a/src/main/java/de/kosit/validationtool/config/DefaultConfiguration.java b/src/main/java/de/kosit/validationtool/config/DefaultConfiguration.java
index 1af8820..830f86b 100644
--- a/src/main/java/de/kosit/validationtool/config/DefaultConfiguration.java
+++ b/src/main/java/de/kosit/validationtool/config/DefaultConfiguration.java
@@ -36,5 +36,5 @@ public class DefaultConfiguration implements Configuration {
private String date;
- public Map additionalParameters;
+ private Map additionalParameters;
}
diff --git a/src/main/java/de/kosit/validationtool/impl/input/SourceInput.java b/src/main/java/de/kosit/validationtool/impl/input/SourceInput.java
index 8cc4a3d..f11e65f 100644
--- a/src/main/java/de/kosit/validationtool/impl/input/SourceInput.java
+++ b/src/main/java/de/kosit/validationtool/impl/input/SourceInput.java
@@ -75,6 +75,7 @@ public class SourceInput extends AbstractInput {
return (ss.getInputStream() != null && ss.getInputStream().available() == 0)
|| (ss.getReader() != null && !ss.getReader().ready());
} catch (final IOException e) {
+ log.error("Error checking consumed state", e);
return true;
}
}
From 5886c9a0dbd98cbde6c687af2ff9483abbb236c6 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Wed, 29 Apr 2020 17:17:16 +0200
Subject: [PATCH 11/36] (fix) generate source to target folder
---
pom.xml | 35 ---------
.../model/daemon/ObjectFactory.java | 77 -------------------
2 files changed, 112 deletions(-)
delete mode 100644 src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java
diff --git a/pom.xml b/pom.xml
index 022da02..e3e404d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -145,18 +145,6 @@
-
- maven-clean-plugin
- 3.1.0
-
-
-
- src/generated/java
- false
-
-
-
-
org.apache.maven.plugins
maven-enforcer-plugin
@@ -278,28 +266,6 @@
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
- 3.0.0
-
-
- add-source
- generate-sources
-
- add-source
-
-
-
- src/generated/java
-
-
-
-
-
-
org.jvnet.jaxb2.maven2
@@ -316,7 +282,6 @@
true
src/main/model/xsd
src/main/model/binding
- src/generated/java
false
-Xinheritance
diff --git a/src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java b/src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java
deleted file mode 100644
index b5a58c5..0000000
--- a/src/generated/java/de/kosit/validationtool/model/daemon/ObjectFactory.java
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-// Diese Datei wurde mit der JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0 generiert
-// Siehe https://javaee.github.io/jaxb-v2/
-// Änderungen an dieser Datei gehen bei einer Neukompilierung des Quellschemas verloren.
-// Generiert: 2020.04.29 um 05:05:04 PM CEST
-//
-
-
-package de.kosit.validationtool.model.daemon;
-
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.annotation.XmlElementDecl;
-import javax.xml.bind.annotation.XmlRegistry;
-import javax.xml.namespace.QName;
-
-
-/**
- * This object contains factory methods for each Java content interface and Java element interface generated in the
- * de.kosit.validationtool.model.daemon package.
- *
- * An ObjectFactory allows you to programatically construct new instances of the Java representation for XML content.
- * The Java representation of XML content can consist of schema derived interfaces and classes representing the binding
- * of schema type definitions, element declarations and model groups. Factory methods for each of these are provided in
- * this class.
- *
- */
-@XmlRegistry
-public class ObjectFactory {
-
- private final static QName _Health_QNAME = new QName("http://www.xoev.de/de/validator/framework/1/daemon", "health");
-
- /**
- * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package:
- * de.kosit.validationtool.model.daemon
- *
- */
- public ObjectFactory() {
- }
-
- /**
- * Create an instance of {@link HealthType }
- *
- */
- public HealthType createHealthType() {
- return new HealthType();
- }
-
- /**
- * Create an instance of {@link ApplicationType }
- *
- */
- public ApplicationType createApplicationType() {
- return new ApplicationType();
- }
-
- /**
- * Create an instance of {@link MemoryType }
- *
- */
- public MemoryType createMemoryType() {
- return new MemoryType();
- }
-
- /**
- * Create an instance of {@link JAXBElement }{@code <}{@link HealthType }{@code >}
- *
- * @param value
- * Java instance representing xml element's value.
- * @return
- * the new instance of {@link JAXBElement }{@code <}{@link HealthType }{@code >}
- */
- @XmlElementDecl(namespace = "http://www.xoev.de/de/validator/framework/1/daemon", name = "health")
- public JAXBElement createHealth(HealthType value) {
- return new JAXBElement(_Health_QNAME, HealthType.class, null, value);
- }
-
-}
From aca3d2bd04108b9c8a6c3a995b3ebb2c74967dda Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Wed, 29 Apr 2020 19:48:57 +0200
Subject: [PATCH 12/36] jacoco for the daemon code
---
pom.xml | 44 +++++++++++++++++++++++++++++++++++---------
1 file changed, 35 insertions(+), 9 deletions(-)
diff --git a/pom.xml b/pom.xml
index e3e404d..352ba42 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,7 +39,7 @@
UTF-8
- 0.8.4
+ 0.8.5
1.18.8
9.9.1-3
1.7.25
@@ -309,14 +309,39 @@
- prepareJacocoJUnitArgLine
+ prepareJacocoSurefireArgLine
prepare-agent
- jacocoArgumentsJUnit
+ jacocoSurefire
+
+ prepareJacocoFailsafeArgLine
+
+ prepare-agent
+
+
+ jacocoFailsafe
+ tcpserver
+ localhost
+ 8081
+
+
+
+ dump
+ post-integration-test
+
+ dump
+
+
+
+ localhost
+ 8081
+ true
+
+
generateJacocoReport
@@ -332,7 +357,7 @@
2.22.0
- -Dfile.encoding=UTF-8 ${jacocoArgumentsJUnit}
+ -Dfile.encoding=UTF-8 ${jacocoSurefire}
@@ -364,20 +389,21 @@
exec-maven-plugin
1.6.0
-
+
run
- pre-integration-test
-
+ pre-integration-test
+
exec
-
+
-
+
java
true
true
true
+ ${jacocoFailsafe}
-classpath
de.kosit.validationtool.cmd.CommandLineApplication
From fcf3ff2bf1236b4f7955000814aeac35720deda5 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Thu, 30 Apr 2020 10:32:22 +0200
Subject: [PATCH 13/36] use random ports; tests for the Daemon
---
pom.xml | 32 +++++++++++++++++--
.../kosit/validationtool/daemon/Daemon.java | 2 --
.../xml/StrictRelativeResolvingStrategy.java | 9 ++----
.../kosit/validationtool/daemon/BaseIT.java | 26 +++++++++++++++
.../CheckHandlerIT.java} | 19 ++---------
.../daemon/ConfigHandlerIT.java | 20 ++++++++++++
.../daemon/HealthHandlerIT.java | 20 ++++++++++++
7 files changed, 102 insertions(+), 26 deletions(-)
create mode 100644 src/test/java/de/kosit/validationtool/daemon/BaseIT.java
rename src/test/java/de/kosit/validationtool/{cmd/DaemonIT.java => daemon/CheckHandlerIT.java} (78%)
create mode 100644 src/test/java/de/kosit/validationtool/daemon/ConfigHandlerIT.java
create mode 100644 src/test/java/de/kosit/validationtool/daemon/HealthHandlerIT.java
diff --git a/pom.xml b/pom.xml
index 352ba42..77cb2b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -145,6 +145,26 @@
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.1.0
+
+
+ reserve-network-port
+
+ reserve-network-port
+
+ process-resources
+
+
+ validator.server.port
+ jacoco.tcp.port
+
+
+
+
+
org.apache.maven.plugins
maven-enforcer-plugin
@@ -319,6 +339,7 @@
prepareJacocoFailsafeArgLine
+ pre-integration-test
prepare-agent
@@ -326,7 +347,7 @@
jacocoFailsafe
tcpserver
localhost
- 8081
+ ${jacoco.tcp.port}
@@ -338,7 +359,7 @@
localhost
- 8081
+ ${jacoco.tcp.port}
true
@@ -411,6 +432,8 @@
${project.build.testOutputDirectory}/examples/simple/scenarios.xml
-r
${project.build.testOutputDirectory}/examples/simple/repository
+ --port
+ ${validator.server.port}
-D
@@ -425,6 +448,7 @@
+ ${jacoco.tcp.port}
@@ -448,6 +472,10 @@
integration-test
verify
+
+
+ -Dfile.encoding=UTF-8 -Ddaemon.port=${validator.server.port}
+
diff --git a/src/main/java/de/kosit/validationtool/daemon/Daemon.java b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
index 6eeda08..9756238 100644
--- a/src/main/java/de/kosit/validationtool/daemon/Daemon.java
+++ b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
@@ -6,7 +6,6 @@ import java.util.concurrent.Executors;
import com.sun.net.httpserver.HttpServer;
-import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -23,7 +22,6 @@ import de.kosit.validationtool.model.daemon.HealthType;
*/
@RequiredArgsConstructor
@Setter
-@Getter
@Slf4j
public class Daemon {
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
index bb6aa29..2db971c 100644
--- a/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
+++ b/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
@@ -1,7 +1,6 @@
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;
@@ -15,6 +14,7 @@ import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
import net.sf.saxon.Configuration;
import net.sf.saxon.expr.XPathContext;
@@ -98,12 +98,9 @@ public class StrictRelativeResolvingStrategy extends BaseResolvingStrategy {
return processor;
}
+ @SneakyThrows
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);
- }
+ return URLEncoder.encode(input, StandardCharsets.UTF_8.name());
}
@Override
diff --git a/src/test/java/de/kosit/validationtool/daemon/BaseIT.java b/src/test/java/de/kosit/validationtool/daemon/BaseIT.java
new file mode 100644
index 0000000..e719345
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/daemon/BaseIT.java
@@ -0,0 +1,26 @@
+package de.kosit.validationtool.daemon;
+
+import org.junit.Before;
+
+import io.restassured.RestAssured;
+
+/**
+ * Base for integration tests.
+ *
+ * @author Andreas Penski
+ */
+public abstract class BaseIT {
+
+ @Before
+ public void setup() {
+ final String port = System.getProperty("daemon.port");
+ if (port != null) {
+ RestAssured.port = Integer.valueOf(port);
+ }
+ final String baseHost = System.getProperty("daemon.host");
+ if (baseHost != null) {
+ RestAssured.baseURI = baseHost;
+ }
+ RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
+ }
+}
diff --git a/src/test/java/de/kosit/validationtool/cmd/DaemonIT.java b/src/test/java/de/kosit/validationtool/daemon/CheckHandlerIT.java
similarity index 78%
rename from src/test/java/de/kosit/validationtool/cmd/DaemonIT.java
rename to src/test/java/de/kosit/validationtool/daemon/CheckHandlerIT.java
index aa5f7f7..86e74f0 100644
--- a/src/test/java/de/kosit/validationtool/cmd/DaemonIT.java
+++ b/src/test/java/de/kosit/validationtool/daemon/CheckHandlerIT.java
@@ -1,4 +1,4 @@
-package de.kosit.validationtool.cmd;
+package de.kosit.validationtool.daemon;
import static io.restassured.RestAssured.given;
@@ -6,13 +6,11 @@ import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
-import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import de.kosit.validationtool.impl.Helper.Simple;
-import io.restassured.RestAssured;
import io.restassured.http.ContentType;
/**
@@ -20,24 +18,13 @@ import io.restassured.http.ContentType;
*
* @author Roula Antoun
*/
-public class DaemonIT {
+public class CheckHandlerIT extends BaseIT {
private static final String APPLICATION_XML = "application/xml";
- @Before
- public void setup() {
- final String port = System.getProperty("daemon.port");
- if (port != null) {
- RestAssured.port = Integer.valueOf(port);
- }
- final String baseHost = System.getProperty("daemon.host");
- if (baseHost != null) {
- RestAssured.baseURI = baseHost;
- }
- RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
- }
+
@Test
public void makeSureThatSuccessTest() throws IOException {
diff --git a/src/test/java/de/kosit/validationtool/daemon/ConfigHandlerIT.java b/src/test/java/de/kosit/validationtool/daemon/ConfigHandlerIT.java
new file mode 100644
index 0000000..baba2d4
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/daemon/ConfigHandlerIT.java
@@ -0,0 +1,20 @@
+package de.kosit.validationtool.daemon;
+
+import static io.restassured.RestAssured.given;
+
+import org.junit.Test;
+
+import io.restassured.http.ContentType;
+
+/**
+ * Integration test for the {@link ConfigHandler}.
+ *
+ * @author Andreas Penski
+ */
+public class ConfigHandlerIT extends BaseIT {
+
+ @Test
+ public void checkHealth() {
+ given().when().get("/server/config").then().statusCode(200).and().contentType(ContentType.XML);
+ }
+}
diff --git a/src/test/java/de/kosit/validationtool/daemon/HealthHandlerIT.java b/src/test/java/de/kosit/validationtool/daemon/HealthHandlerIT.java
new file mode 100644
index 0000000..d706624
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/daemon/HealthHandlerIT.java
@@ -0,0 +1,20 @@
+package de.kosit.validationtool.daemon;
+
+import static io.restassured.RestAssured.given;
+
+import org.junit.Test;
+
+import io.restassured.http.ContentType;
+
+/**
+ * Checks the health endpoint.
+ *
+ * @author Andreas Penski
+ */
+public class HealthHandlerIT extends BaseIT {
+
+ @Test
+ public void checkHealth() {
+ given().when().get("/server/health").then().statusCode(200).and().contentType(ContentType.XML);
+ }
+}
From 16dc45ab46d7c2ba866855f97988038656112f96 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Thu, 30 Apr 2020 14:28:51 +0200
Subject: [PATCH 14/36] some more tests
---
.../config/ConfigurationBuilder.java | 28 ++++--
.../de/kosit/validationtool/config/Keys.java | 7 +-
.../validationtool/config/ReportBuilder.java | 2 +-
.../config/ScenarioBuilder.java | 22 +++--
.../validationtool/config/SchemaBuilder.java | 2 +-
.../config/SchematronBuilder.java | 2 +-
.../validationtool/config/XPathBuilder.java | 33 +++++--
.../validationtool/impl/DateFactory.java | 12 +--
.../config/ConfigurationBuilderTest.java | 88 +++++++++++++++++++
.../config/ScenarioBuilderTest.java | 74 ++++++++++++++++
.../config/SimpleConfigTest.java | 33 +++----
11 files changed, 257 insertions(+), 46 deletions(-)
create mode 100644 src/test/java/de/kosit/validationtool/config/ConfigurationBuilderTest.java
create mode 100644 src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
index d6e91f5..1e3c8a6 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
@@ -16,6 +16,8 @@ import javax.xml.validation.Schema;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils;
+import lombok.AccessLevel;
+import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.Configuration;
@@ -37,6 +39,7 @@ import net.sf.saxon.s9api.Processor;
* @author Andreas Penski
*/
@Slf4j
+@Getter(AccessLevel.PACKAGE)
public class ConfigurationBuilder {
private final List scenarios = new ArrayList<>();
@@ -66,18 +69,33 @@ public class ConfigurationBuilder {
return this;
}
- public ConfigurationBuilder date(final LocalDate localDate) {
- this.date = localDate.toString();
- return this;
- }
public ConfigurationBuilder name(final String name) {
this.name = name;
return this;
}
+ /**
+ * Sets the date for this configuration.
+ *
+ * @param date the date
+ * @return this
+ */
+ public ConfigurationBuilder date(final LocalDate date) {
+ if (date != null) {
+ this.date = date.toString();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the date for this configuration.
+ *
+ * @param date the date
+ * @return this
+ */
public ConfigurationBuilder date(final Date date) {
- return date(LocalDate.ofEpochDay(date.getTime()));
+ return date(date != null ? LocalDate.ofEpochDay(date.getTime()) : null);
}
public ConfigurationBuilder with(final ScenarioBuilder scenarioBuilder) {
diff --git a/src/main/java/de/kosit/validationtool/config/Keys.java b/src/main/java/de/kosit/validationtool/config/Keys.java
index 6670e71..bd37c5c 100644
--- a/src/main/java/de/kosit/validationtool/config/Keys.java
+++ b/src/main/java/de/kosit/validationtool/config/Keys.java
@@ -5,16 +5,19 @@ package de.kosit.validationtool.config;
*
* @author Andreas Penski
*/
-public class Keys {
+public final class Keys {
/**
* The actual scenarios file location as used with {@link ConfigurationLoader}.
*/
public static final String SCENARIOS_FILE = "scenarios_file";
-
/**
* The actual scenarios configuration represented as serializable tree. This either loaded from file or build manually
* via {@link ConfigurationBuilder}
*/
public static final String SCENARIO_DEFINITION = "scenario_definition";
+
+ private Keys() {
+ // hide
+ }
}
diff --git a/src/main/java/de/kosit/validationtool/config/ReportBuilder.java b/src/main/java/de/kosit/validationtool/config/ReportBuilder.java
index 1c69053..b512877 100644
--- a/src/main/java/de/kosit/validationtool/config/ReportBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ReportBuilder.java
@@ -38,7 +38,7 @@ public class ReportBuilder implements Builder, String> build(final ContentRepository repository) {
if (this.executable == null && this.source == null) {
- return createError("Must supply source location and/or executable");
+ return createError(String.format("Must supply source location and/or executable for report '%s'", this.name));
}
final CreateReportType object = createObject();
Result, String> result;
diff --git a/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java b/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
index 18f6536..898f1a9 100644
--- a/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
@@ -16,6 +16,7 @@ import org.apache.commons.lang3.tuple.Pair;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.Scenario;
@@ -37,6 +38,7 @@ import net.sf.saxon.s9api.XPathExecutable;
* @author Andreas Penski
*/
@RequiredArgsConstructor
+@Slf4j
public class ScenarioBuilder implements Builder {
private static int nameCount = 0;
@@ -45,9 +47,9 @@ public class ScenarioBuilder implements Builder {
private final Map namespaces = new HashMap<>();
- private final XPathBuilder matchConfig = new XPathBuilder();
+ private final XPathBuilder matchConfig = new XPathBuilder("match");
- private final XPathBuilder acceptConfig = new XPathBuilder();
+ private final XPathBuilder acceptConfig = new XPathBuilder("accept");
@Getter(AccessLevel.PACKAGE)
private final String name;
@@ -207,13 +209,17 @@ public class ScenarioBuilder implements Builder {
private void buildAccept(final ContentRepository repository, final List errors, final Scenario scenario) {
this.acceptConfig.setNamespaces(this.namespaces);
- final Result result = this.acceptConfig.build(repository);
- if (result.isValid()) {
- scenario.setAcceptExecutable(result.getObject());
- scenario.getConfiguration().setAcceptMatch(this.acceptConfig.getXPath());
- this.namespaces.putAll(this.acceptConfig.getNamespaces());
+ if (this.acceptConfig.isAvailable()) {
+ final Result result = this.acceptConfig.build(repository);
+ if (result.isValid()) {
+ scenario.setAcceptExecutable(result.getObject());
+ scenario.getConfiguration().setAcceptMatch(this.acceptConfig.getXPath());
+ this.namespaces.putAll(this.acceptConfig.getNamespaces());
+ } else {
+ errors.addAll(result.getErrors());
+ }
} else {
- errors.addAll(result.getErrors());
+ log.debug("No accept configuration available");
}
}
diff --git a/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java b/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java
index 49dd722..ca51060 100644
--- a/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java
@@ -37,7 +37,7 @@ public class SchemaBuilder implements Builder, String> build(final ContentRepository repository) {
if (this.schema == null && this.schemaLocation == null) {
- return createError("Must supply schema location and/or schema");
+ return createError(String.format("Must supply source location and/or executable for schema '%s'", this.name));
}
Result, String> result;
try {
diff --git a/src/main/java/de/kosit/validationtool/config/SchematronBuilder.java b/src/main/java/de/kosit/validationtool/config/SchematronBuilder.java
index ad74f43..73c187b 100644
--- a/src/main/java/de/kosit/validationtool/config/SchematronBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/SchematronBuilder.java
@@ -38,7 +38,7 @@ public class SchematronBuilder implements Builder, String> build(final ContentRepository repository) {
if (this.executable == null && this.source == null) {
- return createError("Must supply source location and/or executable");
+ return createError(String.format("Must supply source location and/or executable for schematron '%s'", this.name));
}
final ValidateWithSchematron object = createObject();
Result, String> result;
diff --git a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
index 464d745..d8d53b0 100644
--- a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
@@ -1,5 +1,7 @@
package de.kosit.validationtool.config;
+import static org.apache.commons.lang3.ObjectUtils.isNotEmpty;
+
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -11,7 +13,9 @@ import org.apache.commons.lang3.ArrayUtils;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
+import lombok.RequiredArgsConstructor;
import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.model.Result;
@@ -23,11 +27,15 @@ import net.sf.saxon.s9api.XPathExecutable;
*
* @author Andreas Penski
*/
+@RequiredArgsConstructor
@Data
+@Slf4j
class XPathBuilder implements Builder {
private static final String[] IGNORED_PREFIXES = new String[] { "xsd" };
+ private final String name;
+
private String xpath;
private XPathExecutable executable;
@@ -44,19 +52,30 @@ class XPathBuilder implements Builder {
public String getXPath() {
return this.xpath == null && this.executable != null ? this.executable.getUnderlyingExpression().getInternalExpression().toString()
: this.xpath;
+ }
+ public boolean isAvailable() {
+ return this.executable != null || isNotEmpty(this.xpath);
}
@Override
public Result build(final ContentRepository repository) {
- if (this.executable == null && this.xpath == null) {
- return createError("No configuration for xpath expression found");
+ if (!isAvailable()) {
+ return createError(String.format("No configuration for %s xpath expression found", this.name));
}
- if (this.executable == null) {
- this.executable = repository.createXPath(this.xpath, this.namespaces);
- } else {
- this.xpath = extractExpression();
- this.namespaces = extractNamespaces();
+ try {
+ if (this.executable == null) {
+ this.executable = repository.createXPath(this.xpath, this.namespaces);
+
+ } else {
+ this.xpath = extractExpression();
+ this.namespaces = extractNamespaces();
+ }
+ } catch (final IllegalStateException e) {
+ final String msg = String.format("Error creating %s xpath", this.name, e);
+ log.error(msg, e);
+ return new Result<>(Collections.singletonList(msg));
+
}
return new Result<>(this.executable);
}
diff --git a/src/main/java/de/kosit/validationtool/impl/DateFactory.java b/src/main/java/de/kosit/validationtool/impl/DateFactory.java
index 4665c97..0c9fe9a 100644
--- a/src/main/java/de/kosit/validationtool/impl/DateFactory.java
+++ b/src/main/java/de/kosit/validationtool/impl/DateFactory.java
@@ -3,23 +3,25 @@ package de.kosit.validationtool.impl;
import java.util.Date;
import java.util.GregorianCalendar;
-import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
+import lombok.SneakyThrows;
+
/**
* @author Andreas Penski
*/
public class DateFactory {
+ private DateFactory() {
+ // hide
+ }
+
+ @SneakyThrows
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);
- }
}
}
diff --git a/src/test/java/de/kosit/validationtool/config/ConfigurationBuilderTest.java b/src/test/java/de/kosit/validationtool/config/ConfigurationBuilderTest.java
new file mode 100644
index 0000000..86df9be
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/config/ConfigurationBuilderTest.java
@@ -0,0 +1,88 @@
+package de.kosit.validationtool.config;
+
+import static de.kosit.validationtool.config.ConfigurationBuilder.report;
+import static de.kosit.validationtool.config.ConfigurationBuilder.schematron;
+import static de.kosit.validationtool.config.SimpleConfigTest.createSimpleConfiguration;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.net.URI;
+import java.time.LocalDate;
+import java.util.Date;
+
+import org.hamcrest.Matchers;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+/**
+ * Test {@link ConfigurationBuilder}.
+ *
+ * @author Andreas Penski
+ */
+public class ConfigurationBuilderTest {
+
+ public static final LocalDate EPOCH = LocalDate.of(1970, 1, 1);
+
+ @Rule
+ public ExpectedException exceptions = ExpectedException.none();
+
+ @Test
+ public void testNoConfiguration() {
+ this.exceptions.expect(IllegalStateException.class);
+ new ConfigurationBuilder().build();
+ }
+
+ @Test
+ public void testNoFallback() {
+ this.exceptions.expect(IllegalStateException.class);
+ this.exceptions.expectMessage(Matchers.containsString("fallback"));
+ final ConfigurationBuilder builder = createSimpleConfiguration();
+ builder.with((FallbackBuilder) null);
+ builder.build();
+ }
+
+ @Test
+ public void testNoSchema() {
+ this.exceptions.expect(IllegalStateException.class);
+ this.exceptions.expectMessage(Matchers.containsString("schema"));
+ final ConfigurationBuilder builder = createSimpleConfiguration();
+ builder.getScenarios().get(0).validate((SchemaBuilder) null);
+ builder.build();
+ }
+
+ @Test
+ public void testInvalidSchematron() {
+ this.exceptions.expect(IllegalStateException.class);
+ this.exceptions.expectMessage(Matchers.containsString("schematron"));
+ final ConfigurationBuilder builder = createSimpleConfiguration();
+ builder.getScenarios().get(0).validate(schematron("invalid").source(URI.create("DoesNotExist")));
+ builder.build();
+ }
+
+ @Test
+ public void testInsufficientSchematron() {
+ this.exceptions.expect(IllegalStateException.class);
+ this.exceptions.expectMessage(Matchers.containsString("schematron"));
+ final ConfigurationBuilder builder = createSimpleConfiguration();
+ builder.getScenarios().get(0).validate(schematron("invalid"));
+ builder.build();
+ }
+
+ @Test
+ public void testNoReport() {
+ this.exceptions.expect(IllegalStateException.class);
+ this.exceptions.expectMessage(Matchers.containsString("report"));
+ final ConfigurationBuilder builder = createSimpleConfiguration();
+ builder.getScenarios().get(0).with(report("invalid"));
+ builder.build();
+ }
+
+ @Test
+ public void testDate() {
+ assertThat(createSimpleConfiguration().date(EPOCH).build().getDate()).isEqualTo("1970-01-01");
+ assertThat(createSimpleConfiguration().date(new Date(EPOCH.toEpochDay())).build().getDate()).isEqualTo("1970-01-01");
+ assertThat(createSimpleConfiguration().date((Date) null).build().getDate()).isEqualTo(LocalDate.now().toString());
+ assertThat(createSimpleConfiguration().date((LocalDate) null).build().getDate()).isEqualTo(LocalDate.now().toString());
+ }
+
+}
diff --git a/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java b/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java
new file mode 100644
index 0000000..e6b9d13
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java
@@ -0,0 +1,74 @@
+package de.kosit.validationtool.config;
+
+import static de.kosit.validationtool.config.SimpleConfigTest.createScenarioConfiguration;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import de.kosit.validationtool.impl.Helper.Simple;
+import de.kosit.validationtool.impl.Scenario;
+import de.kosit.validationtool.impl.model.Result;
+
+/**
+ * Test {@link ScenarioBuilder}.
+ *
+ * @author Andreas Penski
+ */
+public class ScenarioBuilderTest {
+
+ @Rule
+ public ExpectedException exceptions = ExpectedException.none();
+
+ @Test
+ public void simpleValid() {
+ final Result result = createScenarioConfiguration().build(Simple.createContentRepository());
+ assertThat(result.isValid()).isTrue();
+ assertThat(result.getObject().getConfiguration()).isNotNull();
+ }
+
+ @Test
+ public void testNoSchema() {
+ final ScenarioBuilder builder = createScenarioConfiguration();
+ builder.validate((SchemaBuilder) null);
+ final Result result = builder.build(Simple.createContentRepository());
+ assertThat(result.isValid()).isFalse();
+ assertThat(result.getErrors()).anyMatch(e -> e.contains("schema"));
+ }
+
+ @Test
+ public void testNoMatch() {
+ final ScenarioBuilder builder = createScenarioConfiguration();
+ builder.match((String) null);
+ final Result result = builder.build(Simple.createContentRepository());
+ assertThat(result.isValid()).isFalse();
+ assertThat(result.getErrors()).anyMatch(e -> e.contains("match"));
+ }
+
+ @Test
+ public void testInvalidMatch() {
+ final ScenarioBuilder builder = createScenarioConfiguration();
+ builder.match("/////");
+ final Result result = builder.build(Simple.createContentRepository());
+ assertThat(result.isValid()).isFalse();
+ assertThat(result.getErrors()).anyMatch(e -> e.contains("match"));
+ }
+
+ @Test
+ public void testNoAccept() {
+ final ScenarioBuilder builder = createScenarioConfiguration();
+ builder.acceptWith((String) null);
+ final Result result = builder.build(Simple.createContentRepository());
+ assertThat(result.isValid()).isTrue();
+ }
+
+ @Test
+ public void testInvalidAccept() {
+ final ScenarioBuilder builder = createScenarioConfiguration();
+ builder.acceptWith("/////");
+ final Result result = builder.build(Simple.createContentRepository());
+ assertThat(result.isValid()).isFalse();
+ assertThat(result.getErrors()).anyMatch(e -> e.contains("accept"));
+ }
+}
diff --git a/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java b/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java
index 1fadac4..d6f50c2 100644
--- a/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java
+++ b/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java
@@ -25,25 +25,26 @@ public class SimpleConfigTest {
@Test
public void testSimpleWithApi() {
//@formatter:off
- final Configuration config =
- Configuration.create().name("Simple-API")
- .with(scenario("simple")
- .validate(schema("Sample Schema").schemaLocation(URI.create("simple.xsd")))
- .with(report("Report für eRechnung").source("report.xsl"))
- .acceptWith("count(//test:rejected) = 0")
- .declareNamespace("cri", "http://www.xoev.de/de/validator/framework/1/createreportinput")
- .declareNamespace("rpt", "http://validator.kosit.de/test-report")
- .declareNamespace("test", "http://validator.kosit.de/test-sample")
- .match("/test:simple")
-// .description("awesome api")
- )
- .with(fallback().name("default").source("report.xsl"))
-
- .resolvingMode(ResolvingMode.STRICT_RELATIVE)
- .useRepository(Simple.REPOSITORY_URI).build();
+ final Configuration config = createSimpleConfiguration().build();
//@formatter:on
final DefaultCheck check = new DefaultCheck(config);
final Result result = check.checkInput(InputFactory.read(Simple.SIMPLE_VALID));
assertThat(result).isNotNull();
}
+
+ static ConfigurationBuilder createSimpleConfiguration() {
+ return Configuration.create().name("Simple-API").with(createScenarioConfiguration()
+ // .description("awesome api")
+ ).with(fallback().name("default").source("report.xsl"))
+
+ .resolvingMode(ResolvingMode.STRICT_RELATIVE).useRepository(Simple.REPOSITORY_URI);
+ }
+
+ static ScenarioBuilder createScenarioConfiguration() {
+ return scenario("simple").validate(schema("Sample Schema").schemaLocation(URI.create("simple.xsd")))
+ .with(report("Report für eRechnung").source("report.xsl")).acceptWith("count(//test:rejected) = 0")
+ .declareNamespace("cri", "http://www.xoev.de/de/validator/framework/1/createreportinput")
+ .declareNamespace("rpt", "http://validator.kosit.de/test-report")
+ .declareNamespace("test", "http://validator.kosit.de/test-sample").match("/test:simple");
+ }
}
From 0bb0f6671da6ead2694f8e75c6b4ea19c07384aa Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Thu, 30 Apr 2020 15:41:39 +0200
Subject: [PATCH 15/36] some more tests
---
.../config/ConfigurationBuilder.java | 11 ++-
.../config/ScenarioBuilder.java | 8 +-
.../validationtool/config/XPathBuilder.java | 23 +++---
.../kosit/validationtool/daemon/Daemon.java | 29 +++++--
.../config/ScenarioBuilderTest.java | 79 +++++++++++++++++++
5 files changed, 131 insertions(+), 19 deletions(-)
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
index 1e3c8a6..3de8f12 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
@@ -186,7 +186,16 @@ public class ConfigurationBuilder {
* @return the scenario configuration builder
*/
public static ScenarioBuilder scenario(final String name) {
- return new ScenarioBuilder(name);
+ return new ScenarioBuilder().name(name);
+ }
+
+ /**
+ * Create a new scenario configuration.
+ *
+ * @return the scenario configuration builder
+ */
+ public static ScenarioBuilder scenario() {
+ return scenario(null);
}
/**
diff --git a/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java b/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
index 898f1a9..cd68369 100644
--- a/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ScenarioBuilder.java
@@ -39,6 +39,7 @@ import net.sf.saxon.s9api.XPathExecutable;
*/
@RequiredArgsConstructor
@Slf4j
+@Getter(AccessLevel.PACKAGE)
public class ScenarioBuilder implements Builder {
private static int nameCount = 0;
@@ -51,8 +52,7 @@ public class ScenarioBuilder implements Builder {
private final XPathBuilder acceptConfig = new XPathBuilder("accept");
- @Getter(AccessLevel.PACKAGE)
- private final String name;
+ private String name;
private SchemaBuilder schemaBuilder;
@@ -273,4 +273,8 @@ public class ScenarioBuilder implements Builder {
return type;
}
+ public ScenarioBuilder name(final String name) {
+ this.name = name;
+ return this;
+ }
}
diff --git a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
index d8d53b0..c65d0ef 100644
--- a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
@@ -9,6 +9,7 @@ import java.util.Map;
import java.util.stream.StreamSupport;
import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
import lombok.AccessLevel;
import lombok.Data;
@@ -32,7 +33,7 @@ import net.sf.saxon.s9api.XPathExecutable;
@Slf4j
class XPathBuilder implements Builder {
- private static final String[] IGNORED_PREFIXES = new String[] { "xsd" };
+ private static final String[] IGNORED_PREFIXES = new String[] { "xsd", "saxon", "xsl", "xs" };
private final String name;
@@ -66,10 +67,9 @@ class XPathBuilder implements Builder {
try {
if (this.executable == null) {
this.executable = repository.createXPath(this.xpath, this.namespaces);
-
} else {
this.xpath = extractExpression();
- this.namespaces = extractNamespaces();
+ extractNamespaces();
}
} catch (final IllegalStateException e) {
final String msg = String.format("Error creating %s xpath", this.name, e);
@@ -80,16 +80,21 @@ class XPathBuilder implements Builder {
return new Result<>(this.executable);
}
- private Map extractNamespaces() {
+ private void extractNamespaces() {
+ if (this.namespaces == null) {
+ this.namespaces = new HashMap<>();
+ }
final Map ns = new HashMap<>();
final Iterator iterator = this.executable.getUnderlyingExpression().getInternalExpression().getRetainedStaticContext()
.iteratePrefixes();
final Iterable iterable = () -> iterator;
- StreamSupport.stream(iterable.spliterator(), false).filter(e -> !ArrayUtils.contains(IGNORED_PREFIXES, e)).forEach(e -> {
- ns.put(e,
- this.executable.getUnderlyingExpression().getInternalExpression().getRetainedStaticContext().getURIForPrefix(e, false));
- });
- return ns;
+ StreamSupport.stream(iterable.spliterator(), false).filter(e -> !ArrayUtils.contains(IGNORED_PREFIXES, e))
+ .filter(StringUtils::isNotBlank).forEach(e -> {
+ ns.put(e, this.executable.getUnderlyingExpression().getInternalExpression().getRetainedStaticContext()
+ .getURIForPrefix(e, false));
+ });
+ this.namespaces.putAll(ns);
+
}
private String extractExpression() {
diff --git a/src/main/java/de/kosit/validationtool/daemon/Daemon.java b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
index 9756238..19bd42e 100644
--- a/src/main/java/de/kosit/validationtool/daemon/Daemon.java
+++ b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
@@ -1,7 +1,10 @@
package de.kosit.validationtool.daemon;
+import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
+
import java.io.IOException;
import java.net.InetSocketAddress;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.sun.net.httpserver.HttpServer;
@@ -13,7 +16,6 @@ import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.Configuration;
import de.kosit.validationtool.impl.ConversionService;
import de.kosit.validationtool.impl.DefaultCheck;
-import de.kosit.validationtool.model.daemon.HealthType;
/**
* HTTP-Daemon für die Bereitstellung der Prüf-Funktionalität via http.
@@ -25,6 +27,12 @@ import de.kosit.validationtool.model.daemon.HealthType;
@Slf4j
public class Daemon {
+ private static final String DEFAULT_HOST = "localhost";
+
+ private static final int DEFAULT_PORT = 8080;
+
+ private static final int DEFAULT_THREAD_COUNT = Runtime.getRuntime().availableProcessors();
+
private final String hostName;
private final int port;
@@ -40,18 +48,25 @@ public class Daemon {
HttpServer server = null;
try {
final ConversionService converter = new ConversionService();
- converter.initialize(HealthType.class.getPackage());
- server = HttpServer.create(new InetSocketAddress(this.hostName, this.port), 0);
+ server = HttpServer.create(getSocket(), 0);
final DefaultCheck check = new DefaultCheck(config);
server.createContext("/", new CheckHandler(check, config.getContentRepository().getProcessor()));
server.createContext("/server/health", new HealthHandler(config, converter));
- server.createContext("/server/config", new ConfigHandler(config, new ConversionService()));
- server.setExecutor(Executors.newFixedThreadPool(this.threadCount));
+ server.createContext("/server/config", new ConfigHandler(config, converter));
+ server.setExecutor(createExecutor());
server.start();
- log.info("Server unter Port {} ist erfolgreich gestartet", this.port);
+ log.info("Server {} started", server.getAddress());
} catch (final IOException e) {
- log.error("Fehler beim HttpServer erstellen: {}", e.getMessage(), e);
+ log.error("Error starting HttpServer for Valdidator: {}", e.getMessage(), e);
}
}
+
+ private ExecutorService createExecutor() {
+ return Executors.newFixedThreadPool(this.threadCount > 0 ? this.threadCount : DEFAULT_THREAD_COUNT);
+ }
+
+ private InetSocketAddress getSocket() {
+ return new InetSocketAddress(defaultIfBlank(this.hostName, DEFAULT_HOST), this.port > 0 ? this.port : DEFAULT_PORT);
+ }
}
diff --git a/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java b/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java
index e6b9d13..f264f62 100644
--- a/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java
+++ b/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java
@@ -3,13 +3,23 @@ package de.kosit.validationtool.config;
import static de.kosit.validationtool.config.SimpleConfigTest.createScenarioConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.Scenario;
import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.model.scenarios.NamespaceType;
+import de.kosit.validationtool.model.scenarios.ScenarioType;
+
+import net.sf.saxon.s9api.XPathExecutable;
/**
* Test {@link ScenarioBuilder}.
@@ -71,4 +81,73 @@ public class ScenarioBuilderTest {
assertThat(result.isValid()).isFalse();
assertThat(result.getErrors()).anyMatch(e -> e.contains("accept"));
}
+
+ @Test
+ public void testCombinedNamespaces() {
+ final ContentRepository repository = Simple.createContentRepository();
+ final Map ns1 = new HashMap<>();
+ ns1.put("n1", "http://n1.org");
+ final XPathExecutable match = repository.createXPath("//n1:*", ns1);
+
+ final Map ns2 = new HashMap<>();
+ ns2.put("n2", "http://n2.org");
+ final XPathExecutable accept = repository.createXPath("//n2:*", ns2);
+
+ final ScenarioBuilder builder = createScenarioConfiguration();
+ builder.getNamespaces().clear();
+
+ builder.match(match).acceptWith(accept).declareNamespace("n3", "http://n3.org");
+ final Result result = builder.build(repository);
+
+ assertThat(result.isValid()).isTrue();
+ final Scenario scenario = result.getObject();
+ final List namespaces = scenario.getConfiguration().getNamespace();
+ assertThat(namespaces.stream().map(NamespaceType::getPrefix)).containsExactly("n1", "n2", "n3");
+ assertThat(namespaces).hasSize(3);
+ }
+
+ @Test
+ public void testConfigureWithExecutable() {
+ final ContentRepository repository = Simple.createContentRepository();
+ final XPathExecutable match = repository.createXPath("//*", null);
+ final XPathExecutable accept = repository.createXPath("//*", null);
+ final ScenarioBuilder builder = createScenarioConfiguration();
+ builder.getNamespaces().clear();
+
+ builder.match(match);
+ builder.acceptWith(accept);
+ final Result result = builder.build(repository);
+ assertThat(result.isValid()).isTrue();
+ final ScenarioType configuration = result.getObject().getConfiguration();
+ assertThat(configuration.getMatch()).isNotEmpty();
+ assertThat(configuration.getAcceptMatch()).isNotEmpty();
+ assertThat(configuration.getNamespace()).isEmpty();
+ }
+
+ @Test
+ public void testBasicAttributes() {
+ final ContentRepository repository = Simple.createContentRepository();
+ final String random = RandomStringUtils.random(5);
+ final ScenarioBuilder builder = createScenarioConfiguration();
+ builder.name(random).description(random);
+ final Result result = builder.build(repository);
+ assertThat(result.isValid()).isTrue();
+ final ScenarioType config = result.getObject().getConfiguration();
+ assertThat(config.getName()).isEqualTo(random);
+ assertThat(config.getDescription()).isNotNull();
+ assertThat(config.getDescription().getPOrOlOrUl()).isNotEmpty();
+ }
+
+ @Test
+ public void testNoBasicAttributes() {
+ final ContentRepository repository = Simple.createContentRepository();
+ final ScenarioBuilder builder = createScenarioConfiguration();
+ builder.name(null);
+ final Result result = builder.build(repository);
+ assertThat(result.isValid()).isTrue();
+ final ScenarioType config = result.getObject().getConfiguration();
+ assertThat(config.getName()).contains("manually");
+ assertThat(config.getDescription()).isNotNull();
+ assertThat(config.getDescription().getPOrOlOrUl()).isNotEmpty();
+ }
}
From 46ddea57d699b134976487d94415ffa5da129c52 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Thu, 30 Apr 2020 15:49:09 +0200
Subject: [PATCH 16/36] some more tests
---
src/main/java/de/kosit/validationtool/daemon/Daemon.java | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/main/java/de/kosit/validationtool/daemon/Daemon.java b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
index 19bd42e..3ff2f57 100644
--- a/src/main/java/de/kosit/validationtool/daemon/Daemon.java
+++ b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
@@ -16,6 +16,7 @@ import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.Configuration;
import de.kosit.validationtool.impl.ConversionService;
import de.kosit.validationtool.impl.DefaultCheck;
+import de.kosit.validationtool.model.daemon.HealthType;
/**
* HTTP-Daemon für die Bereitstellung der Prüf-Funktionalität via http.
@@ -47,12 +48,14 @@ public class Daemon {
public void startServer(final Configuration config) {
HttpServer server = null;
try {
+ final ConversionService healthConverter = new ConversionService();
+ healthConverter.initialize(HealthType.class.getPackage());
final ConversionService converter = new ConversionService();
server = HttpServer.create(getSocket(), 0);
final DefaultCheck check = new DefaultCheck(config);
server.createContext("/", new CheckHandler(check, config.getContentRepository().getProcessor()));
- server.createContext("/server/health", new HealthHandler(config, converter));
+ server.createContext("/server/health", new HealthHandler(config, healthConverter));
server.createContext("/server/config", new ConfigHandler(config, converter));
server.setExecutor(createExecutor());
server.start();
From 2c479eded509a4fa650c4eb10f519d1245daee08 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Thu, 30 Apr 2020 17:44:23 +0200
Subject: [PATCH 17/36] some more tests
---
.../impl/tasks/CreateReportAction.java | 12 ++-
.../config/ScenarioBuilderTest.java | 22 ++---
.../config/SimpleConfigTest.java | 16 +---
.../config/TestScenarioFactory.java | 21 +++++
.../de/kosit/validationtool/impl/Helper.java | 1 +
.../impl/tasks/CreateReportActionTest.java | 86 +++++++++++++++++++
.../{ => tasks}/DocumentParseActionTest.java | 4 +-
.../impl/xml/BaseResolverTest.java | 65 ++++++++++++++
8 files changed, 197 insertions(+), 30 deletions(-)
create mode 100644 src/test/java/de/kosit/validationtool/config/TestScenarioFactory.java
create mode 100644 src/test/java/de/kosit/validationtool/impl/tasks/CreateReportActionTest.java
rename src/test/java/de/kosit/validationtool/impl/{ => tasks}/DocumentParseActionTest.java (96%)
create mode 100644 src/test/java/de/kosit/validationtool/impl/xml/BaseResolverTest.java
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
index 444aeec..59df0b8 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
@@ -72,6 +72,10 @@ public class CreateReportAction implements CheckAction {
*/
private static class ReaderWrapper implements XMLReader {
+ private static final String SAX_FEATURES_NAMESPACE_PREFIXES = "http://xml.org/sax/features/namespace-prefixes";
+
+ private static final String SAX_FEATURES_NAMESPACES = "http://xml.org/sax/features/namespaces";
+
private final XMLReader delegate;
public ReaderWrapper(final XMLReader xmlReader) {
@@ -80,9 +84,9 @@ public class CreateReportAction implements CheckAction {
@Override
public boolean getFeature(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
- if (name.equals("http://xml.org/sax/features/namespaces")) {
+ if (SAX_FEATURES_NAMESPACES.equals(name)) {
return true;
- } else if (name.equals("http://xml.org/sax/features/namespace-prefixes")) {
+ } else if (SAX_FEATURES_NAMESPACE_PREFIXES.equals(name)) {
return false;
}
// just return false on unknown properties
@@ -92,10 +96,10 @@ public class CreateReportAction implements CheckAction {
@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) {
+ if (name.equals(SAX_FEATURES_NAMESPACES) && !value) {
throw new SAXNotRecognizedException(name);
}
- if (name.equals("http://xml.org/sax/features/namespace-prefixes") && value) {
+ if (name.equals(SAX_FEATURES_NAMESPACE_PREFIXES) && value) {
throw new SAXNotRecognizedException(name);
}
}
diff --git a/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java b/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java
index f264f62..3b968c5 100644
--- a/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java
+++ b/src/test/java/de/kosit/validationtool/config/ScenarioBuilderTest.java
@@ -1,6 +1,6 @@
package de.kosit.validationtool.config;
-import static de.kosit.validationtool.config.SimpleConfigTest.createScenarioConfiguration;
+import static de.kosit.validationtool.config.TestScenarioFactory.createScenario;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.HashMap;
@@ -33,14 +33,14 @@ public class ScenarioBuilderTest {
@Test
public void simpleValid() {
- final Result result = createScenarioConfiguration().build(Simple.createContentRepository());
+ final Result result = createScenario().build(Simple.createContentRepository());
assertThat(result.isValid()).isTrue();
assertThat(result.getObject().getConfiguration()).isNotNull();
}
@Test
public void testNoSchema() {
- final ScenarioBuilder builder = createScenarioConfiguration();
+ final ScenarioBuilder builder = createScenario();
builder.validate((SchemaBuilder) null);
final Result result = builder.build(Simple.createContentRepository());
assertThat(result.isValid()).isFalse();
@@ -49,7 +49,7 @@ public class ScenarioBuilderTest {
@Test
public void testNoMatch() {
- final ScenarioBuilder builder = createScenarioConfiguration();
+ final ScenarioBuilder builder = createScenario();
builder.match((String) null);
final Result result = builder.build(Simple.createContentRepository());
assertThat(result.isValid()).isFalse();
@@ -58,7 +58,7 @@ public class ScenarioBuilderTest {
@Test
public void testInvalidMatch() {
- final ScenarioBuilder builder = createScenarioConfiguration();
+ final ScenarioBuilder builder = createScenario();
builder.match("/////");
final Result result = builder.build(Simple.createContentRepository());
assertThat(result.isValid()).isFalse();
@@ -67,7 +67,7 @@ public class ScenarioBuilderTest {
@Test
public void testNoAccept() {
- final ScenarioBuilder builder = createScenarioConfiguration();
+ final ScenarioBuilder builder = createScenario();
builder.acceptWith((String) null);
final Result result = builder.build(Simple.createContentRepository());
assertThat(result.isValid()).isTrue();
@@ -75,7 +75,7 @@ public class ScenarioBuilderTest {
@Test
public void testInvalidAccept() {
- final ScenarioBuilder builder = createScenarioConfiguration();
+ final ScenarioBuilder builder = createScenario();
builder.acceptWith("/////");
final Result result = builder.build(Simple.createContentRepository());
assertThat(result.isValid()).isFalse();
@@ -93,7 +93,7 @@ public class ScenarioBuilderTest {
ns2.put("n2", "http://n2.org");
final XPathExecutable accept = repository.createXPath("//n2:*", ns2);
- final ScenarioBuilder builder = createScenarioConfiguration();
+ final ScenarioBuilder builder = createScenario();
builder.getNamespaces().clear();
builder.match(match).acceptWith(accept).declareNamespace("n3", "http://n3.org");
@@ -111,7 +111,7 @@ public class ScenarioBuilderTest {
final ContentRepository repository = Simple.createContentRepository();
final XPathExecutable match = repository.createXPath("//*", null);
final XPathExecutable accept = repository.createXPath("//*", null);
- final ScenarioBuilder builder = createScenarioConfiguration();
+ final ScenarioBuilder builder = createScenario();
builder.getNamespaces().clear();
builder.match(match);
@@ -128,7 +128,7 @@ public class ScenarioBuilderTest {
public void testBasicAttributes() {
final ContentRepository repository = Simple.createContentRepository();
final String random = RandomStringUtils.random(5);
- final ScenarioBuilder builder = createScenarioConfiguration();
+ final ScenarioBuilder builder = createScenario();
builder.name(random).description(random);
final Result result = builder.build(repository);
assertThat(result.isValid()).isTrue();
@@ -141,7 +141,7 @@ public class ScenarioBuilderTest {
@Test
public void testNoBasicAttributes() {
final ContentRepository repository = Simple.createContentRepository();
- final ScenarioBuilder builder = createScenarioConfiguration();
+ final ScenarioBuilder builder = createScenario();
builder.name(null);
final Result result = builder.build(repository);
assertThat(result.isValid()).isTrue();
diff --git a/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java b/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java
index d6f50c2..f4374ec 100644
--- a/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java
+++ b/src/test/java/de/kosit/validationtool/config/SimpleConfigTest.java
@@ -1,13 +1,9 @@
package de.kosit.validationtool.config;
import static de.kosit.validationtool.config.ConfigurationBuilder.fallback;
-import static de.kosit.validationtool.config.ConfigurationBuilder.report;
-import static de.kosit.validationtool.config.ConfigurationBuilder.scenario;
-import static de.kosit.validationtool.config.ConfigurationBuilder.schema;
+import static de.kosit.validationtool.config.TestScenarioFactory.createScenario;
import static org.assertj.core.api.Assertions.assertThat;
-import java.net.URI;
-
import org.junit.Test;
import de.kosit.validationtool.api.Configuration;
@@ -33,18 +29,12 @@ public class SimpleConfigTest {
}
static ConfigurationBuilder createSimpleConfiguration() {
- return Configuration.create().name("Simple-API").with(createScenarioConfiguration()
+ return Configuration.create().name("Simple-API").with(createScenario()
// .description("awesome api")
).with(fallback().name("default").source("report.xsl"))
.resolvingMode(ResolvingMode.STRICT_RELATIVE).useRepository(Simple.REPOSITORY_URI);
}
- static ScenarioBuilder createScenarioConfiguration() {
- return scenario("simple").validate(schema("Sample Schema").schemaLocation(URI.create("simple.xsd")))
- .with(report("Report für eRechnung").source("report.xsl")).acceptWith("count(//test:rejected) = 0")
- .declareNamespace("cri", "http://www.xoev.de/de/validator/framework/1/createreportinput")
- .declareNamespace("rpt", "http://validator.kosit.de/test-report")
- .declareNamespace("test", "http://validator.kosit.de/test-sample").match("/test:simple");
- }
+
}
diff --git a/src/test/java/de/kosit/validationtool/config/TestScenarioFactory.java b/src/test/java/de/kosit/validationtool/config/TestScenarioFactory.java
new file mode 100644
index 0000000..61772d1
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/config/TestScenarioFactory.java
@@ -0,0 +1,21 @@
+package de.kosit.validationtool.config;
+
+import static de.kosit.validationtool.config.ConfigurationBuilder.report;
+import static de.kosit.validationtool.config.ConfigurationBuilder.scenario;
+import static de.kosit.validationtool.config.ConfigurationBuilder.schema;
+
+import java.net.URI;
+
+/**
+ * @author Andreas Penski
+ */
+public class TestScenarioFactory {
+
+ public static ScenarioBuilder createScenario() {
+ return scenario("simple").validate(schema("Sample Schema").schemaLocation(URI.create("simple.xsd")))
+ .with(report("Report für eRechnung").source("report.xsl")).acceptWith("count(//test:rejected) = 0")
+ .declareNamespace("cri", "http://www.xoev.de/de/validator/framework/1/createreportinput")
+ .declareNamespace("rpt", "http://validator.kosit.de/test-report")
+ .declareNamespace("test", "http://validator.kosit.de/test-sample").match("/test:simple");
+ }
+}
diff --git a/src/test/java/de/kosit/validationtool/impl/Helper.java b/src/test/java/de/kosit/validationtool/impl/Helper.java
index f06d755..a19a61c 100644
--- a/src/test/java/de/kosit/validationtool/impl/Helper.java
+++ b/src/test/java/de/kosit/validationtool/impl/Helper.java
@@ -82,6 +82,7 @@ public class Helper {
return new ContentRepository(strategy, Simple.REPOSITORY_URI);
}
+
public static URI getSchemaLocation() {
return ROOT.resolve("repository/simple.xsd");
}
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/CreateReportActionTest.java b/src/test/java/de/kosit/validationtool/impl/tasks/CreateReportActionTest.java
new file mode 100644
index 0000000..fe6acb3
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/CreateReportActionTest.java
@@ -0,0 +1,86 @@
+package de.kosit.validationtool.impl.tasks;
+
+import static de.kosit.validationtool.config.TestScenarioFactory.createScenario;
+import static de.kosit.validationtool.impl.Helper.serialize;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import javax.xml.transform.Source;
+
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import de.kosit.validationtool.api.InputFactory;
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.ConversionService;
+import de.kosit.validationtool.impl.Helper.Simple;
+import de.kosit.validationtool.impl.Scenario;
+import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.impl.tasks.CheckAction.Bag;
+
+import net.sf.saxon.s9api.DocumentBuilder;
+import net.sf.saxon.s9api.Processor;
+import net.sf.saxon.s9api.SaxonApiException;
+
+/**
+ * Test for {@link CreateReportAction}.
+ *
+ * @author Andreas Penski
+ */
+public class CreateReportActionTest {
+
+ private CreateReportAction action;
+
+ private ContentRepository repository;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Before
+ public void setup() {
+ this.repository = Simple.createContentRepository();
+ this.action = new CreateReportAction(this.repository.getProcessor(), new ConversionService(), this.repository.getResolver());
+ }
+
+ @Test
+ public void testSimpleCreate() {
+ final Bag bag = TestBagBuilder.createBag(true, true);
+ final Scenario scenario = createScenario().build(this.repository).getObject();
+ bag.setScenarioSelectionResult(new Result<>(scenario));
+ bag.setReport(null);
+ this.action.check(bag);
+ assertThat(bag.getReport()).isNotNull();
+ }
+
+ @Test
+ public void testNoValidParseResult() {
+ // e.g. no valid xml file specified
+ final Bag bag = TestBagBuilder.createBag(InputFactory.read("someBytes".getBytes(), "invalid"), true);
+ final Scenario scenario = createScenario().build(this.repository).getObject();
+ bag.setScenarioSelectionResult(new Result<>(scenario));
+ assertThat(bag.getReport()).isNull();
+ this.action.check(bag);
+ assertThat(bag.getReport()).isNotNull();
+ final String reportString = serialize(bag.getReport());
+ assertThat(reportString).contains("SAXParseException");
+ }
+
+ @Test
+ public void testExecutionException() throws SaxonApiException {
+ this.expectedException.expect(IllegalStateException.class);
+ this.expectedException.expectMessage(Matchers.containsString("Can not create final report"));
+ final Processor p = mock(Processor.class);
+ final DocumentBuilder documentBuilder = mock(DocumentBuilder.class);
+ this.action = new CreateReportAction(p, new ConversionService(), null);
+
+ when(p.newDocumentBuilder()).thenReturn(documentBuilder);
+ when(documentBuilder.build(any(Source.class))).thenThrow(new SaxonApiException("mocked"));
+ this.action.check(TestBagBuilder.createBag(InputFactory.read(Simple.SIMPLE_VALID), true));
+
+ }
+}
diff --git a/src/test/java/de/kosit/validationtool/impl/DocumentParseActionTest.java b/src/test/java/de/kosit/validationtool/impl/tasks/DocumentParseActionTest.java
similarity index 96%
rename from src/test/java/de/kosit/validationtool/impl/DocumentParseActionTest.java
rename to src/test/java/de/kosit/validationtool/impl/tasks/DocumentParseActionTest.java
index 5f98181..8e276ad 100644
--- a/src/test/java/de/kosit/validationtool/impl/DocumentParseActionTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/DocumentParseActionTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package de.kosit.validationtool.impl;
+package de.kosit.validationtool.impl.tasks;
import static de.kosit.validationtool.api.InputFactory.read;
import static org.assertj.core.api.Assertions.assertThat;
@@ -27,9 +27,9 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.model.Result;
-import de.kosit.validationtool.impl.tasks.DocumentParseAction;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
import net.sf.saxon.s9api.XdmNode;
diff --git a/src/test/java/de/kosit/validationtool/impl/xml/BaseResolverTest.java b/src/test/java/de/kosit/validationtool/impl/xml/BaseResolverTest.java
new file mode 100644
index 0000000..51a2871
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/impl/xml/BaseResolverTest.java
@@ -0,0 +1,65 @@
+package de.kosit.validationtool.impl.xml;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import javax.xml.XMLConstants;
+import javax.xml.validation.SchemaFactory;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+
+import lombok.RequiredArgsConstructor;
+
+/**
+ *
+ * Tests the internal functions used to create a secure resolver
+ *
+ * @author Andreas Penski
+ */
+public class BaseResolverTest {
+
+ @RequiredArgsConstructor
+ private class TestResolvingStrategy extends StrictRelativeResolvingStrategy {
+
+ void setInternalProperty(final SchemaFactory factory, final boolean lenient) {
+ allowExternalSchema(factory, lenient, "quatsch");
+ }
+ }
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void testIgnoreUnsupportedProperty() throws SAXNotRecognizedException, SAXNotSupportedException {
+ final SchemaFactory sf = mock(SchemaFactory.class);
+ final TestResolvingStrategy s = new TestResolvingStrategy();
+ doThrow(new SAXNotRecognizedException("not supported")).when(sf).setProperty(any(), any());
+ s.setInternalProperty(sf, true);
+ }
+
+ @Test
+ public void testFailOnUnsupportedProperty() throws SAXNotRecognizedException, SAXNotSupportedException {
+ this.expectedException.expect(IllegalStateException.class);
+ final SchemaFactory sf = mock(SchemaFactory.class);
+ final TestResolvingStrategy s = new TestResolvingStrategy();
+ doThrow(new SAXNotRecognizedException("not supported")).when(sf).setProperty(any(), any());
+ s.setInternalProperty(sf, false);
+ }
+
+ @Test
+ public void testSimpleSuccess() throws SAXNotRecognizedException, SAXNotSupportedException {
+ final SchemaFactory sf = mock(SchemaFactory.class);
+ final TestResolvingStrategy s = new TestResolvingStrategy();
+ s.setInternalProperty(sf, true);
+ s.setInternalProperty(sf, false);
+ verify(sf, times(2)).setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "quatsch");
+ }
+
+}
From 7abc07291101bf489e80db3d3a70437281b1f279 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Thu, 30 Apr 2020 17:57:02 +0200
Subject: [PATCH 18/36] cleanup
---
.../kosit/validationtool/cmd/DemoBuilder.java | 69 -------------------
.../impl/xml/BaseResolvingStrategy.java | 7 +-
2 files changed, 3 insertions(+), 73 deletions(-)
delete mode 100644 src/main/java/de/kosit/validationtool/cmd/DemoBuilder.java
diff --git a/src/main/java/de/kosit/validationtool/cmd/DemoBuilder.java b/src/main/java/de/kosit/validationtool/cmd/DemoBuilder.java
deleted file mode 100644
index 074d08d..0000000
--- a/src/main/java/de/kosit/validationtool/cmd/DemoBuilder.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package de.kosit.validationtool.cmd;
-
-import static de.kosit.validationtool.config.ConfigurationBuilder.defaultFallback;
-import static de.kosit.validationtool.config.ConfigurationBuilder.report;
-import static de.kosit.validationtool.config.ConfigurationBuilder.scenario;
-import static de.kosit.validationtool.config.ConfigurationBuilder.schema;
-import static de.kosit.validationtool.config.ConfigurationBuilder.schematron;
-
-import java.net.URI;
-
-import javax.xml.validation.Schema;
-
-import de.kosit.validationtool.api.Configuration;
-import de.kosit.validationtool.config.FallbackBuilder;
-import de.kosit.validationtool.config.ScenarioBuilder;
-import de.kosit.validationtool.impl.ResolvingMode;
-
-import net.sf.saxon.s9api.XPathExecutable;
-
-/**
- * @author Andreas Penski
- */
-public class DemoBuilder {
-
- public static void main(final String[] args) {
- final XPathExecutable xpath = null;
- // @formatter:off
- Configuration
- .create()
- .name("some config")
- .resolvingMode(ResolvingMode.JDK_SUPPORTED)
- .with(scenario("s1").match("//name").validate(schema("http://some.schema.url")).description("some desc"))
- .with(scenario("s2")
- .match(xpath)
- .acceptWith(xpath)
- .validate(schema(URI.create("http://some.other.schema.url")))
- .validate(schematron("some checks").source("some-schematron.xsl"))
- .with(report("myReport").source(URI.create("some.xsl")))
- .description("some desc"))
- .with(defaultFallback())
-
- .build();
-
- Configuration
- .create()
- .name("xrechnung")
- .resolvingMode(ResolvingMode.STRICT_LOCAL)
- .with( ubl() )
- .with(cii())
- .with( myFallback())
- .build();
- // @formatter:on
- }
-
- private static ScenarioBuilder cii() {
- return null;
- }
-
- private static FallbackBuilder myFallback() {
- return new FallbackBuilder();
- }
-
- private static ScenarioBuilder ubl() {
- final Schema schema = null; // load somehow
- final ScenarioBuilder ubl = scenario("ubl");
- ubl.validate(schema("someSchema", schema));
- return ubl;
- }
-}
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java
index e8ea274..3b548fb 100644
--- a/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java
+++ b/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java
@@ -6,8 +6,7 @@ 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 org.xml.sax.SAXException;
import lombok.extern.slf4j.Slf4j;
@@ -61,7 +60,7 @@ public abstract class BaseResolvingStrategy implements ResolvingConfigurationStr
private void setProperty(final PropertySetter setter, final boolean lenient, final String errorMessage) {
try {
setter.apply();
- } catch (final SAXNotRecognizedException | SAXNotSupportedException e) {
+ } catch (final SAXException e) {
if (lenient) {
log.warn(errorMessage);
@@ -117,6 +116,6 @@ public abstract class BaseResolvingStrategy implements ResolvingConfigurationStr
@FunctionalInterface
private interface PropertySetter {
- void apply() throws SAXNotRecognizedException, SAXNotSupportedException;
+ void apply() throws SAXException;
}
}
From 1a001a1af46addbc91f2854b5351f798f5e16dd6 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Fri, 1 May 2020 13:47:40 +0200
Subject: [PATCH 19/36] more tests
---
.../validationtool/config/SchemaBuilder.java | 2 +-
.../impl/xml/RelativeUriResolver.java | 9 +-
.../cmd/CommandlineApplicationTest.java | 9 ++
.../config/SchemaBuilderTest.java | 93 +++++++++++++++++++
.../de/kosit/validationtool/impl/Helper.java | 4 +-
.../impl/RelativeUriResolverTest.java | 2 +-
6 files changed, 115 insertions(+), 4 deletions(-)
create mode 100644 src/test/java/de/kosit/validationtool/config/SchemaBuilderTest.java
diff --git a/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java b/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java
index ca51060..3c7aa1d 100644
--- a/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/SchemaBuilder.java
@@ -57,7 +57,7 @@ public class SchemaBuilder implements Builder, String> result = builder.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isTrue();
+ }
+
+ @Test
+ public void testNoConfiguration() {
+ final SchemaBuilder builder = schema("no-config");
+ final Result, String> result = builder.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isFalse();
+ }
+
+ @Test
+ public void testBuildNamedSchema() {
+ final SchemaBuilder builder = schema("myname").schemaLocation(Simple.SCHEMA);
+ final Result, String> result = builder.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isTrue();
+ assertThat(result.getObject().getKey().getResource().stream().map(ResourceType::getName).findFirst().get()).isEqualTo("myname");
+ }
+
+ @Test
+ public void testInvalidSchema() {
+ final SchemaBuilder builder = schema("myname").schemaLocation(Simple.INVALID);
+ final Result, String> result = builder.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isFalse();
+ }
+
+ @Test
+ public void testNonExisting() {
+ final SchemaBuilder builder = schema("myname").schemaLocation(Simple.REPOSITORY_URI.resolve("doesNotExist.xsd"));
+ final Result, String> result = builder.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isFalse();
+ }
+
+ @Test
+ public void testPath() {
+ final SchemaBuilder builder = schema("myname").schemaLocation(Paths.get(Simple.SCHEMA));
+ final Result, String> result = builder.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isTrue();
+ }
+
+ @Test
+ public void testStringLocation() {
+ final SchemaBuilder builder = schema("myname").schemaLocation("simple.xsd");
+ final Result, String> result = builder.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isTrue();
+ }
+
+ @Test
+ public void testPrecompiled() {
+ final ContentRepository repository = Simple.createContentRepository();
+ final Schema schema = repository.createSchema(Simple.SCHEMA);
+
+ final SchemaBuilder builder = schema("myname").schema(schema);
+ final Result, String> result = builder.build(repository);
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isTrue();
+ }
+}
diff --git a/src/test/java/de/kosit/validationtool/impl/Helper.java b/src/test/java/de/kosit/validationtool/impl/Helper.java
index a19a61c..d0ca817 100644
--- a/src/test/java/de/kosit/validationtool/impl/Helper.java
+++ b/src/test/java/de/kosit/validationtool/impl/Helper.java
@@ -77,6 +77,8 @@ public class Helper {
public static final URI REPORT_XSL = REPOSITORY_URI.resolve("report.xsl");
+ public static final URI SCHEMA = REPOSITORY_URI.resolve("simple.xsd");
+
public static final ContentRepository createContentRepository() {
final ResolvingConfigurationStrategy strategy = ResolvingMode.STRICT_RELATIVE.getStrategy();
return new ContentRepository(strategy, Simple.REPOSITORY_URI);
@@ -84,7 +86,7 @@ public class Helper {
public static URI getSchemaLocation() {
- return ROOT.resolve("repository/simple.xsd");
+ return SCHEMA;
}
}
diff --git a/src/test/java/de/kosit/validationtool/impl/RelativeUriResolverTest.java b/src/test/java/de/kosit/validationtool/impl/RelativeUriResolverTest.java
index 2106f65..925fa26 100644
--- a/src/test/java/de/kosit/validationtool/impl/RelativeUriResolverTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/RelativeUriResolverTest.java
@@ -58,7 +58,7 @@ public class RelativeUriResolverTest {
private URIResolver resolver = new RelativeUriResolver(BASE);
@Test
- public void testSucces() throws TransformerException {
+ public void testSuccess() throws TransformerException {
final Source resource = this.resolver.resolve("ubl-0001.xml", BASE.toASCIIString());
assertThat(resource).isNotNull();
}
From 7dc62012a6939f3698b6d101fa8f2366584f13a3 Mon Sep 17 00:00:00 2001
From: Andreas Penski <18-andreas.penski@users.noreply.projekte.kosit.org>
Date: Sun, 3 May 2020 16:52:55 +0200
Subject: [PATCH 20/36] some cleanup
---
.idea/compiler.xml | 1 +
.idea/misc.xml | 2 +-
CHANGELOG.md | 11 +++++--
docs/api.md | 2 +-
.../api/AcceptRecommendation.java | 7 ++--
.../validationtool/api/Configuration.java | 28 +++++++++-------
.../api/ResolvingConfigurationStrategy.java | 14 +++++---
.../cmd/CommandLineApplication.java | 1 +
.../config/ConfigurationBuilder.java | 13 +++++++-
.../config/ConfigurationLoader.java | 16 +++++++++
.../config/FallbackBuilder.java | 2 ++
.../validationtool/config/XPathBuilder.java | 2 +-
.../impl/EngineInformation.java | 6 ++++
.../impl/tasks/ComputeAcceptanceAction.java | 2 +-
.../impl/tasks/CreateReportAction.java | 5 ++-
.../impl/xml/RelativeUriResolver.java | 13 +-------
.../impl/xml/RemoteResolvingStrategy.java | 7 ++++
.../xml/StrictLocalResolvingStrategy.java | 19 ++++++++---
.../xml/StrictRelativeResolvingStrategy.java | 1 +
src/main/model/xsd/daemon.xsd | 1 -
.../impl/tasks/CreateReportActionTest.java | 8 ++---
.../resources/examples/simple/assertions.xml | 33 +++++++++++++++++++
22 files changed, 144 insertions(+), 50 deletions(-)
create mode 100644 src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java
create mode 100644 src/test/resources/examples/simple/assertions.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 9f64100..bea581f 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -8,6 +8,7 @@
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index f207c86..3ab06a4 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -79,7 +79,7 @@
-
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ec9e2ce..07e06a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,12 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## next version (unreleased)
### Added
-
-- Support java.xml.transform.Source/java.xml.transform.StreamSource as Input
+- Added a builder style configuration API to configure scenarios
+- Added an option to configure xml security e.g. to load from http sources or not from a specific repository
+(so loading is configurable less restrictive, default strategy is to only load from a local repository)
+- Support java.xml.transform.Source as Input
### Changed
-
- Inputs are NOT read into memory (e.g. Byte-Array) prior processing within the validator. This reduces memory consumption.
+- CheckConfiguration is deprecated now. Use Configuration.load(...) or Configuration.build(...)
+- Overall processing of xml files is based on Saxon s9api. No JAXP or SAX classes are used by
+the validator (this further improves performance and memory consumption)
+-
## UNRELEASED
### Fixed
diff --git a/docs/api.md b/docs/api.md
index 31d05ab..e00f0ea 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -56,7 +56,7 @@ public class StandardExample {
// Load scenarios.xml from classpath
URL scenarios = this.getClass().getClassLoader().getResource("scenarios.xml");
// Load the rest of the specific Validator configuration from classpath
- CheckConfiguration config = new CheckConfiguration(scenarios.toURI());
+ Configuration config = Configuration.load(scenarios.toURI());
// Use the default validation procedure
Check validator = new DefaultCheck(config);
// Validate a single document
diff --git a/src/main/java/de/kosit/validationtool/api/AcceptRecommendation.java b/src/main/java/de/kosit/validationtool/api/AcceptRecommendation.java
index c491201..479733d 100644
--- a/src/main/java/de/kosit/validationtool/api/AcceptRecommendation.java
+++ b/src/main/java/de/kosit/validationtool/api/AcceptRecommendation.java
@@ -1,21 +1,22 @@
package de.kosit.validationtool.api;
/**
- * Tri-state describtion of a Recommendation.
+ * Tri-state recommendation whether to accept the {@link Input} or not.
*/
public enum AcceptRecommendation {
+
/**
* The evaluation of the overall validation could not be computed.
*/
UNDEFINED,
/**
- * Recommendation is to accept input based on the evaluation of the overall validation.
+ * Recommendation is to accept {@link Input} based on the evaluation of the overall validation.
*/
ACCEPTABLE,
/**
- * Recommendation is to reject input based on the evaluation of the overall validation.
+ * Recommendation is to reject {@link Input} based on the evaluation of the overall validation.
*/
REJECT
}
diff --git a/src/main/java/de/kosit/validationtool/api/Configuration.java b/src/main/java/de/kosit/validationtool/api/Configuration.java
index dbbb53d..9aa6f70 100644
--- a/src/main/java/de/kosit/validationtool/api/Configuration.java
+++ b/src/main/java/de/kosit/validationtool/api/Configuration.java
@@ -12,15 +12,15 @@ import de.kosit.validationtool.impl.Scenario;
/**
* Configuration of the actual {@link Check} instance. This is an interface and can be implemented by custom
* configuration classes. There are two implementations supported out of the box:
- *
+ *
*
* {@link ConfigurationLoader} implements loading {@link Check} configurations from a scenario.xml file
* Using a builder style api {@link de.kosit.validationtool.config.ConfigurationBuilder}to configure the
* {@link Check}
*
- *
+ *
* Both methods can be used via convinience methods. See below.
- *
+ *
* @author Andreas Penski
*/
@@ -28,51 +28,57 @@ public interface Configuration {
/**
* Returns a list of configured scenarios.
- *
+ *
* @return the list of scenarios
*/
List getScenarios();
/**
* Returns the configured fallback scenario to use, in case no configured scenario match.
- *
+ *
* @return the fallback scenario
*/
Scenario getFallbackScenario();
/**
* Returns the author of this configuration.
- *
+ *
* @return the author
*/
String getAuthor();
/**
* Returns the name of the specification
- *
+ *
* @return the name
*/
String getName();
/**
* The creation date of the config
- *
+ *
* @return the date
*/
String getDate();
+ /**
+ * Add some additional parameters to the validator configuration. Parameter usage depends on actual implementation of
+ * {@link Check}
+ *
+ * @return
+ */
Map getAdditionalParameters();
/**
* The content repository including resolving strategies.
- *
+ *
* @return the configured {@link ContentRepository}
*/
ContentRepository getContentRepository();
/**
* Loads an XML based scenario definition from the file specified via URI.
- *
+ *
* @param scenarioDefinition the XML file with scenario definition
* @return the loaded configuration
*/
@@ -93,7 +99,7 @@ public interface Configuration {
/**
* Creates a {@link Configuration} based on a builder style API using {@link ConfigurationBuilder}
- *
+ *
* @return the Builder
*/
static ConfigurationBuilder create() {
diff --git a/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java b/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java
index 525499a..86cad76 100644
--- a/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java
+++ b/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java
@@ -10,12 +10,15 @@ import javax.xml.validation.Validator;
import net.sf.saxon.s9api.Processor;
/**
- * Centralized construction and configuration of XML related infrastructore components. The KoSIT Validator provides out
- * of the box implementaions with various security levels.
+ * Centralized construction and configuration of XML related infrastructure components. This interface allows to use
+ * custom implementations and configurations of internal xml related factories and objects.
+ *
+ * The KoSIT Validator provides out of the box implementations with various security levels based on openjdk SAX stack.
*
* If you decide to implement a custom strategy, please be aware of XML security within your stack. The validator
* components beyond this strategy asume secured implementation of the interfaces provided by this strategy. There is no
- * effort to mitigate or prevent xml related security issues such as XXE, loading external sources etc.
+ * effort to mitigate or prevent xml related security issues such as XXE, loading external sources etc. Your would be
+ * responsible for this!
*
* @see de.kosit.validationtool.impl.ResolvingMode
* @author Andreas Penski
@@ -35,12 +38,14 @@ public interface ResolvingConfigurationStrategy {
* leverages the saxon s9api for internal processing e.g. xml reading and writing. So this is the main object to secure
* for reading, transforming and writing xml files.
*
+ * Note: you need exactly one instance for all validator related processing.
+ *
* @return a preconfigured {@link Processor}
*/
Processor getProcessor();
/**
- * Creates a specific implementation for resolving referenced objects in XML files. The URIResolver, it is used for
+ * Creates a specific implementation for resolving referenced objects in XML files. The URIResolver is used for
* dereferencing an absolute URI (after resolution) to return a {@link javax.xml.transform.Source}. It can be
* used for resolving relative URIs against a base URI or restrict access to certain URIs.
*
@@ -48,6 +53,7 @@ public interface ResolvingConfigurationStrategy {
* xsl:import-schema declarations.
*
*
+ * @param scenarioRepository an optional repository, your implementation might not need this
* @return a preconfigured {@link URIResolver}
*/
URIResolver createResolver(URI scenarioRepository);
diff --git a/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java b/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java
index 917fe57..ba0e9aa 100644
--- a/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java
+++ b/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java
@@ -29,6 +29,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
index 3de8f12..fab99b4 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
@@ -64,12 +64,17 @@ public class ConfigurationBuilder {
private String description;
+ /**
+ * Add a specific author name to this configuration.
+ *
+ * @param authorName the name of the author
+ * @return this
+ */
public ConfigurationBuilder author(final String authorName) {
this.author = authorName;
return this;
}
-
public ConfigurationBuilder name(final String name) {
this.name = name;
return this;
@@ -135,6 +140,12 @@ public class ConfigurationBuilder {
throw new NotImplementedException("Not yet defined");
}
+ /**
+ * Create a named schematron configuration.
+ *
+ * @param name the name of the schematron configuration
+ * @return new {@link SchemaBuilder}
+ */
public static SchematronBuilder schematron(final String name) {
return new SchematronBuilder().name(name);
}
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java b/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
index 684695d..0457459 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationLoader.java
@@ -175,11 +175,27 @@ public class ConfigurationLoader {
return s;
}
+ /**
+ * Sets actual {@link ResolvingMode}, when the validator needs to resolve stuff on startup.
+ * @param mode the resolving mode
+ * @return this
+ */
public ConfigurationLoader setResolvingMode(final ResolvingMode mode) {
this.resolvingMode = mode;
return this;
}
+ public ConfigurationLoader setResolvingStrategy(final ResolvingConfigurationStrategy strategy){
+ this.resolvingConfigurationStrategy = strategy;
+ return this;
+ }
+
+ /**
+ * Add a parameter to the configuration.
+ * @param name the name of the parameter
+ * @param value the parameter value object
+ * @return this
+ */
public ConfigurationLoader addParameter(final String name, final Object value) {
this.parameters.put(name, value);
return this;
diff --git a/src/main/java/de/kosit/validationtool/config/FallbackBuilder.java b/src/main/java/de/kosit/validationtool/config/FallbackBuilder.java
index 5399f39..d81b096 100644
--- a/src/main/java/de/kosit/validationtool/config/FallbackBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/FallbackBuilder.java
@@ -13,6 +13,8 @@ import de.kosit.validationtool.model.scenarios.CreateReportType;
import de.kosit.validationtool.model.scenarios.ScenarioType;
/**
+ * Create a fallback {@link Scenario} configuration.
+ *
* @author Andreas Penski
*/
public class FallbackBuilder implements Builder {
diff --git a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
index c65d0ef..83cbe5a 100644
--- a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
@@ -72,7 +72,7 @@ class XPathBuilder implements Builder {
extractNamespaces();
}
} catch (final IllegalStateException e) {
- final String msg = String.format("Error creating %s xpath", this.name, e);
+ final String msg = String.format("Error creating %s xpath: %s", this.name, e.getMessage());
log.error(msg, e);
return new Result<>(Collections.singletonList(msg));
diff --git a/src/main/java/de/kosit/validationtool/impl/EngineInformation.java b/src/main/java/de/kosit/validationtool/impl/EngineInformation.java
index 10692ec..d781f94 100644
--- a/src/main/java/de/kosit/validationtool/impl/EngineInformation.java
+++ b/src/main/java/de/kosit/validationtool/impl/EngineInformation.java
@@ -1,5 +1,7 @@
package de.kosit.validationtool.impl;
+import lombok.SneakyThrows;
+
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
@@ -65,6 +67,10 @@ public class EngineInformation {
return getFrameworkVersion().substring(0, 1);
}
+ public static String getBuild() {
+ return PROPERTIES.getProperty("build_number");
+ }
+
/**
* Gibt den Namespace des eingesetzten Frameworks zurück.
*
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java
index 4e7f37b..04ccee2 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/ComputeAcceptanceAction.java
@@ -60,7 +60,7 @@ public class ComputeAcceptanceAction implements CheckAction {
} catch (final SaxonApiException e) {
final String msg = String.format("Error evaluating accept recommendation: %s", selector.getUnderlyingXPathContext().toString());
log.error(msg, e);
- results.addProcessingError(msg);
+ results.stopProcessing(msg);
}
}
diff --git a/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java b/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
index 59df0b8..014bae7 100644
--- a/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
+++ b/src/main/java/de/kosit/validationtool/impl/tasks/CreateReportAction.java
@@ -28,6 +28,7 @@ import javax.xml.bind.Marshaller;
import javax.xml.bind.util.JAXBSource;
import javax.xml.transform.URIResolver;
+import lombok.extern.slf4j.Slf4j;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
@@ -64,6 +65,7 @@ import net.sf.saxon.s9api.XsltTransformer;
* @author Andreas Penski
*/
@RequiredArgsConstructor
+@Slf4j
public class CreateReportAction implements CheckAction {
/**
@@ -206,7 +208,8 @@ public class CreateReportAction implements CheckAction {
results.setReport(destination.getXdmNode());
} catch (final SaxonApiException | SAXException | JAXBException e) {
- throw new IllegalStateException("Can not create final report", e);
+ log.error("Error creating final report", e);
+ results.stopProcessing("Can not create final report: " + e.getMessage());
}
}
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/RelativeUriResolver.java b/src/main/java/de/kosit/validationtool/impl/xml/RelativeUriResolver.java
index ff1d8b3..5b5d731 100644
--- a/src/main/java/de/kosit/validationtool/impl/xml/RelativeUriResolver.java
+++ b/src/main/java/de/kosit/validationtool/impl/xml/RelativeUriResolver.java
@@ -94,16 +94,5 @@ public class RelativeUriResolver implements URIResolver {
}
- public Reader resolve(final URI absoluteURI, final String encoding, final Configuration config) throws XPathException {
- if (isUnderBaseUri(absoluteURI)) {
- try {
- return new InputStreamReader(absoluteURI.toURL().openStream(), encoding);
- } catch (final IOException e) {
- throw new IllegalStateException(String.format("Can not resolve required %s", absoluteURI), e);
- }
- } else {
- throw new IllegalStateException(String.format(
- "The resolved transformation artifact %s is not within the configured repository %s", absoluteURI, this.baseUri));
- }
- }
+
}
\ No newline at end of file
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java
new file mode 100644
index 0000000..3be4a65
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java
@@ -0,0 +1,7 @@
+package de.kosit.validationtool.impl.xml;
+
+public class RemoteResolvingStrategy extends StrictLocalResolvingStrategy {
+
+
+
+}
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java
index 90750b6..68c27a8 100644
--- a/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java
+++ b/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java
@@ -10,7 +10,9 @@ import javax.xml.validation.Validator;
import lombok.extern.slf4j.Slf4j;
/**
- *
+ * This is a slightly more open implementation that allows resolving artifacts from local filesystems. Your are not
+ * bound to a specific 'repository'. But your validation artifacts (schema, xsl, etc.) must be available locally. This
+ * implementation does not allow loading from http sources
*
* @author Andreas Penski
*/
@@ -18,16 +20,23 @@ import lombok.extern.slf4j.Slf4j;
public class StrictLocalResolvingStrategy extends StrictRelativeResolvingStrategy {
/**
- * e.g. don't allow any scheme
+ * Allow loading schema files from any local location.
+ *
+ * @return a configured {@link SchemaFactory}
*/
-
@Override
public SchemaFactory createSchemaFactory() {
final SchemaFactory schemaFactory = super.createSchemaFactory();
- allowExternalSchema(schemaFactory, "file", "jar");
+ allowExternalSchema(schemaFactory, "file");
return schemaFactory;
}
+ /**
+ * The default resolver is able to resolve locally and relative.
+ *
+ * @param repository the repository is not used by this strategy
+ * @return null!
+ */
@Override
public URIResolver createResolver(final URI repository) {
// intentionally return 'null', since all resolving is configured with the other objects
@@ -37,7 +46,7 @@ public class StrictLocalResolvingStrategy extends StrictRelativeResolvingStrateg
@Override
public Validator createValidator(final Schema schema) {
final Validator validator = super.createValidator(schema);
- allowExternalSchema(validator, "file", "jar");
+ allowExternalSchema(validator, "file");
return validator;
}
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
index 2db971c..26361e3 100644
--- a/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
+++ b/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java
@@ -95,6 +95,7 @@ public class StrictRelativeResolvingStrategy extends BaseResolvingStrategy {
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);
+ processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(XMLConstants.ACCESS_EXTERNAL_DTD), false);
return processor;
}
diff --git a/src/main/model/xsd/daemon.xsd b/src/main/model/xsd/daemon.xsd
index 5d1186a..c5f9b8c 100644
--- a/src/main/model/xsd/daemon.xsd
+++ b/src/main/model/xsd/daemon.xsd
@@ -16,7 +16,6 @@
-
diff --git a/src/test/java/de/kosit/validationtool/impl/tasks/CreateReportActionTest.java b/src/test/java/de/kosit/validationtool/impl/tasks/CreateReportActionTest.java
index fe6acb3..ae23151 100644
--- a/src/test/java/de/kosit/validationtool/impl/tasks/CreateReportActionTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/tasks/CreateReportActionTest.java
@@ -38,8 +38,6 @@ public class CreateReportActionTest {
private ContentRepository repository;
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
@Before
public void setup() {
@@ -72,15 +70,15 @@ public class CreateReportActionTest {
@Test
public void testExecutionException() throws SaxonApiException {
- this.expectedException.expect(IllegalStateException.class);
- this.expectedException.expectMessage(Matchers.containsString("Can not create final report"));
final Processor p = mock(Processor.class);
final DocumentBuilder documentBuilder = mock(DocumentBuilder.class);
this.action = new CreateReportAction(p, new ConversionService(), null);
when(p.newDocumentBuilder()).thenReturn(documentBuilder);
when(documentBuilder.build(any(Source.class))).thenThrow(new SaxonApiException("mocked"));
- this.action.check(TestBagBuilder.createBag(InputFactory.read(Simple.SIMPLE_VALID), true));
+ final Bag bag = TestBagBuilder.createBag(InputFactory.read(Simple.SIMPLE_VALID), true);
+ this.action.check(bag);
+ assertThat(bag.isStopped()).isTrue();
}
}
diff --git a/src/test/resources/examples/simple/assertions.xml b/src/test/resources/examples/simple/assertions.xml
new file mode 100644
index 0000000..40a4242
--- /dev/null
+++ b/src/test/resources/examples/simple/assertions.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+ http://www.xoev.de/de/validator/varl/1
+
+
+ Schema wurde validiert
+
+
+
+
+
From 01ae592862f06e6b1f85a2cf553a37ee3cf35612 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Mon, 4 May 2020 14:56:36 +0200
Subject: [PATCH 21/36] more tests
---
.../validationtool/config/XPathBuilder.java | 23 ++--
.../impl/ContentRepository.java | 2 +-
.../config/XPathBuilderTest.java | 116 ++++++++++++++++++
3 files changed, 129 insertions(+), 12 deletions(-)
create mode 100644 src/test/java/de/kosit/validationtool/config/XPathBuilderTest.java
diff --git a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
index 83cbe5a..c5f60b8 100644
--- a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
@@ -13,7 +13,6 @@ import org.apache.commons.lang3.StringUtils;
import lombok.AccessLevel;
import lombok.Data;
-import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -42,9 +41,15 @@ class XPathBuilder implements Builder {
private XPathExecutable executable;
@Setter(AccessLevel.PACKAGE)
- @Getter(AccessLevel.PACKAGE)
private Map namespaces;
+ Map getNamespaces() {
+ if (this.namespaces == null) {
+ this.namespaces = new HashMap<>();
+ }
+ return this.namespaces;
+ }
+
/**
* Returns the xpath expression.
*
@@ -66,7 +71,7 @@ class XPathBuilder implements Builder {
}
try {
if (this.executable == null) {
- this.executable = repository.createXPath(this.xpath, this.namespaces);
+ this.executable = repository.createXPath(this.xpath, getNamespaces());
} else {
this.xpath = extractExpression();
extractNamespaces();
@@ -81,19 +86,15 @@ class XPathBuilder implements Builder {
}
private void extractNamespaces() {
- if (this.namespaces == null) {
- this.namespaces = new HashMap<>();
- }
+
final Map ns = new HashMap<>();
final Iterator iterator = this.executable.getUnderlyingExpression().getInternalExpression().getRetainedStaticContext()
.iteratePrefixes();
final Iterable iterable = () -> iterator;
StreamSupport.stream(iterable.spliterator(), false).filter(e -> !ArrayUtils.contains(IGNORED_PREFIXES, e))
- .filter(StringUtils::isNotBlank).forEach(e -> {
- ns.put(e, this.executable.getUnderlyingExpression().getInternalExpression().getRetainedStaticContext()
- .getURIForPrefix(e, false));
- });
- this.namespaces.putAll(ns);
+ .filter(StringUtils::isNotBlank).forEach(e -> ns.put(e, this.executable.getUnderlyingExpression().getInternalExpression()
+ .getRetainedStaticContext().getURIForPrefix(e, false)));
+ getNamespaces().putAll(ns);
}
diff --git a/src/main/java/de/kosit/validationtool/impl/ContentRepository.java b/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
index 6fd7a5b..84757d4 100644
--- a/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
+++ b/src/main/java/de/kosit/validationtool/impl/ContentRepository.java
@@ -143,7 +143,7 @@ public class ContentRepository {
throw new IllegalStateException("Can not compile xslt executable for uri " + uri, e);
} finally {
if (!listener.hasErrors() && listener.hasEvents()) {
- log.warn("Received warnings while loading a xslt script {}", uri);
+ log.warn("Received warnings or errors while loading a xslt script {}", uri);
listener.getErrors().forEach(e -> e.log(log));
}
}
diff --git a/src/test/java/de/kosit/validationtool/config/XPathBuilderTest.java b/src/test/java/de/kosit/validationtool/config/XPathBuilderTest.java
new file mode 100644
index 0000000..30d3af1
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/config/XPathBuilderTest.java
@@ -0,0 +1,116 @@
+package de.kosit.validationtool.config;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.Test;
+
+import de.kosit.validationtool.impl.ContentRepository;
+import de.kosit.validationtool.impl.Helper.Simple;
+import de.kosit.validationtool.impl.model.Result;
+
+import net.sf.saxon.s9api.XPathExecutable;
+
+/**
+ * Tests {@link XPathBuilder}.
+ *
+ * @author Andreas Penski
+ */
+public class XPathBuilderTest {
+
+ @Test
+ public void testSimpleString() {
+ final String name = RandomStringUtils.randomAlphanumeric(5);
+ final XPathBuilder b = new XPathBuilder(name);
+ b.setXpath("//*");
+ final Result result = b.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isTrue();
+ assertThat(b.getNamespaces()).isNotNull();
+ assertThat(b.getNamespaces()).isEmpty();
+ assertThat(b.getXPath()).isNotEmpty();
+ assertThat(b.getName()).isNotEmpty();
+ }
+
+ @Test
+ public void testStringWithNamespace() {
+ final String name = RandomStringUtils.randomAlphanumeric(5);
+ final XPathBuilder b = new XPathBuilder(name);
+ final Map ns = new HashMap<>();
+ ns.put("p", "http://somens");
+ b.setNamespaces(ns);
+ b.setXpath("//p:*");
+ final Result result = b.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isTrue();
+ assertThat(b.getNamespaces()).isNotEmpty();
+ assertThat(b.getXPath()).isNotEmpty();
+ }
+
+ @Test
+ public void testStringWithUnknownNamespace() {
+ final String name = RandomStringUtils.randomAlphanumeric(5);
+ final XPathBuilder b = new XPathBuilder(name);
+ final Map ns = new HashMap<>();
+ ns.put("p", "http://somens");
+ b.setNamespaces(ns);
+ b.setXpath("//u:*");
+ final Result result = b.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isFalse();
+ }
+
+ @Test
+ public void testExecutable() {
+ final String name = RandomStringUtils.randomAlphanumeric(5);
+ final ContentRepository repository = Simple.createContentRepository();
+ final XPathExecutable xpath = repository.createXPath("//*", Collections.emptyMap());
+ final XPathBuilder b = new XPathBuilder(name);
+ b.setExecutable(xpath);
+ final Result result = b.build(repository);
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isTrue();
+ assertThat(b.getNamespaces()).isEmpty();
+ assertThat(b.getXPath()).isNotEmpty();
+ }
+
+ @Test
+ public void testExecutableWithNamespace() {
+ final String name = RandomStringUtils.randomAlphanumeric(5);
+ final ContentRepository repository = Simple.createContentRepository();
+ final Map ns = new HashMap<>();
+ ns.put("p", "http://somens");
+ final XPathExecutable xpath = repository.createXPath("//p:*", ns);
+ final XPathBuilder b = new XPathBuilder(name);
+ b.setExecutable(xpath);
+ final Result result = b.build(repository);
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isTrue();
+ assertThat(b.getNamespaces()).isNotEmpty();
+ assertThat(b.getNamespaces()).containsKey("p");
+ assertThat(b.getXPath()).isNotEmpty();
+ }
+
+ @Test
+ public void testNoName() {
+ final XPathBuilder b = new XPathBuilder(null);
+ b.setXpath("//*");
+ final Result result = b.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isTrue();
+ assertThat(b.getName()).isNull();
+ }
+
+ @Test
+ public void testNoConfig() {
+ final String name = RandomStringUtils.randomAlphanumeric(5);
+ final XPathBuilder b = new XPathBuilder(name);
+ final Result result = b.build(Simple.createContentRepository());
+ assertThat(result).isNotNull();
+ assertThat(result.isValid()).isFalse();
+ }
+}
From 5f15c99c04324d3547d29b28c7c98cb50c9f42a1 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Mon, 4 May 2020 14:57:20 +0200
Subject: [PATCH 22/36] sleep a little longer
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 77cb2b6..34e62a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -447,7 +447,7 @@
-
+
${jacoco.tcp.port}
From 7671977249c2a67b04987f6b008cb29a0956f324 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Mon, 4 May 2020 14:59:21 +0200
Subject: [PATCH 23/36] reduce visibility
---
.../java/de/kosit/validationtool/config/XPathBuilder.java | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
index c5f60b8..7dc6509 100644
--- a/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/XPathBuilder.java
@@ -12,7 +12,7 @@ import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import lombok.AccessLevel;
-import lombok.Data;
+import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -28,7 +28,8 @@ import net.sf.saxon.s9api.XPathExecutable;
* @author Andreas Penski
*/
@RequiredArgsConstructor
-@Data
+@Getter
+@Setter
@Slf4j
class XPathBuilder implements Builder {
From edb8427deccba517948e8f904f2f0a1318132acd Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Mon, 4 May 2020 16:27:55 +0200
Subject: [PATCH 24/36] create tests for resolvers
---
.../impl/xml/RemoteResolvingStrategy.java | 10 ++-
.../de/kosit/validationtool/impl/Helper.java | 21 +++--
.../impl/TestObjectFactory.java | 79 +------------------
...ava => BaseResolverConfigurationTest.java} | 2 +-
.../impl/xml/RemoteResolvingStrategyTest.java | 41 ++++++++++
.../impl/{ => xml}/SaxonSecurityTest.java | 5 +-
.../impl/xml/StrictLocalResolvingTest.java | 43 ++++++++++
.../impl/xml/StrictRelativeResolvingTest.java | 45 +++++++++++
.../resources/examples/resolving/main.xsd | 12 +++
.../resolving/resources/reference.xsd | 9 +++
.../examples/resolving/withRemote.xsd | 14 ++++
11 files changed, 187 insertions(+), 94 deletions(-)
rename src/test/java/de/kosit/validationtool/impl/xml/{BaseResolverTest.java => BaseResolverConfigurationTest.java} (98%)
create mode 100644 src/test/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategyTest.java
rename src/test/java/de/kosit/validationtool/impl/{ => xml}/SaxonSecurityTest.java (96%)
create mode 100644 src/test/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingTest.java
create mode 100644 src/test/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingTest.java
create mode 100644 src/test/resources/examples/resolving/main.xsd
create mode 100644 src/test/resources/examples/resolving/resources/reference.xsd
create mode 100644 src/test/resources/examples/resolving/withRemote.xsd
diff --git a/src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java b/src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java
index 3be4a65..ebebeb5 100644
--- a/src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java
+++ b/src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java
@@ -1,7 +1,13 @@
package de.kosit.validationtool.impl.xml;
+import javax.xml.validation.SchemaFactory;
+
public class RemoteResolvingStrategy extends StrictLocalResolvingStrategy {
-
-
+ @Override
+ public SchemaFactory createSchemaFactory() {
+ final SchemaFactory schemaFactory = super.createSchemaFactory();
+ allowExternalSchema(schemaFactory, "https,http,file");
+ return schemaFactory;
+ }
}
diff --git a/src/test/java/de/kosit/validationtool/impl/Helper.java b/src/test/java/de/kosit/validationtool/impl/Helper.java
index d0ca817..1fa0837 100644
--- a/src/test/java/de/kosit/validationtool/impl/Helper.java
+++ b/src/test/java/de/kosit/validationtool/impl/Helper.java
@@ -19,7 +19,6 @@
package de.kosit.validationtool.impl;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
@@ -84,7 +83,6 @@ public class Helper {
return new ContentRepository(strategy, Simple.REPOSITORY_URI);
}
-
public static URI getSchemaLocation() {
return SCHEMA;
}
@@ -99,6 +97,15 @@ public class Helper {
public static final URI SCENARIOS_ILLFORMED = ROOT.resolve("scenarios-illformed.xml");
}
+ public static class Resolving {
+
+ public static final URI ROOT = EXAMPLES_DIR.resolve("resolving/");
+
+ public static final URI SCHEMA_WITH_REMOTE_REFERENCE = ROOT.resolve("withRemote.xsd");
+
+ public static final URI SCHEMA_WITH_REFERENCE = ROOT.resolve("main.xsd");
+ }
+
public static final URI MODEL_ROOT = Paths.get("src/main/model").toUri();
public static final URI ASSERTION_SCHEMA = MODEL_ROOT.resolve("xsd/assertions.xsd");
@@ -135,16 +142,6 @@ public class Helper {
return c.readXml(url.toURI(), type);
}
- /**
- * Lädt das default test repository mit Artefacten für Unit-Tests
- *
- * @return ein {@link ContentRepository}
- */
- public static ContentRepository loadTestRepository() {
- return new ContentRepository(ResolvingMode.STRICT_RELATIVE.getStrategy(),
- new File("src/test/resources/examples/repository").toURI());
- }
-
public static String serialize(final XdmNode node) {
try ( final StringWriter writer = new StringWriter() ) {
final Processor processor = Helper.getTestProcessor();
diff --git a/src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java b/src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java
index f239d5b..e647843 100644
--- a/src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java
+++ b/src/test/java/de/kosit/validationtool/impl/TestObjectFactory.java
@@ -1,94 +1,19 @@
package de.kosit.validationtool.impl;
-import java.io.Reader;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
+import de.kosit.validationtool.impl.xml.StrictLocalResolvingStrategy;
-import javax.xml.transform.Result;
-import javax.xml.transform.TransformerException;
-
-import net.sf.saxon.Configuration;
-import net.sf.saxon.expr.XPathContext;
-import net.sf.saxon.lib.CollectionFinder;
-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
*/
public class TestObjectFactory {
- 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);
- }
- }
-
- private static final String DISSALLOW_DOCTYPE_DECL_FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
-
- private static final String LOAD_EXTERNAL_DTD_FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
-
- private static final String FEATURE_SECURE_PROCESSING = "http://javax.xml.XMLConstants/feature/secure-processing";
-
private static Processor 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);
- }
- }
-
public static Processor createProcessor() {
if (processor == null) {
- 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(FeatureKeys.DTD_VALIDATION, false);
- processor.setConfigurationProperty(FeatureKeys.ENTITY_RESOLVER_CLASS, "");
- processor.setConfigurationProperty(FeatureKeys.XINCLUDE, false);
- processor.setConfigurationProperty(FeatureKeys.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);
+ processor = new StrictLocalResolvingStrategy().getProcessor();
}
return processor;
}
diff --git a/src/test/java/de/kosit/validationtool/impl/xml/BaseResolverTest.java b/src/test/java/de/kosit/validationtool/impl/xml/BaseResolverConfigurationTest.java
similarity index 98%
rename from src/test/java/de/kosit/validationtool/impl/xml/BaseResolverTest.java
rename to src/test/java/de/kosit/validationtool/impl/xml/BaseResolverConfigurationTest.java
index 51a2871..a37718d 100644
--- a/src/test/java/de/kosit/validationtool/impl/xml/BaseResolverTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/xml/BaseResolverConfigurationTest.java
@@ -23,7 +23,7 @@ import lombok.RequiredArgsConstructor;
*
* @author Andreas Penski
*/
-public class BaseResolverTest {
+public class BaseResolverConfigurationTest {
@RequiredArgsConstructor
private class TestResolvingStrategy extends StrictRelativeResolvingStrategy {
diff --git a/src/test/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategyTest.java b/src/test/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategyTest.java
new file mode 100644
index 0000000..a60244c
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategyTest.java
@@ -0,0 +1,41 @@
+package de.kosit.validationtool.impl.xml;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
+import de.kosit.validationtool.impl.Helper.Resolving;
+
+/**
+ * Tests {@link RemoteResolvingStrategy}.
+ *
+ * @author Andreas Penski
+ */
+public class RemoteResolvingStrategyTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void testRemoteSchemaResolving() throws Exception {
+ final ResolvingConfigurationStrategy s = new RemoteResolvingStrategy();
+ final SchemaFactory schemaFactory = s.createSchemaFactory();
+ final Schema schema = schemaFactory.newSchema(Resolving.SCHEMA_WITH_REMOTE_REFERENCE.toURL());
+ assertThat(schema).isNotNull();
+ }
+
+ @Test
+ public void testLocalSchemaResolving() throws Exception {
+ final ResolvingConfigurationStrategy s = new StrictLocalResolvingStrategy();
+ final SchemaFactory schemaFactory = s.createSchemaFactory();
+ final Schema schema = schemaFactory.newSchema(Resolving.SCHEMA_WITH_REFERENCE.toURL());
+ assertThat(schema).isNotNull();
+ }
+
+}
diff --git a/src/test/java/de/kosit/validationtool/impl/SaxonSecurityTest.java b/src/test/java/de/kosit/validationtool/impl/xml/SaxonSecurityTest.java
similarity index 96%
rename from src/test/java/de/kosit/validationtool/impl/SaxonSecurityTest.java
rename to src/test/java/de/kosit/validationtool/impl/xml/SaxonSecurityTest.java
index 70ac28c..0a63475 100644
--- a/src/test/java/de/kosit/validationtool/impl/SaxonSecurityTest.java
+++ b/src/test/java/de/kosit/validationtool/impl/xml/SaxonSecurityTest.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package de.kosit.validationtool.impl;
+package de.kosit.validationtool.impl.xml;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
@@ -35,9 +35,10 @@ import org.junit.Test;
import lombok.extern.slf4j.Slf4j;
import de.kosit.validationtool.api.InputFactory;
+import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
+import de.kosit.validationtool.impl.TestObjectFactory;
import de.kosit.validationtool.impl.model.Result;
-import de.kosit.validationtool.impl.xml.RelativeUriResolver;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
import net.sf.saxon.s9api.Processor;
diff --git a/src/test/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingTest.java b/src/test/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingTest.java
new file mode 100644
index 0000000..7be18d8
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingTest.java
@@ -0,0 +1,43 @@
+package de.kosit.validationtool.impl.xml;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.hamcrest.Matchers;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.xml.sax.SAXParseException;
+
+import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
+import de.kosit.validationtool.impl.Helper.Resolving;
+
+/**
+ * Tests {@link StrictLocalResolvingStrategy}
+ *
+ * @author Andreas Penski
+ */
+public class StrictLocalResolvingTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void testRemoteSchemaResolving() throws Exception {
+ this.expectedException.expect(SAXParseException.class);
+ this.expectedException.expectMessage(Matchers.containsString("schema_reference"));
+ final ResolvingConfigurationStrategy s = new StrictLocalResolvingStrategy();
+ final SchemaFactory schemaFactory = s.createSchemaFactory();
+ schemaFactory.newSchema(Resolving.SCHEMA_WITH_REMOTE_REFERENCE.toURL());
+ }
+
+ @Test
+ public void testLocalSchemaResolving() throws Exception {
+ final ResolvingConfigurationStrategy s = new StrictLocalResolvingStrategy();
+ final SchemaFactory schemaFactory = s.createSchemaFactory();
+ final Schema schema = schemaFactory.newSchema(Resolving.SCHEMA_WITH_REFERENCE.toURL());
+ assertThat(schema).isNotNull();
+ }
+}
diff --git a/src/test/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingTest.java b/src/test/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingTest.java
new file mode 100644
index 0000000..c6a1fff
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingTest.java
@@ -0,0 +1,45 @@
+package de.kosit.validationtool.impl.xml;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.hamcrest.Matchers;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.xml.sax.SAXParseException;
+
+import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
+import de.kosit.validationtool.impl.Helper.Resolving;
+
+/**
+ * Tests {@link StrictRelativeResolvingStrategy}.
+ *
+ * @author Andreas Penski
+ */
+public class StrictRelativeResolvingTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void testRemoteSchemaResolving() throws Exception {
+ this.expectedException.expect(SAXParseException.class);
+ this.expectedException.expectMessage(Matchers.containsString("schema_reference"));
+ final ResolvingConfigurationStrategy s = new StrictLocalResolvingStrategy();
+ final SchemaFactory schemaFactory = s.createSchemaFactory();
+ schemaFactory.newSchema(Resolving.SCHEMA_WITH_REMOTE_REFERENCE.toURL());
+ }
+
+ @Test
+ public void testLocalSchemaResolving() throws Exception {
+ final ResolvingConfigurationStrategy s = new StrictLocalResolvingStrategy();
+ final SchemaFactory schemaFactory = s.createSchemaFactory();
+ final Schema schema = schemaFactory.newSchema(Resolving.SCHEMA_WITH_REFERENCE.toURL());
+ assertThat(schema).isNotNull();
+ }
+
+ // TODO loading schema from location outside of the repository - this is still possible yet
+}
diff --git a/src/test/resources/examples/resolving/main.xsd b/src/test/resources/examples/resolving/main.xsd
new file mode 100644
index 0000000..16234da
--- /dev/null
+++ b/src/test/resources/examples/resolving/main.xsd
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/examples/resolving/resources/reference.xsd b/src/test/resources/examples/resolving/resources/reference.xsd
new file mode 100644
index 0000000..6f92671
--- /dev/null
+++ b/src/test/resources/examples/resolving/resources/reference.xsd
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/examples/resolving/withRemote.xsd b/src/test/resources/examples/resolving/withRemote.xsd
new file mode 100644
index 0000000..89c51b9
--- /dev/null
+++ b/src/test/resources/examples/resolving/withRemote.xsd
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 649ea6c0c4f61105fdb53b6876e0951c1979f50e Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Wed, 6 May 2020 10:49:12 +0200
Subject: [PATCH 25/36] documentation
---
README.md | 15 ++-
docs/api.md | 93 +++++++++++++++++++
.../kosit/validationtool/config/Builder.java | 10 +-
.../config/ConfigurationBuilder.java | 58 +++++++++++-
.../validationtool/impl/ResolvingMode.java | 3 +-
5 files changed, 174 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 483ba8b..ee08010 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ The general way using the CLI is:
java -jar validationtool--standalone.jar -s [OPTIONS] [FILE] [FILE] [FILE] ...
```
-You can more CLI options by
+You can see more CLI options with
```shell
java -jar validationtool--standalone.jar --help
@@ -68,7 +68,18 @@ A concrete example with a specific validator configuration can be found on [GitH
### Application User Interface (API / embedded usage)
-The validator can also be used in own Java Applications via the API. Details can be [found here](./docs/api.md).
+The validator can also be used in own Java Applications via the API. Usage would be something like this:
+```
+Path scenarios = Paths.get("scenarios.xml");
+Configuration config = Configuration.load(scenarios.toUri());
+Input document = InputFactory.read(testDocument);
+
+Check validator = new DefaultCheck(config);
+Result validationResult = validator.checkInput(document);
+
+// examine the result here
+```
+Details and further configuration options can be [found here](./docs/api.md).
### Daemon-Mode
diff --git a/docs/api.md b/docs/api.md
index e00f0ea..c59bc23 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -121,3 +121,96 @@ recommendation.
This allows to have control over what validation result is to be considered _acceptable_ for your own application context. E.g. you can
overrule schematron validation errors with _acceptMatch_ configuration and consider certain errors as _acceptable_. Nevertheless you can *not*
overrule schema errors with accept match.
+
+## Building scenario configurations with the Builder API
+Despite using preconfigured [scenario files](configurations.md) it is also possible to create a validator configuration using
+a builder API. A valid configuration consists of the following:
+
+* at least one valid scenario configuration, this includes
+ * a valid match configuration to identify/activate this scenario
+ * a valid XML schema configuration
+ * a valid report transformation configuration
+ * valid schematron validation configurations (optional)
+ * a valid accept match configuration to compute acceptance information (optional)
+* valid a fallback scenario configuration
+
+A simple configuration looks like this:
+
+```java
+import static de.kosit.validationtool.config.ConfigurationBuilder.*;
+import de.kosit.validationtool.api.Configuration;
+import java.net.URI;
+import java.nio.file.Path;
+
+public class MyValidator {
+
+ public static void main(String[] args) {
+ Configuration config = Configuration.create().name("myconfiguration")
+ .with(scenario("firstScenario")
+ .match("//myNode")
+ .validate(schema("Sample Schema").schemaLocation(URI.create("simple.xsd")))
+ .validate(schematron("my rules").source("myRules.xsl"))
+ .with(report("my report").source("report.xsl")))
+ .with(fallback().name("default-report").source("fallback.xsl"))
+ .useRepository(Paths.get("/opt/myrepository"))
+ .build();
+ Check validator = new DefaultCheck(config);
+ // .. run your checks
+ }
+}
+```
+
+There a various methods provided by the builder API to configure your scenarios and the validation process.
+
+It is also possible to provide runtime artifacts like `XsltExecutable`, `XPathExecutalbe` or `Schema` to configure the validator.
+This gives you complete control how to load these artifacts.
+---
+**Note:** Creating this objects requires usage of the same instance of the saxon `Processor` as used during validation later. So you
+need to supply a custom `ResolvingConfigurationStrategy` or use the internal one to create these objects. See below.
+
+---
+## Configure xml security an resolving
+
+When using xml related technologies you are supposed to handle certein security issues properly. The KoSIT validator pursues a rather strict
+strategy. The default configuration
+
+* disable DTD validation completely
+* allows loading/resolving only from a configured local content repository (a specific folder)
+* tries to prevent known XML security issues (see [OWASP XML_Security_Cheat_Sheet.html](https://cheatsheetseries.owasp.org/cheatsheets/XML_Security_Cheat_Sheet.html))
+* only works with OpenJDK based XML stacks
+
+However, you can configure certain aspects related to resolving and security yourself. The validator uses a single interface for accessing
+or creating the neccessary XML API objects like `SchemaFactory`, `Validator`,`URIResolver` or `Processor`: [ResolvingConfigurationStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/api/ResolvingConfigurationStrategy.java)
+
+There are 3 implemenations available out of the box:
+
+1. [StrictRelativeResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java)
+which is the **default**, prevents known XML attacks and only allows loading from a specific local repository location
+1. [StrictLocalResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java)
+which opens the first to load resource from local location
+1. [RemoteResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java)
+which opens the first to load resource also from remote locations via http and https
+
+You can configure usage of one of this implemenations via
+
+````java
+Conifuguration config = Configuration.load(URI.create("myscenarios.xml"))
+ .resolvingMode(ResolvingMode.STRICT_LOCAL)
+ .build();
+````
+
+If you decide to implement your own strategy you can configure this via:
+
+````java
+Conifuguration config = Configuration.load(URI.create("myscenarios.xml"))
+ .resolvingStrategy(new MyCustomResolvingConfigurationStrategy())
+ .build();
+````
+
+---
+**Attention:** If you decide to implement a custom strategy you need to handle xml security risk. Please make sure, that you prevent XXE attacks and
+other kind of attacks. Consider using [BaseResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java)
+and the protected methods within to disable certain features.
+
+---
+
\ No newline at end of file
diff --git a/src/main/java/de/kosit/validationtool/config/Builder.java b/src/main/java/de/kosit/validationtool/config/Builder.java
index bdee7fd..6182e30 100644
--- a/src/main/java/de/kosit/validationtool/config/Builder.java
+++ b/src/main/java/de/kosit/validationtool/config/Builder.java
@@ -4,9 +4,17 @@ import de.kosit.validationtool.impl.ContentRepository;
import de.kosit.validationtool.impl.model.Result;
/**
+ * Internal interface for creating object builders.
+ *
* @author Andreas Penski
*/
-public interface Builder {
+interface Builder {
+ /**
+ * Creates an object based on artifacts provided via a defined {@link ContentRepository}.
+ *
+ * @param repository the {@link ContentRepository}
+ * @return the result of building the object
+ */
Result build(ContentRepository repository);
}
diff --git a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
index fab99b4..ce199b3 100644
--- a/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
+++ b/src/main/java/de/kosit/validationtool/config/ConfigurationBuilder.java
@@ -3,6 +3,7 @@ package de.kosit.validationtool.config;
import static de.kosit.validationtool.impl.DateFactory.createTimestamp;
import java.net.URI;
+import java.nio.file.Path;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
@@ -75,6 +76,12 @@ public class ConfigurationBuilder {
return this;
}
+ /**
+ * Add a specific nam to this configuration
+ *
+ * @param name the name of the configuration
+ * @return this
+ */
public ConfigurationBuilder name(final String name) {
this.name = name;
return this;
@@ -103,11 +110,25 @@ public class ConfigurationBuilder {
return date(date != null ? LocalDate.ofEpochDay(date.getTime()) : null);
}
+ /**
+ * Adds a {@link Scenario} to this list of know scenarios. Note: order of calling this methods defines order of
+ * scenarios when determining the target scenario for a given xml file.
+ *
+ * @param scenarioBuilder the {@link ScenarioBuilder} building the {@link Scenario}
+ * @return this
+ */
public ConfigurationBuilder with(final ScenarioBuilder scenarioBuilder) {
this.scenarios.add(scenarioBuilder);
return this;
}
+ /**
+ * Sets a specific fallback scenario configuration. Note: calling this more than once is possible, but the last call
+ * will define the actual fallback scenario used. There can be only one
+ *
+ * @param builder the {@link FallbackBuilder}
+ * @return this
+ */
public ConfigurationBuilder with(final FallbackBuilder builder) {
if (this.fallbackBuilder != null) {
log.warn("Overriding previously created fallback scenario");
@@ -116,6 +137,12 @@ public class ConfigurationBuilder {
return this;
}
+ /**
+ * Adds a description to this configuration.
+ *
+ * @param description the descriptioin
+ * @return this
+ */
public ConfigurationBuilder description(final String description) {
this.description = description;
return this;
@@ -219,6 +246,12 @@ public class ConfigurationBuilder {
return new ReportBuilder().name(name);
}
+ /**
+ * Builds the actual {@link Configuration} by validating all builder inputs and constructing neccessary objects.
+ *
+ * @return a valid configuration
+ * @throws IllegalStateException when the configuration is not valid/complete
+ */
public Configuration build() {
final ResolvingConfigurationStrategy resolving = getResolvingConfigurationStrategy();
if (this.processor == null) {
@@ -269,7 +302,7 @@ public class ConfigurationBuilder {
}
private List initializeScenarios(final ContentRepository contentRepository) {
- if (this.scenarios.size() == 0) {
+ if (this.scenarios.isEmpty()) {
throw new IllegalStateException("No scenario specified");
}
return this.scenarios.stream().map(s -> {
@@ -291,6 +324,13 @@ public class ConfigurationBuilder {
return this.resolvingMode.getStrategy();
}
+ /**
+ * Sets a specific resolving mode, for resolving xml artifacts for this configuration. See {@link ResolvingMode} for
+ * details.
+ *
+ * @param mode the mode
+ * @return this
+ */
public ConfigurationBuilder resolvingMode(final ResolvingMode mode) {
this.resolvingMode = mode;
return this;
@@ -307,8 +347,24 @@ public class ConfigurationBuilder {
return this;
}
+ /**
+ * Set a specific repository location for resolving artifacts for scenarios.
+ *
+ * @param repository the repository location
+ * @return this
+ */
public ConfigurationBuilder useRepository(final URI repository) {
this.repository = repository;
return this;
}
+
+ /**
+ * Set a specific repository location for resolving artifacts for scenarios.
+ *
+ * @param repository the repository location
+ * @return this
+ */
+ public ConfigurationBuilder useRepository(final Path repository) {
+ return useRepository(repository.toUri());
+ }
}
diff --git a/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java b/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java
index 885674b..1ca8517 100644
--- a/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java
+++ b/src/main/java/de/kosit/validationtool/impl/ResolvingMode.java
@@ -4,6 +4,7 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor;
import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
+import de.kosit.validationtool.impl.xml.RemoteResolvingStrategy;
import de.kosit.validationtool.impl.xml.StrictLocalResolvingStrategy;
import de.kosit.validationtool.impl.xml.StrictRelativeResolvingStrategy;
@@ -24,7 +25,7 @@ public enum ResolvingMode {
STRICT_LOCAL(new StrictLocalResolvingStrategy()),
- JDK_SUPPORTED(null),
+ ALLOW_REMOTE(new RemoteResolvingStrategy()),
CUSTOM(null);
From 91e4d799532fbc775bec91b29f36ae7bbad6adc3 Mon Sep 17 00:00:00 2001
From: Andreas Penski <18-andreas.penski@users.noreply.projekte.kosit.org>
Date: Wed, 6 May 2020 16:17:45 +0200
Subject: [PATCH 26/36] introduce docsify; refactor the daemon
---
.../validationtool/daemon/BaseHandler.java | 19 +-
.../validationtool/daemon/ConfigHandler.java | 2 +-
.../kosit/validationtool/daemon/Daemon.java | 29 +-
.../validationtool/daemon/GuiHandler.java | 52 ++
.../validationtool/daemon/HealthHandler.java | 17 +-
.../validationtool/daemon/RoutingHandler.java | 27 +
src/main/resources/gui/README.md | 20 +
src/main/resources/gui/docs/api.md | 1 +
src/main/resources/gui/index.html | 53 ++
src/main/resources/gui/lib/docsify.min.js | 1 +
src/main/resources/gui/lib/vue.css | 828 ++++++++++++++++++
.../validationtool/daemon/CheckHandlerIT.java | 15 +-
12 files changed, 1038 insertions(+), 26 deletions(-)
create mode 100644 src/main/java/de/kosit/validationtool/daemon/GuiHandler.java
create mode 100644 src/main/java/de/kosit/validationtool/daemon/RoutingHandler.java
create mode 100644 src/main/resources/gui/README.md
create mode 100644 src/main/resources/gui/docs/api.md
create mode 100644 src/main/resources/gui/index.html
create mode 100644 src/main/resources/gui/lib/docsify.min.js
create mode 100644 src/main/resources/gui/lib/vue.css
diff --git a/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java b/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java
index 014652f..4a7fe54 100644
--- a/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java
+++ b/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java
@@ -11,15 +11,21 @@ import com.sun.net.httpserver.HttpHandler;
*
* @author Andreas Penski
*/
-public abstract class BaseHandler implements HttpHandler {
+abstract class BaseHandler implements HttpHandler {
protected static final String APPLICATION_XML = "application/xml";
+ protected static final int OK = 200;
+
protected static void write(final HttpExchange exchange, final byte[] content, final String contentType) throws IOException {
- final OutputStream os = exchange.getResponseBody();
+ write(exchange, contentType, os -> os.write(content));
+ }
+
+ protected static void write(final HttpExchange exchange, final String contentType, Write write) throws IOException {
exchange.getResponseHeaders().add("Content-Type", contentType);
- exchange.sendResponseHeaders(200, content.length);
- os.write(content);
+ exchange.sendResponseHeaders(OK, 0);
+ final OutputStream os = exchange.getResponseBody();
+ write.write(os);
os.close();
}
@@ -31,4 +37,9 @@ public abstract class BaseHandler implements HttpHandler {
os.close();
}
+ @FunctionalInterface
+ protected interface Write {
+
+ public void write(OutputStream out) throws IOException;
+ }
}
diff --git a/src/main/java/de/kosit/validationtool/daemon/ConfigHandler.java b/src/main/java/de/kosit/validationtool/daemon/ConfigHandler.java
index 57b71ae..696c70e 100644
--- a/src/main/java/de/kosit/validationtool/daemon/ConfigHandler.java
+++ b/src/main/java/de/kosit/validationtool/daemon/ConfigHandler.java
@@ -26,7 +26,7 @@ import de.kosit.validationtool.model.scenarios.Scenarios;
*/
@Slf4j
@RequiredArgsConstructor
-public class ConfigHandler extends BaseHandler {
+ class ConfigHandler extends BaseHandler {
private final Configuration configuration;
diff --git a/src/main/java/de/kosit/validationtool/daemon/Daemon.java b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
index 3ff2f57..6dde8c6 100644
--- a/src/main/java/de/kosit/validationtool/daemon/Daemon.java
+++ b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
@@ -7,8 +7,10 @@ import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
+import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -32,14 +34,19 @@ public class Daemon {
private static final int DEFAULT_PORT = 8080;
- private static final int DEFAULT_THREAD_COUNT = Runtime.getRuntime().availableProcessors();
-
private final String hostName;
private final int port;
private final int threadCount;
+ @Setter(AccessLevel.PRIVATE)
+ private boolean guiDisabled = false;
+
+ public void disableGui() {
+ guiDisabled = true;
+ }
+
/**
* Methode zum Starten des Servers
*
@@ -53,8 +60,7 @@ public class Daemon {
final ConversionService converter = new ConversionService();
server = HttpServer.create(getSocket(), 0);
- final DefaultCheck check = new DefaultCheck(config);
- server.createContext("/", new CheckHandler(check, config.getContentRepository().getProcessor()));
+ server.createContext("/", createRootHandler(config));
server.createContext("/server/health", new HealthHandler(config, healthConverter));
server.createContext("/server/config", new ConfigHandler(config, converter));
server.setExecutor(createExecutor());
@@ -65,8 +71,21 @@ public class Daemon {
}
}
+ private HttpHandler createRootHandler(Configuration config) {
+ HttpHandler rootHandler;
+ final DefaultCheck check = new DefaultCheck(config);
+ final CheckHandler checkHandler = new CheckHandler(check, config.getContentRepository().getProcessor());
+ if (!guiDisabled) {
+ GuiHandler gui = new GuiHandler();
+ rootHandler = new RoutingHandler(checkHandler, gui);
+ } else {
+ rootHandler = checkHandler;
+ }
+ return rootHandler;
+ }
+
private ExecutorService createExecutor() {
- return Executors.newFixedThreadPool(this.threadCount > 0 ? this.threadCount : DEFAULT_THREAD_COUNT);
+ return Executors.newFixedThreadPool(this.threadCount > 0 ? this.threadCount : Runtime.getRuntime().availableProcessors());
}
private InetSocketAddress getSocket() {
diff --git a/src/main/java/de/kosit/validationtool/daemon/GuiHandler.java b/src/main/java/de/kosit/validationtool/daemon/GuiHandler.java
new file mode 100644
index 0000000..972daf8
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/daemon/GuiHandler.java
@@ -0,0 +1,52 @@
+package de.kosit.validationtool.daemon;
+
+import com.sun.net.httpserver.HttpExchange;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+public class GuiHandler extends BaseHandler {
+
+ private static final URL INDEX_HTML = GuiHandler.class.getClassLoader().getResource("gui/index.html");
+
+ public GuiHandler() {
+ if (INDEX_HTML == null) {
+ throw new IllegalStateException("No html found");
+ }
+ }
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ assert INDEX_HTML != null;
+ final String path = exchange.getRequestURI().toASCIIString();
+ if (path.equals("/")) {
+ write(exchange, IOUtils.toString(INDEX_HTML, Charset.defaultCharset()).getBytes(), "text/html");
+ } else{
+ final URL resource = GuiHandler.class.getClassLoader().getResource("gui" + path);
+ if (resource != null) {
+ write(exchange,IOUtils.toString(resource, Charset.defaultCharset()).getBytes(), Mediatype.resolveBySuffix(resource.getPath()).getMimeType());;
+ }else {
+ error(exchange,404,"not found");
+ }
+ }
+ }
+
+
+ @RequiredArgsConstructor
+ @Getter
+ protected enum Mediatype {
+ JS("application/javascript"),
+ MD("text/markdown"),
+ CSS("text/css");
+ private final String mimeType;
+
+ static Mediatype resolveBySuffix(String path){
+ return Arrays.stream(values()).filter(e->path.toUpperCase().endsWith("."+e.name())).findFirst().orElse(Mediatype.MD);
+ }
+ }
+}
diff --git a/src/main/java/de/kosit/validationtool/daemon/HealthHandler.java b/src/main/java/de/kosit/validationtool/daemon/HealthHandler.java
index d11dabc..de7e40f 100644
--- a/src/main/java/de/kosit/validationtool/daemon/HealthHandler.java
+++ b/src/main/java/de/kosit/validationtool/daemon/HealthHandler.java
@@ -4,6 +4,8 @@ import java.io.IOException;
import com.sun.net.httpserver.HttpExchange;
+import de.kosit.validationtool.impl.EngineInformation;
+import de.kosit.validationtool.model.daemon.ApplicationType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -15,13 +17,12 @@ import de.kosit.validationtool.model.daemon.MemoryType;
/**
* Handler that implements a simple health check. Useful for monitoring the service.
*
- * @author Andreas Penski`
+ * @author Andreas Penski
*/
@Slf4j
@RequiredArgsConstructor
class HealthHandler extends BaseHandler {
-
private final Configuration scenarios;
private final ConversionService conversionService;
@@ -34,9 +35,11 @@ class HealthHandler extends BaseHandler {
}
- private static HealthType createHealth() {
+ private HealthType createHealth() {
final HealthType h = new HealthType();
h.setMemory(createMemory());
+ h.setApplication(createApplication());
+ h.setStatus(scenarios.getScenarios().size() > 0 ? "UP" : "DOWN");
return h;
}
@@ -48,4 +51,12 @@ class HealthHandler extends BaseHandler {
m.setTotalMemory(runtime.totalMemory());
return m;
}
+
+ private static ApplicationType createApplication() {
+ ApplicationType a = new ApplicationType();
+ a.setBuild(EngineInformation.getBuild());
+ a.setName(EngineInformation.getName());
+ a.setVersion(EngineInformation.getVersion());
+ return a;
+ }
}
diff --git a/src/main/java/de/kosit/validationtool/daemon/RoutingHandler.java b/src/main/java/de/kosit/validationtool/daemon/RoutingHandler.java
new file mode 100644
index 0000000..d1a7675
--- /dev/null
+++ b/src/main/java/de/kosit/validationtool/daemon/RoutingHandler.java
@@ -0,0 +1,27 @@
+package de.kosit.validationtool.daemon;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import lombok.RequiredArgsConstructor;
+
+import java.io.IOException;
+
+@RequiredArgsConstructor
+public class RoutingHandler extends BaseHandler {
+
+ private final CheckHandler checkHandler;
+
+ private final GuiHandler guiHandler;
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ final String requestMethod = exchange.getRequestMethod();
+ if (requestMethod.equals("POST")) {
+ checkHandler.handle(exchange);
+ } else if (requestMethod.equals("GET")) {
+ guiHandler.handle(exchange);
+ } else {
+ error(exchange, 405, String.format("Method % not supported", requestMethod));
+ }
+ }
+}
diff --git a/src/main/resources/gui/README.md b/src/main/resources/gui/README.md
new file mode 100644
index 0000000..7c3955f
--- /dev/null
+++ b/src/main/resources/gui/README.md
@@ -0,0 +1,20 @@
+# KoSIT Validator - Daemon
+
+[API usage](docs/api)
+
+# Server information
+View [validator configuration](/server/config) or health information
+
+# Try it
+
+
+
diff --git a/src/main/resources/gui/docs/api.md b/src/main/resources/gui/docs/api.md
new file mode 100644
index 0000000..dfd790e
--- /dev/null
+++ b/src/main/resources/gui/docs/api.md
@@ -0,0 +1 @@
+Put content here ;)
\ No newline at end of file
diff --git a/src/main/resources/gui/index.html b/src/main/resources/gui/index.html
new file mode 100644
index 0000000..4919302
--- /dev/null
+++ b/src/main/resources/gui/index.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Validator
+
+
+
+
+
+
+
+Loading validator...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/gui/lib/docsify.min.js b/src/main/resources/gui/lib/docsify.min.js
new file mode 100644
index 0000000..79db41b
--- /dev/null
+++ b/src/main/resources/gui/lib/docsify.min.js
@@ -0,0 +1 @@
+!function(){function o(n){var r=Object.create(null);return function(e){var t=c(e)?e:JSON.stringify(e);return r[t]||(r[t]=n(e))}}var i=o(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),l=Object.prototype.hasOwnProperty,d=Object.assign||function(e){for(var t=arguments,n=1;n=a.length)i(r);else if("function"==typeof e)if(2===e.length)e(r,function(e){r=e,s(t+1)});else{var n=e(r);r=void 0===n?r:n,s(t+1)}else s(t+1)};s(0)}var O=p.title;function P(){var e=m("section.cover");if(e){var t=e.getBoundingClientRect().height;window.pageYOffset>=t||e.classList.contains("hidden")?A(v,"add","sticky"):A(v,"remove","sticky")}}function z(e,t,r,n){var i=[];null!=(t=m(t))&&(i=y(t,"a"));var a,s=decodeURI(e.toURL(e.getCurrentPath()));return i.sort(function(e,t){return t.href.length-e.href.length}).forEach(function(e){var t=e.getAttribute("href"),n=r?e.parentNode:e;0!==s.indexOf(t)||a?A(n,"remove","active"):(a=e,A(n,"add","active"))}),n&&(p.title=a?a.title||a.innerText+" - "+O:O),a}var j=decodeURIComponent,N=encodeURIComponent;function M(e){var n={};return(e=e.trim().replace(/^(\?|#|&)/,""))&&e.split("&").forEach(function(e){var t=e.replace(/\+/g," ").split("=");n[t[0]]=t[1]&&j(t[1])}),n}function q(e,t){void 0===t&&(t=[]);var n=[];for(var r in e)-1this.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,t,n,r){return(e/=r/2)<1?n/2*e*e+t:-n/2*(--e*(e-2)-1)+t}}]),X);function X(){var e=0o){t=t||u;break}t=u}if(t){var h=Q[re(decodeURIComponent(e),t.getAttribute("data-id"))];if(h&&h!==a&&(a&&a.classList.remove("active"),h.classList.add("active"),a=h,!J&&v.classList.contains("sticky"))){var p=n.clientHeight,d=a.offsetTop+a.clientHeight+40,g=d-0=i.scrollTop&&d<=i.scrollTop+p?i.scrollTop:g?0:d-p;n.scrollTop=f}}}}function re(e,t){return e+"?id="+t}function ie(e,t){if(t){var n=s().topMargin,r=b("#"+t);r&&function(e,t){void 0===t&&(t=0),K&&K.stop(),ee=!1,K=new V({start:window.pageYOffset,end:e.getBoundingClientRect().top+window.pageYOffset-t,duration:500}).on("tick",function(e){return window.scrollTo(0,e)}).on("done",function(){ee=!0,K=null}).begin()}(r,n);var i=Q[re(e,t)],a=b(m(".sidebar"),"li.active");a&&a.classList.remove("active"),i&&i.classList.add("active")}}var ae=p.scrollingElement||p.documentElement;function se(e,t){if(void 0===t&&(t='
'),!e||!e.length)return"";var n="";return e.forEach(function(e){n+=''+e.title+" ",e.children&&(n+=se(e.children,t))}),t.replace("{inner}",n)}function oe(e,t){return''+t.slice(5).trim()+"
"}function le(e,r){var i=[],a={};return e.forEach(function(e){var t=e.level||1,n=t-1;r?@[\]^`{|}~]/g;function he(e){return e.toLowerCase()}function pe(e){if("string"!=typeof e)return"";var t=e.trim().replace(/[A-Z]+/g,he).replace(/<[^>\d]+>/g,"").replace(ue,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),n=ce[t];return n=l.call(ce,t)?n+1:0,(ce[t]=n)&&(t=t+"-"+n),t}function de(e,t){return' '}function ge(e){void 0===e&&(e="");var r={};return{str:e=e&&e.replace(/^'/,"").replace(/'$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-]+)?/g,function(e,t,n){return-1===t.indexOf(":")?(r[t]=n&&n.replace(/"/g,"")||!0,""):e}).trim(),config:r}}pe.clear=function(){ce={}};var fe="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function me(e,t){return e(t={exports:{}},t.exports),t.exports}var ve,be=me(function(e){var c=function(c){var u=/\blang(?:uage)?-([\w-]+)\b/i,t=0,T={manual:c.Prism&&c.Prism.manual,disableWorkerMessageHandler:c.Prism&&c.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof R?new R(e.type,T.util.encode(e.content),e.alias):Array.isArray(e)?e.map(T.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(y instanceof R)){if(d&&v!=t.length-1){if(u.lastIndex=b,!(A=u.exec(e)))break;for(var k=A.index+(p&&A[1]?A[1].length:0),w=A.index+A[0].length,x=v,_=b,S=t.length;x"+n.content+""+n.tag+">"},!c.document)return c.addEventListener&&(T.disableWorkerMessageHandler||c.addEventListener("message",function(e){var t=JSON.parse(e.data),n=t.language,r=t.code,i=t.immediateClose;c.postMessage(T.highlight(r,T.languages[n],n)),i&&c.close()},!1)),T;var e=T.util.currentScript();if(e&&(T.filename=e.src,e.hasAttribute("data-manual")&&(T.manual=!0)),!T.manual){function n(){T.manual||T.highlightAll()}var r=document.readyState;"loading"===r||"interactive"===r&&e&&e.defer?document.addEventListener("DOMContentLoaded",n):window.requestAnimationFrame?window.requestAnimationFrame(n):window.setTimeout(n,16)}return T}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=c),void 0!==fe&&(fe.Prism=c),c.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:(?!)*\]\s*)?>/i,greedy:!0},cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/?[\da-z]{1,8};/i},c.languages.markup.tag.inside["attr-value"].inside.entity=c.languages.markup.entity,c.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(c.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:c.languages[t]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:c.languages[t]};var i={};i[e]={pattern:RegExp(/(<__[\s\S]*?>)(?:\s*|[\s\S])*?(?=<\/__>)/.source.replace(/__/g,e),"i"),lookbehind:!0,greedy:!0,inside:r},c.languages.insertBefore("markup","cdata",i)}}),c.languages.xml=c.languages.extend("markup",{}),c.languages.html=c.languages.markup,c.languages.mathml=c.languages.markup,c.languages.svg=c.languages.markup,function(e){var t=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/@[\w-]+/}},url:{pattern:RegExp("url\\((?:"+t.source+"|[^\n\r()]*)\\)","i"),inside:{function:/^url/i,punctuation:/^\(|\)$/}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+t.source+")*?(?=\\s*\\{)"),string:{pattern:t,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),e.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:e.languages.css}},alias:"language-css"}},n.tag))}(c),c.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},c.languages.javascript=c.languages.extend("clike",{"class-name":[c.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/--|\+\+|\*\*=?|=>|&&|\|\||[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?[.?]?|[~:]/}),c.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,c.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=(?:\s|\/\*[\s\S]*?\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:c.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:c.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:c.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:c.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),c.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:c.languages.javascript}},string:/[\s\S]+/}}}),c.languages.markup&&c.languages.markup.tag.addInlined("script","javascript"),c.languages.js=c.languages.javascript,"undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector&&(self.Prism.fileHighlight=function(e){e=e||document;var l={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"};Array.prototype.slice.call(e.querySelectorAll("pre[data-src]")).forEach(function(e){if(!e.hasAttribute("data-src-loaded")){for(var t,n=e.getAttribute("data-src"),r=e,i=/\blang(?:uage)?-([\w-]+)\b/i;r&&!i.test(r.className);)r=r.parentNode;if(r&&(t=(e.className.match(i)||[,""])[1]),!t){var a=(n.match(/\.(\w+)$/)||[,""])[1];t=l[a]||a}var s=document.createElement("code");s.className="language-"+t,e.textContent="",s.textContent="Loading…",e.appendChild(s);var o=new XMLHttpRequest;o.open("GET",n,!0),o.onreadystatechange=function(){4==o.readyState&&(o.status<400&&o.responseText?(s.textContent=o.responseText,c.highlightElement(s),e.setAttribute("data-src-loaded","")):400<=o.status?s.textContent="✖ Error "+o.status+" while fetching file: "+o.statusText:s.textContent="✖ Error: File does not exist or is empty")},o.send(null)}})},document.addEventListener("DOMContentLoaded",function(){self.Prism.fileHighlight()}))});function ye(e,t){return"___"+e.toUpperCase()+t+"___"}ve=Prism,Object.defineProperties(ve.languages["markup-templating"]={},{buildPlaceholders:{value:function(r,i,e,a){if(r.language===i){var s=r.tokenStack=[];r.code=r.code.replace(e,function(e){if("function"==typeof a&&!a(e))return e;for(var t,n=s.length;-1!==r.code.indexOf(t=ye(i,n));)++n;return s[n]=e,t}),r.grammar=ve.languages.markup}}},tokenizePlaceholders:{value:function(d,g){if(d.language===g&&d.tokenStack){d.grammar=ve.languages[g];var f=0,m=Object.keys(d.tokenStack);!function e(t){for(var n=0;n=m.length);n++){var r=t[n];if("string"==typeof r||r.content&&"string"==typeof r.content){var i=m[f],a=d.tokenStack[i],s="string"==typeof r?r:r.content,o=ye(g,i),l=s.indexOf(o);if(-1 ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|\\n*|\\n*|?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:h,table:h,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,text:/^[^\n]+/};function l(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||m.defaults,this.rules=y.normal,this.options.pedantic?this.rules=y.pedantic:this.options.gfm&&(this.rules=y.gfm)}y._label=/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,y._title=/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/,y.def=e(y.def).replace("label",y._label).replace("title",y._title).getRegex(),y.bullet=/(?:[*+-]|\d{1,9}\.)/,y.item=/^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/,y.item=e(y.item,"gm").replace(/bull/g,y.bullet).getRegex(),y.list=e(y.list).replace(/bull/g,y.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+y.def.source+")").getRegex(),y._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",y._comment=//,y.html=e(y.html,"i").replace("comment",y._comment).replace("tag",y._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),y.paragraph=e(y._paragraph).replace("hr",y.hr).replace("heading"," {0,3}#{1,6} +").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}|~{3,})[^`\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)").replace("tag",y._tag).getRegex(),y.blockquote=e(y.blockquote).replace("paragraph",y.paragraph).getRegex(),y.normal=d({},y),y.gfm=d({},y.normal,{nptable:/^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,table:/^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/}),y.pedantic=d({},y.normal,{html:e("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?\\1> *(?:\\n{2,}|\\s*$)| \\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",y._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,fences:h,paragraph:e(y.normal._paragraph).replace("hr",y.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",y.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()}),l.rules=y,l.lex=function(e,t){return new l(t).lex(e)},l.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},l.prototype.token=function(e,t){var n,r,i,a,s,o,l,c,u,h,p,d,g,f,m,v;for(e=e.replace(/^ +$/gm,"");e;)if((i=this.rules.newline.exec(e))&&(e=e.substring(i[0].length),1 ?/gm,""),this.token(i,t),this.tokens.push({type:"blockquote_end"});else if(i=this.rules.list.exec(e)){for(e=e.substring(i[0].length),l={type:"list_start",ordered:f=1<(a=i[2]).length,start:f?+a:"",loose:!1},this.tokens.push(l),n=!(c=[]),g=(i=i[0].match(this.rules.item)).length,p=0;p?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:h,tag:"^comment|^[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,strong:/^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,em:/^_([^\s_])_(?!_)|^\*([^\s*<\[])\*(?!\*)|^_([^\s<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s<"][\s\S]*?[^\s\*])\*(?!\*|[^\spunctuation])|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:h,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\?@\\[^_{|}~",n.em=e(n.em).replace(/punctuation/g,n._punctuation).getRegex(),n._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,n._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,n._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,n.autolink=e(n.autolink).replace("scheme",n._scheme).replace("email",n._email).getRegex(),n._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,n.tag=e(n.tag).replace("comment",y._comment).replace("attribute",n._attribute).getRegex(),n._label=/(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,n._href=/<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/,n._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,n.link=e(n.link).replace("label",n._label).replace("href",n._href).replace("title",n._title).getRegex(),n.reflink=e(n.reflink).replace("label",n._label).getRegex(),n.normal=d({},n),n.pedantic=d({},n.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,link:e(/^!?\[(label)\]\((.*?)\)/).replace("label",n._label).getRegex(),reflink:e(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",n._label).getRegex()}),n.gfm=d({},n.normal,{escape:e(n.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~+(?=\S)([\s\S]*?\S)~+/,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\/i.test(a[0])&&(this.inLink=!1),!this.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(a[0])?this.inRawBlock=!0:this.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(a[0])&&(this.inRawBlock=!1),e=e.substring(a[0].length),o+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0];else if(a=this.rules.link.exec(e)){var l=g(a[2],"()");if(-1$/,"$1"),o+=this.outputLink(a,{href:u.escapes(r),title:u.escapes(i)}),this.inLink=!1}else if((a=this.rules.reflink.exec(e))||(a=this.rules.nolink.exec(e))){if(e=e.substring(a[0].length),t=(a[2]||a[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){o+=a[0].charAt(0),e=a[0].substring(1)+e;continue}this.inLink=!0,o+=this.outputLink(a,t),this.inLink=!1}else if(a=this.rules.strong.exec(e))e=e.substring(a[0].length),o+=this.renderer.strong(this.output(a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.em.exec(e))e=e.substring(a[0].length),o+=this.renderer.em(this.output(a[6]||a[5]||a[4]||a[3]||a[2]||a[1]));else if(a=this.rules.code.exec(e))e=e.substring(a[0].length),o+=this.renderer.codespan(k(a[2].trim(),!0));else if(a=this.rules.br.exec(e))e=e.substring(a[0].length),o+=this.renderer.br();else if(a=this.rules.del.exec(e))e=e.substring(a[0].length),o+=this.renderer.del(this.output(a[1]));else if(a=this.rules.autolink.exec(e))e=e.substring(a[0].length),r="@"===a[2]?"mailto:"+(n=k(this.mangle(a[1]))):n=k(a[1]),o+=this.renderer.link(r,null,n);else if(this.inLink||!(a=this.rules.url.exec(e))){if(a=this.rules.text.exec(e))e=e.substring(a[0].length),this.inRawBlock?o+=this.renderer.text(this.options.sanitize?this.options.sanitizer?this.options.sanitizer(a[0]):k(a[0]):a[0]):o+=this.renderer.text(k(this.smartypants(a[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else{if("@"===a[2])r="mailto:"+(n=k(a[0]));else{for(;s=a[0],a[0]=this.rules._backpedal.exec(a[0])[0],s!==a[0];);n=k(a[0]),r="www."===a[1]?"http://"+n:n}e=e.substring(a[0].length),o+=this.renderer.link(r,null,n)}return o},u.escapes=function(e){return e?e.replace(u.rules._escapes,"$1"):e},u.prototype.outputLink=function(e,t){var n=t.href,r=t.title?k(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,r,this.output(e[1])):this.renderer.image(n,r,k(e[1]))},u.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},u.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",r=e.length,i=0;i'+(n?e:k(e,!0))+"\n":""+(n?e:k(e,!0))+" "},r.prototype.blockquote=function(e){return"\n"+e+" \n"},r.prototype.html=function(e){return e},r.prototype.heading=function(e,t,n,r){return this.options.headerIds?"\n":""+e+" \n"},r.prototype.hr=function(){return this.options.xhtml?" \n":" \n"},r.prototype.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+r+">\n"},r.prototype.listitem=function(e){return""+e+" \n"},r.prototype.checkbox=function(e){return" "},r.prototype.paragraph=function(e){return""+e+"
\n"},r.prototype.table=function(e,t){return"\n\n"+e+" \n"+(t=t&&""+t+" ")+"
\n"},r.prototype.tablerow=function(e){return"\n"+e+" \n"},r.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+""+n+">\n"},r.prototype.strong=function(e){return""+e+" "},r.prototype.em=function(e){return""+e+" "},r.prototype.codespan=function(e){return""+e+""},r.prototype.br=function(){return this.options.xhtml?" ":" "},r.prototype.del=function(e){return""+e+""},r.prototype.link=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r='"+n+" "},r.prototype.image=function(e,t,n){if(null===(e=a(this.options.sanitize,this.options.baseUrl,e)))return n;var r=' ":">"},r.prototype.text=function(e){return e},i.prototype.strong=i.prototype.em=i.prototype.codespan=i.prototype.del=i.prototype.text=function(e){return e},i.prototype.link=i.prototype.image=function(e,t,n){return""+n},i.prototype.br=function(){return""},c.parse=function(e,t){return new c(t).parse(e)},c.prototype.parse=function(e){this.inline=new u(e.links,this.options),this.inlineText=new u(e.links,d({},this.options,{renderer:new i})),this.tokens=e.reverse();for(var t="";this.next();)t+=this.tok();return t},c.prototype.next=function(){return this.token=this.tokens.pop(),this.token},c.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},c.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},c.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,p(this.inlineText.output(this.token.text)),this.slugger);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,r,i="",a="";for(n="",e=0;e?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t))for(var n=t;this.seen[n]++,t=n+"-"+this.seen[n],this.seen.hasOwnProperty(t););return this.seen[t]=0,t},k.escapeTest=/[&<>"']/,k.escapeReplace=/[&<>"']/g,k.replacements={"&":"&","<":"<",">":">",'"':""","'":"'"},k.escapeTestNoEncode=/[<>"']|&(?!#?\w+;)/,k.escapeReplaceNoEncode=/[<>"']|&(?!#?\w+;)/g;var s={},o=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;function h(){}function d(e){for(var t,n,r=arguments,i=1;it)n.splice(t);else for(;n.lengthAn error occurred:
"+k(e.message+"",!0)+" ";throw e}}h.exec=h,m.options=m.setOptions=function(e){return d(m.defaults,e),m},m.getDefaults=function(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:new r,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,xhtml:!1}},m.defaults=m.getDefaults(),m.Parser=c,m.parser=c.parse,m.Renderer=r,m.TextRenderer=i,m.Lexer=l,m.lexer=l.lex,m.InlineLexer=u,m.inlineLexer=u.output,m.Slugger=t,m.parse=m,v.exports=m}()}),we={},xe={markdown:function(e){return{url:e}},mermaid:function(e){return{url:e}},iframe:function(e,t){return{html:'"}},video:function(e,t){return{html:'Not Support "}},audio:function(e,t){return{html:'Not Support "}},code:function(e,t){var n=e.match(/\.(\w+)$/);return"md"===(n=t||n&&n[1])&&(n="markdown"),{url:e,lang:n}}},_e=function(i,e){var a=this;this.config=i,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=i.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?i.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var s,t=this._initRenderer();this.heading=t.heading;var n=i.markdown||{};s=r(n)?n(ke,t):(ke.setOptions(d(n,{renderer:d(t,n.renderer)})),ke),this._marked=s,this.compile=function(n){var r=!0,e=o(function(e){r=!1;var t="";return n?(t=c(n)?s(n):s.parser(n),t=i.noEmoji?t:function(e){return e.replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(/:(\w+?):/gi,window.emojify||de).replace(/__colon__/g,":")}(t),pe.clear(),t):n})(n),t=a.router.parse().file;return r?a.toc=a.cacheTOC[t]:a.cacheTOC[t]=[].concat(a.toc),e}};_e.prototype.compileEmbed=function(e,t){var n,r=ge(t),i=r.str,a=r.config;if(t=i,a.include){var s;if(H(e)||(e=D(this.contentBase,I(this.router.getCurrentPath()),e)),a.type&&(s=xe[a.type]))(n=s.call(this,e,t)).type=a.type;else{var o="code";/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(n=xe[o].call(this,e,t)).type=o}return n.fragment=a.fragment,n}},_e.prototype._matchNotCompileLink=function(e){for(var t=this.config.noCompileLinks||[],n=0;n'+r+" "},r.code=function(e){return e.renderer.code=function(e,t){void 0===t&&(t="");var n=be.languages[t]||be.languages.markup;return''+be.highlight(e.replace(/@DOCSIFY_QM@/g,"`"),n)+" "}}({renderer:e}),r.link=function(e){var t=e.renderer,o=e.router,l=e.linkTarget,c=e.compilerClass;return t.link=function(e,t,n){void 0===t&&(t="");var r=[],i=ge(t),a=i.str,s=i.config;return t=a,H(e)||c._matchNotCompileLink(e)||s.ignore?(!H(e)&&e.startsWith("./")&&(e=document.URL.replace(/\/(?!.*\/).*/,"/").replace("#/./","")+e),r.push(0===e.indexOf("mailto:")?"":'target="'+l+'"')):(e===c.config.homepage&&(e="README"),e=o.toURL(e,null,o.getCurrentPath())),s.target&&r.push('target="'+s.target+'"'),s.disabled&&(r.push("disabled"),e="javascript:void(0)"),s.class&&r.push('class="'+s.class+'"'),s.id&&r.push('id="'+s.id+'"'),t&&r.push('title="'+t+'"'),'"+n+" "}}({renderer:e,router:l,linkTarget:t,compilerClass:c}),r.paragraph=function(e){return e.renderer.paragraph=function(e){return/^!>/.test(e)?oe("tip",e):/^\?>/.test(e)?oe("warn",e):""+e+"
"}}({renderer:e}),r.image=function(e){var t=e.renderer,h=e.contentBase,p=e.router;return t.image=function(e,t,n){var r=e,i=[],a=ge(t),s=a.str,o=a.config;if(t=s,o["no-zoom"]&&i.push("data-no-zoom"),t&&i.push('title="'+t+'"'),o.size){var l=o.size.split("x"),c=l[0],u=l[1];u?i.push('width="'+c+'" height="'+u+'"'):i.push('width="'+c+'" height="'+c+'"')}return o.class&&i.push('class="'+o.class+'"'),o.id&&i.push('id="'+o.id+'"'),H(e)||(r=D(h,I(p.getCurrentPath()),e)),0 ":' "}}({renderer:e,contentBase:n,router:l}),r.list=function(e){return e.renderer.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+" "+[//.test(e.split('class="task-list"')[0])?'class="task-list"':"",n&&1"+e+""+r+">"}}({renderer:e}),r.listitem=function(e){return e.renderer.listitem=function(e){return/^(]*>)/.test(e)?''+e+" ":""+e+" "}}({renderer:e}),e.origin=r,e},_e.prototype.sidebar=function(e,t){var n=this.toc,r=this.router.getCurrentPath(),i="";if(e)i=this.compile(e);else{for(var a=0;a{inner}"),this.cacheTree[r]=l}return i},_e.prototype.subSidebar=function(e){if(e){var t=this.router.getCurrentPath(),n=this.cacheTree,r=this.toc;r[0]&&r[0].ignoreAllSubs&&r.splice(0),r[0]&&1===r[0].level&&r.shift();for(var i=0;i\n'+e+"\n"}]).links={}:(t=[{type:"html",text:e}]).links={};s({token:a,embedToken:t}),++c>=l&&s({})}}(t);t.embed.url?L(t.embed.url).then(r):r(t.embed.html)}}({compile:o,embedTokens:c,fetch:t},function(e){var t=e.embedToken,n=e.token;if(n){var r=n.index+p;d(h,t.links),l=l.slice(0,r).concat(t,l.slice(r+1)),p+=t.length-1}else Ae[a]=l.concat(),l.links=Ae[a].links=h,i(l)})}var Ee=/([^{]*?)\w(?=\})/g,Ce={YYYY:"getFullYear",YY:"getYear",MM:function(e){return e.getMonth()+1},DD:"getDate",HH:"getHours",mm:"getMinutes",ss:"getSeconds",fff:"getMilliseconds"};function Fe(){var e=y(".markdown-section>script").filter(function(e){return!/template/.test(e.type)})[0];if(!e)return!1;var t=e.innerText.trim();if(!t)return!1;setTimeout(function(e){window.__EXECUTE_RESULT__=new Function(t)()},0)}function Le(e,t,n){return t="function"==typeof n?n(t):"string"==typeof n?function(r,i){var a=[],s=0;return r.replace(Ee,function(t,e,n){a.push(r.substring(s,n-1)),s=n+=t.length+1,a.push(i&&i[t]||function(e){return("00"+("string"==typeof Ce[t]?e[Ce[t]]():Ce[t](e))).slice(-t.length)})}),s!==r.length&&a.push(r.substring(s)),function(e){for(var t="",n=0,r=e||new Date;n404 - Not found",this._renderTo(".markdown-section",e),this.config.loadSidebar||this._renderSidebar(),!1===this.config.executeScript||void 0===window.Vue||Fe()?this.config.executeScript&&Fe():setTimeout(function(e){var t=window.__EXECUTE_RESULT__;t&&t.$destroy&&t.$destroy(),window.__EXECUTE_RESULT__=(new window.Vue).$mount("#main")},0)}function Re(e){var t=e.config;e.compiler=new _e(t,e.router),window.__current_docsify_compiler__=e.compiler;var n=t.el||"#app",r=b("nav")||k("nav"),i=b(n),a="",s=v;if(i){if(t.repo&&(a+=function(e,t){return e?(/\/\//.test(e)||(e="https://github.com/"+e),' '):""}(t.repo,t.cornerExternalLinkTarge)),t.coverpage&&(a+=function(){var e=", 100%, 85%";return''}()),t.logo){var o=/^data:image/.test(t.logo),l=/(?:http[s]?:)?\/\//.test(t.logo),c=/^\./.test(t.logo);o||l||c||(t.logo=D(e.router.getBasePath(),t.logo))}a+=function(e){var t=e.name?e.name:"",n='';return(g?n+"":""+n)+' '}(t),e._renderTo(i,a,!0)}else e.rendered=!0;t.mergeNavbar&&g?s=b(".sidebar"):(r.classList.add("app-nav"),t.repo||r.classList.add("no-badge")),t.loadNavbar&&x(s,r),t.themeColor&&(p.head.appendChild(k("div",function(e){return""}(t.themeColor)).firstElementChild),function(n){if(!(window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)"))){var e=y("style:not(.inserted),link");[].forEach.call(e,function(e){if("STYLE"===e.nodeName)T(e,n);else if("LINK"===e.nodeName){var t=e.getAttribute("href");if(!/\.css$/.test(t))return;L(t).then(function(e){var t=k("style",e);f.appendChild(t),T(t,n)})}})}}(t.themeColor)),e._updateRender(),A(v,"ready")}var Oe={};function Pe(e){this.config=e}function ze(e){var t=location.href.indexOf("#");location.replace(location.href.slice(0,0<=t?t:0)+"#"+e)}Pe.prototype.getBasePath=function(){return this.config.basePath},Pe.prototype.getFile=function(e,t){void 0===e&&(e=this.getCurrentPath());var n=this.config,r=this.getBasePath(),i="string"==typeof n.ext?n.ext:".md";return e=(e=function(e,t){return new RegExp("\\.("+t.replace(/^\./,"")+"|html)$","g").test(e)?e:/\/$/g.test(e)?e+"README"+t:""+e+t}(e=n.alias?function e(t,n,r){var i=Object.keys(n).filter(function(e){return(Oe[e]||(Oe[e]=new RegExp("^"+e+"$"))).test(t)&&t!==r})[0];return i?e(t.replace(Oe[i],n[i]),n,t):t}(e,n.alias):e,i))==="/README"+i&&n.homepage||e,e=H(e)?e:D(r,e),t&&(e=e.replace(new RegExp("^"+r),"")),e},Pe.prototype.onchange=function(e){void 0===e&&(e=h),e()},Pe.prototype.getCurrentPath=function(){},Pe.prototype.normalize=function(){},Pe.prototype.parse=function(){},Pe.prototype.toURL=function(e,t,n){var r=n&&"#"===e[0],i=this.parse(U(e));if(i.query=d({},i.query,t),e=(e=i.path+q(i.query)).replace(/\.md(\?)|\.md$/,"$1"),r){var a=n.indexOf("?");e=(0([^<]*?)
$');if(i){if("color"===i[2])n.style.background=i[1]+(i[3]||"");else{var a=i[1];A(n,"add","has-mask"),H(i[1])||(a=D(this.router.getBasePath(),i[1])),n.style.backgroundImage="url("+a+")",n.style.backgroundSize="cover",n.style.backgroundPosition="center center"}r=r.replace(i[0],"")}this._renderTo(".cover-main",r),P()}else A(n,"remove","show")},De._updateRender=function(){!function(e){var t=m(".app-name-link"),n=e.config.nameLink,r=e.route.path;if(t)if(c(e.config.nameLink))t.setAttribute("href",n);else if("object"==typeof n){var i=Object.keys(n).filter(function(e){return-1 nav {
+ display: none;
+}
+div#app {
+ font-size: 30px;
+ font-weight: lighter;
+ margin: 40vh auto;
+ text-align: center;
+}
+div#app:empty::before {
+ content: 'Loading...';
+}
+.emoji {
+ height: 1.2rem;
+ vertical-align: middle;
+}
+.progress {
+ background-color: var(--theme-color, #42b983);
+ height: 2px;
+ left: 0px;
+ position: fixed;
+ right: 0px;
+ top: 0px;
+ transition: width 0.2s, opacity 0.4s;
+ width: 0%;
+ z-index: 999999;
+}
+.search a:hover {
+ color: var(--theme-color, #42b983);
+}
+.search .search-keyword {
+ color: var(--theme-color, #42b983);
+ font-style: normal;
+ font-weight: bold;
+}
+html,
+body {
+ height: 100%;
+}
+body {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ color: #34495e;
+ font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
+ font-size: 15px;
+ letter-spacing: 0;
+ margin: 0;
+ overflow-x: hidden;
+}
+img {
+ max-width: 100%;
+}
+a[disabled] {
+ cursor: not-allowed;
+ opacity: 0.6;
+}
+kbd {
+ border: solid 1px #ccc;
+ border-radius: 3px;
+ display: inline-block;
+ font-size: 12px !important;
+ line-height: 12px;
+ margin-bottom: 3px;
+ padding: 3px 5px;
+ vertical-align: middle;
+}
+li input[type='checkbox'] {
+ margin: 0 0.2em 0.25em 0;
+ vertical-align: middle;
+}
+.app-nav {
+ margin: 25px 60px 0 0;
+ position: absolute;
+ right: 0;
+ text-align: right;
+ z-index: 10;
+/* navbar dropdown */
+}
+.app-nav.no-badge {
+ margin-right: 25px;
+}
+.app-nav p {
+ margin: 0;
+}
+.app-nav > a {
+ margin: 0 1rem;
+ padding: 5px 0;
+}
+.app-nav ul,
+.app-nav li {
+ display: inline-block;
+ list-style: none;
+ margin: 0;
+}
+.app-nav a {
+ color: inherit;
+ font-size: 16px;
+ text-decoration: none;
+ transition: color 0.3s;
+}
+.app-nav a:hover {
+ color: var(--theme-color, #42b983);
+}
+.app-nav a.active {
+ border-bottom: 2px solid var(--theme-color, #42b983);
+ color: var(--theme-color, #42b983);
+}
+.app-nav li {
+ display: inline-block;
+ margin: 0 1rem;
+ padding: 5px 0;
+ position: relative;
+ cursor: pointer;
+}
+.app-nav li ul {
+ background-color: #fff;
+ border: 1px solid #ddd;
+ border-bottom-color: #ccc;
+ border-radius: 4px;
+ box-sizing: border-box;
+ display: none;
+ max-height: calc(100vh - 61px);
+ overflow-y: auto;
+ padding: 10px 0;
+ position: absolute;
+ right: -15px;
+ text-align: left;
+ top: 100%;
+ white-space: nowrap;
+}
+.app-nav li ul li {
+ display: block;
+ font-size: 14px;
+ line-height: 1rem;
+ margin: 0;
+ margin: 8px 14px;
+ white-space: nowrap;
+}
+.app-nav li ul a {
+ display: block;
+ font-size: inherit;
+ margin: 0;
+ padding: 0;
+}
+.app-nav li ul a.active {
+ border-bottom: 0;
+}
+.app-nav li:hover ul {
+ display: block;
+}
+.github-corner {
+ border-bottom: 0;
+ position: fixed;
+ right: 0;
+ text-decoration: none;
+ top: 0;
+ z-index: 1;
+}
+.github-corner:hover .octo-arm {
+ -webkit-animation: octocat-wave 560ms ease-in-out;
+ animation: octocat-wave 560ms ease-in-out;
+}
+.github-corner svg {
+ color: #fff;
+ fill: var(--theme-color, #42b983);
+ height: 80px;
+ width: 80px;
+}
+main {
+ display: block;
+ position: relative;
+ width: 100vw;
+ height: 100%;
+ z-index: 0;
+}
+main.hidden {
+ display: none;
+}
+.anchor {
+ display: inline-block;
+ text-decoration: none;
+ transition: all 0.3s;
+}
+.anchor span {
+ color: #34495e;
+}
+.anchor:hover {
+ text-decoration: underline;
+}
+.sidebar {
+ border-right: 1px solid rgba(0,0,0,0.07);
+ overflow-y: auto;
+ padding: 40px 0 0;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ transition: transform 250ms ease-out;
+ width: 300px;
+ z-index: 20;
+}
+.sidebar > h1 {
+ margin: 0 auto 1rem;
+ font-size: 1.5rem;
+ font-weight: 300;
+ text-align: center;
+}
+.sidebar > h1 a {
+ color: inherit;
+ text-decoration: none;
+}
+.sidebar > h1 .app-nav {
+ display: block;
+ position: static;
+}
+.sidebar .sidebar-nav {
+ line-height: 2em;
+ padding-bottom: 40px;
+}
+.sidebar li.collapse .app-sub-sidebar {
+ display: none;
+}
+.sidebar ul {
+ margin: 0 0 0 15px;
+ padding: 0;
+}
+.sidebar li > p {
+ font-weight: 700;
+ margin: 0;
+}
+.sidebar ul,
+.sidebar ul li {
+ list-style: none;
+}
+.sidebar ul li a {
+ border-bottom: none;
+ display: block;
+}
+.sidebar ul li ul {
+ padding-left: 20px;
+}
+.sidebar::-webkit-scrollbar {
+ width: 4px;
+}
+.sidebar::-webkit-scrollbar-thumb {
+ background: transparent;
+ border-radius: 4px;
+}
+.sidebar:hover::-webkit-scrollbar-thumb {
+ background: rgba(136,136,136,0.4);
+}
+.sidebar:hover::-webkit-scrollbar-track {
+ background: rgba(136,136,136,0.1);
+}
+.sidebar-toggle {
+ background-color: transparent;
+ background-color: rgba(255,255,255,0.8);
+ border: 0;
+ outline: none;
+ padding: 10px;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ text-align: center;
+ transition: opacity 0.3s;
+ width: 284px;
+ z-index: 30;
+ cursor: pointer;
+}
+.sidebar-toggle:hover .sidebar-toggle-button {
+ opacity: 0.4;
+}
+.sidebar-toggle span {
+ background-color: var(--theme-color, #42b983);
+ display: block;
+ margin-bottom: 4px;
+ width: 16px;
+ height: 2px;
+}
+body.sticky .sidebar,
+body.sticky .sidebar-toggle {
+ position: fixed;
+}
+.content {
+ padding-top: 60px;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 300px;
+ transition: left 250ms ease;
+}
+.markdown-section {
+ margin: 0 auto;
+ max-width: 80%;
+ padding: 30px 15px 40px 15px;
+ position: relative;
+}
+.markdown-section > * {
+ box-sizing: border-box;
+ font-size: inherit;
+}
+.markdown-section > :first-child {
+ margin-top: 0 !important;
+}
+.markdown-section hr {
+ border: none;
+ border-bottom: 1px solid #eee;
+ margin: 2em 0;
+}
+.markdown-section iframe {
+ border: 1px solid #eee;
+/* fix horizontal overflow on iOS Safari */
+ width: 1px;
+ min-width: 100%;
+}
+.markdown-section table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ display: block;
+ margin-bottom: 1rem;
+ overflow: auto;
+ width: 100%;
+}
+.markdown-section th {
+ border: 1px solid #ddd;
+ font-weight: bold;
+ padding: 6px 13px;
+}
+.markdown-section td {
+ border: 1px solid #ddd;
+ padding: 6px 13px;
+}
+.markdown-section tr {
+ border-top: 1px solid #ccc;
+}
+.markdown-section tr:nth-child(2n) {
+ background-color: #f8f8f8;
+}
+.markdown-section p.tip {
+ background-color: #f8f8f8;
+ border-bottom-right-radius: 2px;
+ border-left: 4px solid #f66;
+ border-top-right-radius: 2px;
+ margin: 2em 0;
+ padding: 12px 24px 12px 30px;
+ position: relative;
+}
+.markdown-section p.tip:before {
+ background-color: #f66;
+ border-radius: 100%;
+ color: #fff;
+ content: '!';
+ font-family: 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
+ font-size: 14px;
+ font-weight: bold;
+ left: -12px;
+ line-height: 20px;
+ position: absolute;
+ height: 20px;
+ width: 20px;
+ text-align: center;
+ top: 14px;
+}
+.markdown-section p.tip code {
+ background-color: #efefef;
+}
+.markdown-section p.tip em {
+ color: #34495e;
+}
+.markdown-section p.warn {
+ background: rgba(66,185,131,0.1);
+ border-radius: 2px;
+ padding: 1rem;
+}
+.markdown-section ul.task-list > li {
+ list-style-type: none;
+}
+body.close .sidebar {
+ transform: translateX(-300px);
+}
+body.close .sidebar-toggle {
+ width: auto;
+}
+body.close .content {
+ left: 0;
+}
+@media print {
+ .github-corner,
+ .sidebar-toggle,
+ .sidebar,
+ .app-nav {
+ display: none;
+ }
+}
+@media screen and (max-width: 768px) {
+ .github-corner,
+ .sidebar-toggle,
+ .sidebar {
+ position: fixed;
+ }
+ .app-nav {
+ margin-top: 16px;
+ }
+ .app-nav li ul {
+ top: 30px;
+ }
+ main {
+ height: auto;
+ overflow-x: hidden;
+ }
+ .sidebar {
+ left: -300px;
+ transition: transform 250ms ease-out;
+ }
+ .content {
+ left: 0;
+ max-width: 100vw;
+ position: static;
+ padding-top: 20px;
+ transition: transform 250ms ease;
+ }
+ .app-nav,
+ .github-corner {
+ transition: transform 250ms ease-out;
+ }
+ .sidebar-toggle {
+ background-color: transparent;
+ width: auto;
+ padding: 30px 30px 10px 10px;
+ }
+ body.close .sidebar {
+ transform: translateX(300px);
+ }
+ body.close .sidebar-toggle {
+ background-color: rgba(255,255,255,0.8);
+ transition: 1s background-color;
+ width: 284px;
+ padding: 10px;
+ }
+ body.close .content {
+ transform: translateX(300px);
+ }
+ body.close .app-nav,
+ body.close .github-corner {
+ display: none;
+ }
+ .github-corner:hover .octo-arm {
+ -webkit-animation: none;
+ animation: none;
+ }
+ .github-corner .octo-arm {
+ -webkit-animation: octocat-wave 560ms ease-in-out;
+ animation: octocat-wave 560ms ease-in-out;
+ }
+}
+@-webkit-keyframes octocat-wave {
+ 0%, 100% {
+ transform: rotate(0);
+ }
+ 20%, 60% {
+ transform: rotate(-25deg);
+ }
+ 40%, 80% {
+ transform: rotate(10deg);
+ }
+}
+@keyframes octocat-wave {
+ 0%, 100% {
+ transform: rotate(0);
+ }
+ 20%, 60% {
+ transform: rotate(-25deg);
+ }
+ 40%, 80% {
+ transform: rotate(10deg);
+ }
+}
+section.cover {
+ align-items: center;
+ background-position: center center;
+ background-repeat: no-repeat;
+ background-size: cover;
+ height: 100vh;
+ display: none;
+}
+section.cover.show {
+ display: flex;
+}
+section.cover.has-mask .mask {
+ background-color: #fff;
+ opacity: 0.8;
+ position: absolute;
+ top: 0;
+ height: 100%;
+ width: 100%;
+}
+section.cover .cover-main {
+ flex: 1;
+ margin: -20px 16px 0;
+ text-align: center;
+ z-index: 1;
+}
+section.cover a {
+ color: inherit;
+ text-decoration: none;
+}
+section.cover a:hover {
+ text-decoration: none;
+}
+section.cover p {
+ line-height: 1.5rem;
+ margin: 1em 0;
+}
+section.cover h1 {
+ color: inherit;
+ font-size: 2.5rem;
+ font-weight: 300;
+ margin: 0.625rem 0 2.5rem;
+ position: relative;
+ text-align: center;
+}
+section.cover h1 a {
+ display: block;
+}
+section.cover h1 small {
+ bottom: -0.4375rem;
+ font-size: 1rem;
+ position: absolute;
+}
+section.cover blockquote {
+ font-size: 1.5rem;
+ text-align: center;
+}
+section.cover ul {
+ line-height: 1.8;
+ list-style-type: none;
+ margin: 1em auto;
+ max-width: 500px;
+ padding: 0;
+}
+section.cover .cover-main > p:last-child a {
+ border-color: var(--theme-color, #42b983);
+ border-radius: 2rem;
+ border-style: solid;
+ border-width: 1px;
+ box-sizing: border-box;
+ color: var(--theme-color, #42b983);
+ display: inline-block;
+ font-size: 1.05rem;
+ letter-spacing: 0.1rem;
+ margin: 0.5rem 1rem;
+ padding: 0.75em 2rem;
+ text-decoration: none;
+ transition: all 0.15s ease;
+}
+section.cover .cover-main > p:last-child a:last-child {
+ background-color: var(--theme-color, #42b983);
+ color: #fff;
+}
+section.cover .cover-main > p:last-child a:last-child:hover {
+ color: inherit;
+ opacity: 0.8;
+}
+section.cover .cover-main > p:last-child a:hover {
+ color: inherit;
+}
+section.cover blockquote > p > a {
+ border-bottom: 2px solid var(--theme-color, #42b983);
+ transition: color 0.3s;
+}
+section.cover blockquote > p > a:hover {
+ color: var(--theme-color, #42b983);
+}
+body {
+ background-color: #fff;
+}
+/* sidebar */
+.sidebar {
+ background-color: #fff;
+ color: #364149;
+}
+.sidebar li {
+ margin: 6px 0 6px 0;
+}
+.sidebar ul li a {
+ color: #505d6b;
+ font-size: 14px;
+ font-weight: normal;
+ overflow: hidden;
+ text-decoration: none;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.sidebar ul li a:hover {
+ text-decoration: underline;
+}
+.sidebar ul li ul {
+ padding: 0;
+}
+.sidebar ul li.active > a {
+ border-right: 2px solid;
+ color: var(--theme-color, #42b983);
+ font-weight: 600;
+}
+.app-sub-sidebar li::before {
+ content: '-';
+ padding-right: 4px;
+ float: left;
+}
+/* markdown content found on pages */
+.markdown-section h1,
+.markdown-section h2,
+.markdown-section h3,
+.markdown-section h4,
+.markdown-section strong {
+ color: #2c3e50;
+ font-weight: 600;
+}
+.markdown-section a {
+ color: var(--theme-color, #42b983);
+ font-weight: 600;
+}
+.markdown-section h1 {
+ font-size: 2rem;
+ margin: 0 0 1rem;
+}
+.markdown-section h2 {
+ font-size: 1.75rem;
+ margin: 45px 0 0.8rem;
+}
+.markdown-section h3 {
+ font-size: 1.5rem;
+ margin: 40px 0 0.6rem;
+}
+.markdown-section h4 {
+ font-size: 1.25rem;
+}
+.markdown-section h5 {
+ font-size: 1rem;
+}
+.markdown-section h6 {
+ color: #777;
+ font-size: 1rem;
+}
+.markdown-section figure,
+.markdown-section p {
+ margin: 1.2em 0;
+}
+.markdown-section p,
+.markdown-section ul,
+.markdown-section ol {
+ line-height: 1.6rem;
+ word-spacing: 0.05rem;
+}
+.markdown-section ul,
+.markdown-section ol {
+ padding-left: 1.5rem;
+}
+.markdown-section blockquote {
+ border-left: 4px solid var(--theme-color, #42b983);
+ color: #858585;
+ margin: 2em 0;
+ padding-left: 20px;
+}
+.markdown-section blockquote p {
+ font-weight: 600;
+ margin-left: 0;
+}
+.markdown-section iframe {
+ margin: 1em 0;
+}
+.markdown-section em {
+ color: #7f8c8d;
+}
+.markdown-section code {
+ background-color: #f8f8f8;
+ border-radius: 2px;
+ color: #e96900;
+ font-family: 'Roboto Mono', Monaco, courier, monospace;
+ font-size: 0.8rem;
+ margin: 0 2px;
+ padding: 3px 5px;
+ white-space: pre-wrap;
+}
+.markdown-section pre {
+ -moz-osx-font-smoothing: initial;
+ -webkit-font-smoothing: initial;
+ background-color: #f8f8f8;
+ font-family: 'Roboto Mono', Monaco, courier, monospace;
+ line-height: 1.5rem;
+ margin: 1.2em 0;
+ overflow: auto;
+ padding: 0 1.4rem;
+ position: relative;
+ word-wrap: normal;
+}
+/* code highlight */
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+ color: #8e908c;
+}
+.token.namespace {
+ opacity: 0.7;
+}
+.token.boolean,
+.token.number {
+ color: #c76b29;
+}
+.token.punctuation {
+ color: #525252;
+}
+.token.property {
+ color: #c08b30;
+}
+.token.tag {
+ color: #2973b7;
+}
+.token.string {
+ color: var(--theme-color, #42b983);
+}
+.token.selector {
+ color: #6679cc;
+}
+.token.attr-name {
+ color: #2973b7;
+}
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+ color: #22a2c9;
+}
+.token.attr-value,
+.token.control,
+.token.directive,
+.token.unit {
+ color: var(--theme-color, #42b983);
+}
+.token.keyword,
+.token.function {
+ color: #e96900;
+}
+.token.statement,
+.token.regex,
+.token.atrule {
+ color: #22a2c9;
+}
+.token.placeholder,
+.token.variable {
+ color: #3d8fd1;
+}
+.token.deleted {
+ text-decoration: line-through;
+}
+.token.inserted {
+ border-bottom: 1px dotted #202746;
+ text-decoration: none;
+}
+.token.italic {
+ font-style: italic;
+}
+.token.important,
+.token.bold {
+ font-weight: bold;
+}
+.token.important {
+ color: #c94922;
+}
+.token.entity {
+ cursor: help;
+}
+.markdown-section pre > code {
+ -moz-osx-font-smoothing: initial;
+ -webkit-font-smoothing: initial;
+ background-color: #f8f8f8;
+ border-radius: 2px;
+ color: #525252;
+ display: block;
+ font-family: 'Roboto Mono', Monaco, courier, monospace;
+ font-size: 0.8rem;
+ line-height: inherit;
+ margin: 0 2px;
+ max-width: inherit;
+ overflow: inherit;
+ padding: 2.2em 5px;
+ white-space: inherit;
+}
+.markdown-section code::after,
+.markdown-section code::before {
+ letter-spacing: 0.05rem;
+}
+code .token {
+ -moz-osx-font-smoothing: initial;
+ -webkit-font-smoothing: initial;
+ min-height: 1.5rem;
+ position: relative;
+ left: auto;
+}
+pre::after {
+ color: #ccc;
+ content: attr(data-lang);
+ font-size: 0.6rem;
+ font-weight: 600;
+ height: 15px;
+ line-height: 15px;
+ padding: 5px 10px 0;
+ position: absolute;
+ right: 0;
+ text-align: right;
+ top: 0;
+}
diff --git a/src/test/java/de/kosit/validationtool/daemon/CheckHandlerIT.java b/src/test/java/de/kosit/validationtool/daemon/CheckHandlerIT.java
index 86e74f0..9aef88b 100644
--- a/src/test/java/de/kosit/validationtool/daemon/CheckHandlerIT.java
+++ b/src/test/java/de/kosit/validationtool/daemon/CheckHandlerIT.java
@@ -17,15 +17,12 @@ import io.restassured.http.ContentType;
* Testet the Daemon-Mode input , Methoden , Output Content-Type and the success case
*
* @author Roula Antoun
+ * @author Andreas Penski
*/
public class CheckHandlerIT extends BaseIT {
-
private static final String APPLICATION_XML = "application/xml";
-
-
-
@Test
public void makeSureThatSuccessTest() throws IOException {
try ( final InputStream io = Simple.SIMPLE_VALID.toURL().openStream() ) {
@@ -50,15 +47,7 @@ public class CheckHandlerIT extends BaseIT {
return IOUtils.toByteArray(io);
}
- @Test
- public void methodNotAllowedTest() {
- given().when().get("/").then().statusCode(405);
- given().when().put("/").then().statusCode(405);
- given().when().patch("/").then().statusCode(405);
- given().when().delete("/").then().statusCode(405);
- given().when().head("/").then().statusCode(405);
- given().when().options("/").then().statusCode(405);
- }
+
@Test
public void xmlResultTest() throws IOException {
From b02126c1ccbd29232818742c4dce931e87a21dd3 Mon Sep 17 00:00:00 2001
From: Andreas Penski <18-andreas.penski@users.noreply.projekte.kosit.org>
Date: Wed, 6 May 2020 17:11:35 +0200
Subject: [PATCH 27/36] documentation for the daemon
---
README.md | 5 +-
docs/daemon.md | 87 +++++++++++++++++++
pom.xml | 6 ++
.../kosit/validationtool/daemon/Daemon.java | 25 ++++--
.../validationtool/daemon/RoutingHandler.java | 5 +-
5 files changed, 114 insertions(+), 14 deletions(-)
create mode 100644 docs/daemon.md
diff --git a/README.md b/README.md
index ee08010..c4672f7 100644
--- a/README.md
+++ b/README.md
@@ -97,8 +97,5 @@ You can configure it with `-H` for IP Adress and `-P` for port number:
java -jar validationtool--standalone.jar -s -D -H 192.168.1.x -P 8081
```
-You can HTTP-POST to `/` and the response will return the report document as defined in your validator configuration.
-
-Additionally there is the GET `/health` endpoint which can be used by monitoring systems.
-
+Details and further configuration options can be [found here](./docs/daemon.md).
diff --git a/docs/daemon.md b/docs/daemon.md
new file mode 100644
index 0000000..577a4c8
--- /dev/null
+++ b/docs/daemon.md
@@ -0,0 +1,87 @@
+# Validator daemon
+You can also start the validator as a HTTP-Server. This is based JDK http server functionality. Keep that mind, if you want to deploy this
+in production scenarios with heavy load.
+
+## Basic usage
+To just use the validator daemon as is, start the _Daemon-Mode_ with the `-D` option.
+
+```shell
+java -jar validationtool--standalone.jar -s -D
+```
+
+Per default the HTTP-Server listens on _localhost_ at Port 8080.
+
+You can configure it with `-H` for IP Adress and `-P` for port number:
+
+```shell
+java -jar validationtool--standalone.jar -s -D -H 192.168.1.x -P 8081
+```
+
+## Customized usage
+You can also leverage the API to create a customized version of the Daemon. Just instantiate, configure and start the daemon like this:
+
+````java
+Configuration config = Configuration.load(...);
+
+Daemon daemon = new Daemon();
+daemon.setPort("8090");
+// further config goes here
+daemon.startServer(config);
+````
+
+The possible customizations are:
+
+* `bindAddress` - the interface to bind the daemon to
+* `port` - the port to expose
+* `threadCount` - number of worker threads to handle results
+* `guiEnabled` - enable or disable the basic gui with usage information
+
+## Access the http interface
+The validation service listens to `POST`-requests to any server uri. You need to supply the xml/object to validate in the post body.
+The service expects a single plain input in the post body, e.g. `multipart/form-data` is not supported.
+
+Examples:
+
+* `cURL`
+```shell script
+curl --location --request POST 'http://localhost:8080' \
+--header 'Content-Type: application/xml' \
+--data-binary '@/target.xml'
+```
+
+* `java` (Apache HttpClient)
+```java
+HttpClient httpClient = HttpClientBuilder.create().build();
+HttpPost postRequest = new HttpPost("http://localhost:8080/");
+FileEntity entity = new FileEntity(Paths.get("some.xml").toFile(), ContentType.APPLICATION_XML);
+postRequest.setEntity(entity);
+HttpResponse response = httpClient.execute(postRequest);
+System.out.println(IOUtils.toString(response.getEntity().getContent()));
+```
+
+* `javascript`
+```javascript
+var myHeaders = new Headers();
+myHeaders.append("Content-Type", "application/xml");
+
+var file = "";
+
+var requestOptions = {
+ method: 'POST',
+ headers: myHeaders,
+ body: file,
+ redirect: 'follow'
+};
+
+fetch("http://localhost:8080", requestOptions)
+ .then(response => response.text())
+ .then(result => console.log(result))
+ .catch(error => console.log('error', error));
+```
+## Authorization
+There is no mechanism to check, whether client is allowed to consume the service or not. The user is responsible to secure access to the service
+This can be done using infrastructural service like a forwarding proxy (e.g. `nginx` or `apache http server`) or by implementing a custom solution
+
+## Monitoring and administration
+The validation service can be integrated in monitoring solutions like `Icinga` or `Nagios`. There is a `health` endpoint exposed under `/server/health` wich returns
+some basic information about the service like memory consumption, general information about the version and a status `UP` as an XML file.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 34e62a2..694557a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -129,6 +129,12 @@
1.0.0
test
+
+org.apache.httpcomponents
+httpclient
+ 4.5.8
+
+
diff --git a/src/main/java/de/kosit/validationtool/daemon/Daemon.java b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
index 6dde8c6..fa0dcbe 100644
--- a/src/main/java/de/kosit/validationtool/daemon/Daemon.java
+++ b/src/main/java/de/kosit/validationtool/daemon/Daemon.java
@@ -34,17 +34,24 @@ public class Daemon {
private static final int DEFAULT_PORT = 8080;
- private final String hostName;
+ private String bindAddress;
- private final int port;
+ private int port;
- private final int threadCount;
+ private int threadCount;
- @Setter(AccessLevel.PRIVATE)
- private boolean guiDisabled = false;
+ private boolean guiEnabled = true;
- public void disableGui() {
- guiDisabled = true;
+ /**
+ * Create a new daemon.
+ * @param hostname the interface to bind to
+ * @param port the port to expose
+ * @param threadCount the number of working threads
+ */
+ public Daemon(String hostname, int port, int threadCount) {
+ this.bindAddress = hostname;
+ this.port = port;
+ this.threadCount = threadCount;
}
/**
@@ -75,7 +82,7 @@ public class Daemon {
HttpHandler rootHandler;
final DefaultCheck check = new DefaultCheck(config);
final CheckHandler checkHandler = new CheckHandler(check, config.getContentRepository().getProcessor());
- if (!guiDisabled) {
+ if (guiEnabled) {
GuiHandler gui = new GuiHandler();
rootHandler = new RoutingHandler(checkHandler, gui);
} else {
@@ -89,6 +96,6 @@ public class Daemon {
}
private InetSocketAddress getSocket() {
- return new InetSocketAddress(defaultIfBlank(this.hostName, DEFAULT_HOST), this.port > 0 ? this.port : DEFAULT_PORT);
+ return new InetSocketAddress(defaultIfBlank(this.bindAddress, DEFAULT_HOST), this.port > 0 ? this.port : DEFAULT_PORT);
}
}
diff --git a/src/main/java/de/kosit/validationtool/daemon/RoutingHandler.java b/src/main/java/de/kosit/validationtool/daemon/RoutingHandler.java
index d1a7675..d15a39c 100644
--- a/src/main/java/de/kosit/validationtool/daemon/RoutingHandler.java
+++ b/src/main/java/de/kosit/validationtool/daemon/RoutingHandler.java
@@ -6,8 +6,11 @@ import lombok.RequiredArgsConstructor;
import java.io.IOException;
+/**
+ * A simple handler which routes between the {@link CheckHandler} and the {@link GuiHandler} depending on the request.
+ */
@RequiredArgsConstructor
-public class RoutingHandler extends BaseHandler {
+class RoutingHandler extends BaseHandler {
private final CheckHandler checkHandler;
From 9e3b12685b7f6735a4056c0544fa17aa4158ed33 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Thu, 7 May 2020 17:10:33 +0200
Subject: [PATCH 28/36] Support for DOMSource and other Sources
---
docs/api.md | 2 +-
.../validationtool/api/InputFactory.java | 9 +++-
.../impl/input/SourceInput.java | 50 +++++++++++--------
.../validationtool/api/InputFactoryTest.java | 34 +++++++++++++
4 files changed, 72 insertions(+), 23 deletions(-)
diff --git a/docs/api.md b/docs/api.md
index c59bc23..14add56 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -4,7 +4,7 @@ The Validator offers an API which allows you to integrate Validator in your own
## Dependency Management
-Currently, we *do not* deploy to Maven Central or similar. Hence you need to build and optionally deploy the Validator artifacts to your own
+Currently, we *do not* deploy to Maven Central or similar. Hence, you need to build and optionally deploy the Validator artifacts to your own
shared (or local) repository (see for example [Maven Documentation](https://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html)).
### Maven
diff --git a/src/main/java/de/kosit/validationtool/api/InputFactory.java b/src/main/java/de/kosit/validationtool/api/InputFactory.java
index 8cf380f..b491cb2 100644
--- a/src/main/java/de/kosit/validationtool/api/InputFactory.java
+++ b/src/main/java/de/kosit/validationtool/api/InputFactory.java
@@ -164,8 +164,10 @@ public class InputFactory {
}
/**
- * Reads a test document from a {@link Source}.
- *
+ * Reads a test document from a {@link Source}.
+ * Note: computing the hashcode is only supported for {@link StreamSource}. You can not directly use other {@link Source
+ * Soures}. You need to supply the hashcode for identification then.
+ *
* @param source source
* @return an {@link Input}
*/
@@ -175,6 +177,9 @@ public class InputFactory {
/**
* Reads a test document from a {@link Source} using a specified digest algorithm.
+ *
+ * Note: computing the hashcode is only supported for {@link StreamSource}. You can not directly use other {@link Source
+ * Soures}. You need to supply the hashcode for identification then.
*
* @param source source
* @param digestAlgorithm the digest algorithm
diff --git a/src/main/java/de/kosit/validationtool/impl/input/SourceInput.java b/src/main/java/de/kosit/validationtool/impl/input/SourceInput.java
index f11e65f..cb9f929 100644
--- a/src/main/java/de/kosit/validationtool/impl/input/SourceInput.java
+++ b/src/main/java/de/kosit/validationtool/impl/input/SourceInput.java
@@ -7,13 +7,25 @@ import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.io.input.ReaderInputStream;
-import org.apache.commons.lang3.NotImplementedException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
/**
- * A validator {@link de.kosit.validationtool.api.Input} based an on a {@link Source}.
+ * A validator {@link de.kosit.validationtool.api.Input} based on a {@link Source}.
+ *
+ * Note: The various implementations of {@link Source} varies wether the can be read twice or no. This implementation
+ * tries to handle this with respect document identification (hashcode).
+ *
+ * This class is know to work with:
+ *
+ * {@link StreamSource} - both {@link java.io.InputStream} based and {@link java.io.Reader} based
+ * {@link javax.xml.transform.dom.DOMSource}
+ * {@link javax.xml.bind.util.JAXBSource}
+ *
+ *
+ * Other {@link Source Sources} may work as well, please try and let us know.
+ *
*
* @author Andreas Penski
*/
@@ -40,26 +52,23 @@ public class SourceInput extends AbstractInput {
}
private void validate() {
- if (!isSupported()) {
+ if (!isHashcodeComputed() && !isSupported()) {
throw new IllegalStateException("Unsupported source. Only StreamSource supported yet");
}
- if (((StreamSource) this.source).getInputStream() == null && !isHashcodeComputed()) {
+ if (!isHashcodeComputed() && ((StreamSource) this.source).getInputStream() == null) {
log.warn("No hashcode supplied, will wrap the reader using system default charset");
}
}
@Override
public Source getSource() throws IOException {
- if (!isSupported()) {
+ if (!isHashcodeComputed() && !isSupported()) {
throw new IllegalStateException("Unsupported source. Only InputStream-based StreamSource supported yet");
}
- if (isWrappingRequired()) {
- return wrap();
- }
if (isConsumed()) {
throw new IllegalStateException("A SourceInput can only read once");
}
- return this.source;
+ return isHashcodeComputed() ? this.source : wrappedSource();
}
private boolean isSupported() {
@@ -67,24 +76,25 @@ public class SourceInput extends AbstractInput {
}
private boolean isConsumed() throws IOException {
- if (!isStreamSource()) {
- throw new NotImplementedException("Supports only StreamSource yet");
- }
- final StreamSource ss = (StreamSource) this.source;
- try {
- return (ss.getInputStream() != null && ss.getInputStream().available() == 0)
- || (ss.getReader() != null && !ss.getReader().ready());
- } catch (final IOException e) {
- log.error("Error checking consumed state", e);
- return true;
+ if (isStreamSource()) {
+
+ final StreamSource ss = (StreamSource) this.source;
+ try {
+ return (ss.getInputStream() != null && ss.getInputStream().available() == 0)
+ || (ss.getReader() != null && !ss.getReader().ready());
+ } catch (final IOException e) {
+ log.error("Error checking consumed state", e);
+ return true;
+ }
}
+ return false;
}
private boolean isStreamSource() {
return this.source instanceof StreamSource;
}
- private Source wrap() {
+ private Source wrappedSource() {
Source result = this.source;
if (isStreamSource()) {
final StreamSource ss = (StreamSource) this.source;
diff --git a/src/test/java/de/kosit/validationtool/api/InputFactoryTest.java b/src/test/java/de/kosit/validationtool/api/InputFactoryTest.java
index 3fc6122..aa8e6e3 100644
--- a/src/test/java/de/kosit/validationtool/api/InputFactoryTest.java
+++ b/src/test/java/de/kosit/validationtool/api/InputFactoryTest.java
@@ -31,14 +31,29 @@ import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
+import de.kosit.validationtool.impl.TestObjectFactory;
import de.kosit.validationtool.impl.input.SourceInput;
+import de.kosit.validationtool.impl.model.Result;
+import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
+
+import net.sf.saxon.dom.NodeOverNodeInfo;
+import net.sf.saxon.s9api.BuildingContentHandler;
+import net.sf.saxon.s9api.DocumentBuilder;
+import net.sf.saxon.s9api.SaxonApiException;
+import net.sf.saxon.s9api.XdmNode;
/**
* Testet den Hashcode-Service.
@@ -163,4 +178,23 @@ public class InputFactoryTest {
InputFactory.read(Simple.NOT_EXISTING);
}
+ @Test
+ public void testDomSource() throws SaxonApiException, SAXException, IOException {
+ final DocumentBuilder builder = TestObjectFactory.createProcessor().newDocumentBuilder();
+
+ final BuildingContentHandler handler = builder.newBuildingContentHandler();
+ handler.startDocument();
+ handler.startElement("http://some.ns", "mynode", "mynode", new AttributesImpl());
+ final Document dom = NodeOverNodeInfo.wrap(handler.getDocumentNode().getUnderlyingNode()).getOwnerDocument();
+ final Input domInput = InputFactory.read(new DOMSource(dom), "MD5", "id".getBytes());
+ assertThat(domInput).isNotNull();
+ final Source source = domInput.getSource();
+ assertThat(source).isNotNull();
+ final Result parsed = Helper.parseDocument(domInput);
+ assertThat(parsed.isValid()).isTrue();
+
+ // read twice
+ assertThat(Helper.parseDocument(domInput).getObject()).isNotNull();
+ }
+
}
From b164d711994b45c0bf29800fa9abe6f2767a0cd7 Mon Sep 17 00:00:00 2001
From: Andreas Penski <18-andreas.penski@users.noreply.projekte.kosit.org>
Date: Mon, 11 May 2020 09:07:40 +0200
Subject: [PATCH 29/36] test for gui handler
---
.../kosit/validationtool/daemon/BaseHandler.java | 7 ++++---
.../validationtool/daemon/GuiHandlerIT.java | 16 ++++++++++++++++
2 files changed, 20 insertions(+), 3 deletions(-)
create mode 100644 src/test/java/de/kosit/validationtool/daemon/GuiHandlerIT.java
diff --git a/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java b/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java
index 4a7fe54..9aaae1c 100644
--- a/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java
+++ b/src/main/java/de/kosit/validationtool/daemon/BaseHandler.java
@@ -29,10 +29,11 @@ abstract class BaseHandler implements HttpHandler {
os.close();
}
- protected static void error(final HttpExchange httpExchange, final int statusCode, final String message) throws IOException {
+ protected static void error(final HttpExchange exchange, final int statusCode, final String message) throws IOException {
final byte[] bytes = message.getBytes();
- httpExchange.sendResponseHeaders(statusCode, bytes.length);
- final OutputStream os = httpExchange.getResponseBody();
+ exchange.getResponseHeaders().add("Content-Type", "text/plain");
+ exchange.sendResponseHeaders(statusCode, bytes.length);
+ final OutputStream os = exchange.getResponseBody();
os.write(bytes);
os.close();
}
diff --git a/src/test/java/de/kosit/validationtool/daemon/GuiHandlerIT.java b/src/test/java/de/kosit/validationtool/daemon/GuiHandlerIT.java
new file mode 100644
index 0000000..bd62201
--- /dev/null
+++ b/src/test/java/de/kosit/validationtool/daemon/GuiHandlerIT.java
@@ -0,0 +1,16 @@
+package de.kosit.validationtool.daemon;
+
+import io.restassured.http.ContentType;
+import org.junit.Test;
+
+import static io.restassured.RestAssured.given;
+
+public class GuiHandlerIT extends BaseIT {
+
+ @Test
+ public void checkGui() {
+ given().when().get("/").then().statusCode(200).and().contentType(ContentType.HTML);
+ given().when().get("/README.md").then().statusCode(200).and().contentType("text/markdown");
+ given().when().get("/unknown.md").then().statusCode(404).and().contentType(ContentType.TEXT);
+ }
+}
From 06a88848db923779ee682a4dac6bbc6de4e65ce5 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Fri, 15 May 2020 10:01:07 +0200
Subject: [PATCH 30/36] prepare docs for release 1.3.0
---
CHANGELOG.md | 7 +++--
README.md | 19 +++++---------
docs/contribute.md | 11 ++++++++
src/main/resources/gui/docs/api.md | 42 +++++++++++++++++++++++++++++-
4 files changed, 61 insertions(+), 18 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 07e06a0..7e1c78f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## next version (unreleased)
+## 1.3.0
### Added
- Added a builder style configuration API to configure scenarios
@@ -19,11 +19,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- CheckConfiguration is deprecated now. Use Configuration.load(...) or Configuration.build(...)
- Overall processing of xml files is based on Saxon s9api. No JAXP or SAX classes are used by
the validator (this further improves performance and memory consumption)
--
-## UNRELEASED
### Fixed
-- Validator was creating invalid createReportInput xml in case of no scenrio match
+- Validator was creating invalid createReportInput xml in case of no scenrio match
+
## 1.2.0
### Added
diff --git a/README.md b/README.md
index c4672f7..d02d2d5 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@
The validator is an XML validation-engine. It validates XML documents against XML Schema and Schematron Rules depending on self defined [scenarios](docs/configurations.md) which are used to fully configure the validation process.
The validator always outputs a [validation report in XML](docs/configurations.md#validators-report) including all validation errors and data about the validation.
+See [architecture](docs/architecture.md) for informations about the actual validation process.
+
## Packages
The validator distribution contains the following artifacts:
@@ -12,16 +14,6 @@ The validator distribution contains the following artifacts:
1. **validationtool-`-java8-standalone.jar**: Uber-JAR for standalone usage with Java JDK 8 containing all dependencies in one jar file. This file file *does not* contain JAXB and depends on the bundled version of the JDK.
1. **libs/***: directory containing all (incl. optional) dependencies of the validator
-## Build
-
-### Requirements
-
-* Maven > 3.0.0
-* Java > 8 update 111
-
-### Procedure
-
- `mvn install` generates two different packages in the `dist` directory:
## Validation Configurations
@@ -29,7 +21,7 @@ The validator is just an engine and does not know anything about XML Documents a
Validation rules and details are defined in [validation scenarios](docs/configurations.md) which are used to fully configure the validation process.
-All configurations are self-contained modules and deployed on their own.
+All configurations are self-contained modules and deployed and developed on their own.
### Third Party Validation Configurations
@@ -64,7 +56,8 @@ You can see more CLI options with
java -jar validationtool--standalone.jar --help
```
-A concrete example with a specific validator configuration can be found on [GitHub](https://github.com/itplr-kosit/validator-configuration-xrechnung)
+A concrete example with a specific validator configuration can be found on
+[GitHub](https://github.com/itplr-kosit/validator-configuration-xrechnung)
### Application User Interface (API / embedded usage)
@@ -97,5 +90,5 @@ You can configure it with `-H` for IP Adress and `-P` for port number:
java -jar validationtool--standalone.jar -s -D -H 192.168.1.x -P 8081
```
-Details and further configuration options can be [found here](./docs/daemon.md).
+Usage details and further configuration options can be [found here](./docs/daemon.md).
diff --git a/docs/contribute.md b/docs/contribute.md
index 191f2e4..76d0d95 100644
--- a/docs/contribute.md
+++ b/docs/contribute.md
@@ -25,3 +25,14 @@ due to historical reasons. This not only works in Eclipse but also in IntelliJ (
The configuration can be found in `.settings`-directory. For IntelliJ this is all set up. Additionally this should work in Eclipse out of the box.
Another potential usage scenario would be to integrate the formatter via git hooks into the commit-pipeline (e.g [Example Hook](https://gist.github.com/ktoso/708972) ).
For other IDEs you are on your own.
+
+## Build
+
+### Requirements
+
+* Maven > 3.0.0
+* Java > 8 update 111
+
+### Procedure
+
+ `mvn install` generates two different packages in the `dist` directory:
\ No newline at end of file
diff --git a/src/main/resources/gui/docs/api.md b/src/main/resources/gui/docs/api.md
index dfd790e..dc84852 100644
--- a/src/main/resources/gui/docs/api.md
+++ b/src/main/resources/gui/docs/api.md
@@ -1 +1,41 @@
-Put content here ;)
\ No newline at end of file
+The validation service listens to `POST`-requests to any server uri. You need to supply the xml/object to validate in the post body.
+The service expects a single plain input in the post body, e.g. `multipart/form-data` is not supported.
+
+Examples:
+
+* `cURL`
+```shell script
+curl --location --request POST 'http://localhost:8080' \
+--header 'Content-Type: application/xml' \
+--data-binary '@/target.xml'
+```
+
+* `java` (Apache HttpClient)
+```java
+HttpClient httpClient = HttpClientBuilder.create().build();
+HttpPost postRequest = new HttpPost("http://localhost:8080/");
+FileEntity entity = new FileEntity(Paths.get("some.xml").toFile(), ContentType.APPLICATION_XML);
+postRequest.setEntity(entity);
+HttpResponse response = httpClient.execute(postRequest);
+System.out.println(IOUtils.toString(response.getEntity().getContent()));
+```
+
+* `javascript`
+```javascript
+var myHeaders = new Headers();
+myHeaders.append("Content-Type", "application/xml");
+
+var file = "";
+
+var requestOptions = {
+ method: 'POST',
+ headers: myHeaders,
+ body: file,
+ redirect: 'follow'
+};
+
+fetch("http://localhost:8080", requestOptions)
+ .then(response => response.text())
+ .then(result => console.log(result))
+ .catch(error => console.log('error', error));
+```
\ No newline at end of file
From 997f5073fea819071a52c39fbdfadf3b97f91e61 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Mon, 18 May 2020 09:22:35 +0200
Subject: [PATCH 31/36] further doc
---
README.md | 19 +++---------
docs/api.md | 31 ++++++++++---------
docs/daemon.md | 25 ++++++++++++---
.../cmd/CommandLineApplication.java | 8 ++++-
4 files changed, 49 insertions(+), 34 deletions(-)
diff --git a/README.md b/README.md
index d02d2d5..4168738 100644
--- a/README.md
+++ b/README.md
@@ -18,19 +18,17 @@ The validator distribution contains the following artifacts:
## Validation Configurations
The validator is just an engine and does not know anything about XML Documents and has no own validation rules.
-
Validation rules and details are defined in [validation scenarios](docs/configurations.md) which are used to fully configure the validation process.
-
All configurations are self-contained modules and deployed and developed on their own.
### Third Party Validation Configurations
Currently, there are two public third party validation configurations available.
-* Validation Configuration for [XRechnung](http://www.xoev.de/de/xrechnung) is available on
+* Validation Configuration for [XRechnung](http://www.xoev.de/de/xrechnung):
* Source code is available on [GitHub](https://github.com/itplr-kosit/validator-configuration-xrechnung)
* [Releases](https://github.com/itplr-kosit/validator-configuration-xrechnung/releases) can also be downloaded
-* Validation Configuration for XGewerbeanzeige
+* Validation Configuration for [XGewerbeanzeige](https://xgewerbeanzeige.de/)
* Source code is available on [GitHub](https://github.com/itplr-kosit/validator-configuration-xgewerbeanzeige)
* [Releases](https://github.com/itplr-kosit/validator-configuration-xgewerbeanzeige/releases) can also be downloaded
@@ -50,7 +48,7 @@ The general way using the CLI is:
java -jar validationtool--standalone.jar -s [OPTIONS] [FILE] [FILE] [FILE] ...
```
-You can see more CLI options with
+The help option displays further CLI options to customize the process:
```shell
java -jar validationtool--standalone.jar --help
@@ -72,7 +70,7 @@ Result validationResult = validator.checkInput(document);
// examine the result here
```
-Details and further configuration options can be [found here](./docs/api.md).
+The [API documentation](./docs/api.md) shows further configuration options.
### Daemon-Mode
@@ -82,13 +80,6 @@ You can also start the validator as an HTTP-Server. Just start it in _Daemon-Mod
java -jar validationtool--standalone.jar -s -D
```
-Per default the HTTP-Server listens on _localhost_ at Port 8080.
-You can configure it with `-H` for IP Adress and `-P` for port number:
-
-```shell
-java -jar validationtool--standalone.jar -s -D -H 192.168.1.x -P 8081
-```
-
-Usage details and further configuration options can be [found here](./docs/daemon.md).
+The [daemon documentation](./docs/daemon.md) shows more usage details and further configuration options.
diff --git a/docs/api.md b/docs/api.md
index 14add56..bea4a12 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -1,6 +1,6 @@
# Validator API
-The Validator offers an API which allows you to integrate Validator in your own applications.
+The Validator offers an API which allows you to integrate the Validator in your own applications.
## Dependency Management
@@ -87,11 +87,13 @@ The `Result` interface has convenience methods to retrieve details about XSD val
[Result.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Result.java) for details.
Initializing all XML artifacts and XSLT-executables is expensive. The `Check` instance is *threadsafe* and keeps all artifacts. Therefore,
-we recommend the re-use of an `Check` instance.
+we recommend the re-use of a `Check` instance.
-The only input `de.kosit.validationtool.api.Input` which can be created by various methods of `de.kosit.validationtool.api.InputFactory`.
-The `InputFactory` calculates a hash sum for each Input which is also written to the Report. _SHA-256_ from the JDK is the default algorithm.
-It can be changed using the `read`-methods of `InputFactory`.
+Beside the validator's configuration the only input are instances of [Input](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Input.java)
+which can be created by various methods of the [InputFactory](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/InputFactory.java).
+The [InputFactory](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/InputFactory.java)
+ calculates a hash sum for each Input which is also written to the Report. _SHA-256_ from the JDK is the default algorithm.
+It can be changed using other `read`-methods of [InputFactory](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/InputFactory.java).
The main interface [Check.java](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Check.java)
allows using a batch interface (processing list of [Inputs](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Input.java)).
@@ -99,7 +101,8 @@ However, there is no parallel processing implemented at the moment.
## Accept Recommendation and Accept Match
-A tri-state Object `AcceptRecommendation` can be retrieved from the `Result` using `getAcceptRecommendation()`.
+A tri-state object [AcceptRecommendation](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/AcceptRecommendation.java)
+can be retrieved from the [Result](https://github.com/itplr-kosit/validator/blob/master/src/main/java/de/kosit/validationtool/api/Result.java) using `getAcceptRecommendation()`.
The three defined states are:
@@ -112,7 +115,7 @@ The accept recommendation is based on either:
1. schema and schematron validation result
1. if configured based on _acceptMatch_ configuration of the scenario (see below)
-### Accept Match in Scenario Configuration
+### Accept match in scenario configuration
For your own configuration you can add an `acceptMatch` element in each scenario. It can contain in XPATH expression over your own
defined `Report` to compute a boolean. An XPATH expression evaluating to true will lead to an `ACCEPTABLE` and otherwise to a `REJECT`
@@ -132,7 +135,7 @@ a builder API. A valid configuration consists of the following:
* a valid report transformation configuration
* valid schematron validation configurations (optional)
* a valid accept match configuration to compute acceptance information (optional)
-* valid a fallback scenario configuration
+* a valid fallback scenario configuration
A simple configuration looks like this:
@@ -160,10 +163,10 @@ public class MyValidator {
}
```
-There a various methods provided by the builder API to configure your scenarios and the validation process.
-
+There are various methods provided by the builder API to configure your scenarios and the validation process.
It is also possible to provide runtime artifacts like `XsltExecutable`, `XPathExecutalbe` or `Schema` to configure the validator.
This gives you complete control how to load these artifacts.
+
---
**Note:** Creating this objects requires usage of the same instance of the saxon `Processor` as used during validation later. So you
need to supply a custom `ResolvingConfigurationStrategy` or use the internal one to create these objects. See below.
@@ -187,11 +190,11 @@ There are 3 implemenations available out of the box:
1. [StrictRelativeResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java)
which is the **default**, prevents known XML attacks and only allows loading from a specific local repository location
1. [StrictLocalResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java)
-which opens the first to load resource from local location
+which opens the first to load resource from local locations
1. [RemoteResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java)
-which opens the first to load resource also from remote locations via http and https
+which further opens the second to load resources also from remote locations via http and https
-You can configure usage of one of this implemenations via
+You can configure usage of one of this implemenations using the `ResolvingMode` via
````java
Conifuguration config = Configuration.load(URI.create("myscenarios.xml"))
@@ -208,7 +211,7 @@ Conifuguration config = Configuration.load(URI.create("myscenarios.xml"))
````
---
-**Attention:** If you decide to implement a custom strategy you need to handle xml security risk. Please make sure, that you prevent XXE attacks and
+:warning: **Attention:** If you decide to implement a custom strategy you need to handle xml security risk. Please make sure, that you prevent XXE attacks and
other kind of attacks. Consider using [BaseResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/BaseResolvingStrategy.java)
and the protected methods within to disable certain features.
diff --git a/docs/daemon.md b/docs/daemon.md
index 577a4c8..5be8c75 100644
--- a/docs/daemon.md
+++ b/docs/daemon.md
@@ -1,9 +1,11 @@
# Validator daemon
-You can also start the validator as a HTTP-Server. This is based JDK http server functionality. Keep that mind, if you want to deploy this
+You can also start the validator as a HTTP-Server. This is based [JDK http server](https://docs.oracle.com/javase/8/docs/jre/api/net/httpserver/spec/com/sun/net/httpserver/HttpServer.html) functionality
+and should work with OpenJDK based Java distributions. Keep that mind, if you want to deploy this
in production scenarios with heavy load.
## Basic usage
-To just use the validator daemon as is, start the _Daemon-Mode_ with the `-D` option.
+To just use the validator daemon as is, start the _Daemon-Mode_ with the `-D` option and supply a suitable
+ [validator configuration](configurations.md)
```shell
java -jar validationtool--standalone.jar -s -D
@@ -11,7 +13,7 @@ java -jar validationtool--standalone.jar -s -D
Per default the HTTP-Server listens on _localhost_ at Port 8080.
-You can configure it with `-H` for IP Adress and `-P` for port number:
+You can configure the daemon with `-H` for IP Adress and `-P` for port number:
```shell
java -jar validationtool--standalone.jar -s -D -H 192.168.1.x -P 8081
@@ -38,7 +40,7 @@ The possible customizations are:
## Access the http interface
The validation service listens to `POST`-requests to any server uri. You need to supply the xml/object to validate in the post body.
-The service expects a single plain input in the post body, e.g. `multipart/form-data` is not supported.
+The service expects a single xml input in the post body, e.g. `multipart/form-data` is not supported.
Examples:
@@ -84,4 +86,17 @@ This can be done using infrastructural service like a forwarding proxy (e.g. `ng
## Monitoring and administration
The validation service can be integrated in monitoring solutions like `Icinga` or `Nagios`. There is a `health` endpoint exposed under `/server/health` wich returns
-some basic information about the service like memory consumption, general information about the version and a status `UP` as an XML file.
\ No newline at end of file
+some basic information about the service like memory consumption, general information about the version and a status `UP` as an XML file.
+
+## GUI
+The daemon provides a simple GUI when issuing `GET` requests providing the following:
+
+ 1. usage information
+ 1. information about the actual [validator configuration](configurations.md) used by this daemon
+ 1. a simple form to test the daemon with custom inputs
+
+ The GUI can be disabled with using the API (see above) or via CLI
+
+ ```shell script
+java -jar validationtool--standalone.jar -s -D --disable-gui
+```
\ No newline at end of file
diff --git a/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java b/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java
index ba0e9aa..2e254aa 100644
--- a/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java
+++ b/src/main/java/de/kosit/validationtool/cmd/CommandLineApplication.java
@@ -29,7 +29,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
@@ -97,6 +96,9 @@ public class CommandLineApplication {
private static final Option WORKER_COUNT = Option.builder("T").longOpt("threads").hasArg()
.desc("Number of threads processing validation requests").build();
+ private static final Option DISABLE_GUI = Option.builder("G").longOpt("disable-gui").desc("Disables the GUI of the daemon mode")
+ .build();
+
public static final int DAEMON_SIGNAL = 100;
private static final Option PRINT_MEM_STATS = Option.builder("m").longOpt("memory-stats").desc("Prints some memory stats").build();
@@ -175,6 +177,9 @@ public class CommandLineApplication {
warnUnusedOptions(cmd, unavailable, true);
final ConfigurationLoader config = Configuration.load(determineDefinition(cmd), determineRepository(cmd));
final Daemon validDaemon = new Daemon(determineHost(cmd), determinePort(cmd), determineThreads(cmd));
+ if (cmd.hasOption(DISABLE_GUI.getOpt())) {
+ validDaemon.setGuiEnabled(false);
+ }
validDaemon.startServer(config.build());
return DAEMON_SIGNAL;
}
@@ -379,6 +384,7 @@ public class CommandLineApplication {
options.addOption(CHECK_ASSERTIONS);
options.addOption(PRINT_MEM_STATS);
options.addOption(WORKER_COUNT);
+ options.addOption(DISABLE_GUI);
return options;
}
}
From e01976e7aeb454f33e580e743535471b8c4f022f Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Thu, 28 May 2020 15:38:43 +0200
Subject: [PATCH 32/36] fix some typos
---
README.md | 2 +-
docs/api.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 4168738..60de6b9 100644
--- a/README.md
+++ b/README.md
@@ -74,7 +74,7 @@ The [API documentation](./docs/api.md) shows further configuration options.
### Daemon-Mode
-You can also start the validator as an HTTP-Server. Just start it in _Daemon-Mode_ with the `-D` option.
+You can also start the validator as a HTTP-Server. Just start it in _Daemon-Mode_ with the `-D` option.
```shell
java -jar validationtool--standalone.jar -s -D
diff --git a/docs/api.md b/docs/api.md
index bea4a12..66e77cc 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -190,7 +190,7 @@ There are 3 implemenations available out of the box:
1. [StrictRelativeResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/StrictRelativeResolvingStrategy.java)
which is the **default**, prevents known XML attacks and only allows loading from a specific local repository location
1. [StrictLocalResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/StrictLocalResolvingStrategy.java)
-which opens the first to load resource from local locations
+which opens the first strategy to load resources from local locations
1. [RemoteResolvingStrategy.java](https://github.com/itplr-kosit/validator/tree/master/src/main/java/de/kosit/validationtool/impl/xml/RemoteResolvingStrategy.java)
which further opens the second to load resources also from remote locations via http and https
From 3e4aa9f957d78eddb5d13743b9fce97454706483 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Fri, 29 May 2020 09:03:16 +0200
Subject: [PATCH 33/36] (chore) daemon documentation and license clarification
for docsify
---
src/main/resources/gui/README.md | 2 +
src/main/resources/gui/docs/api.md | 1 +
src/main/resources/gui/docs/configurations.md | 15 +++++
src/main/resources/gui/index.html | 56 +++++++++++--------
src/main/resources/gui/lib/License | 23 ++++++++
5 files changed, 75 insertions(+), 22 deletions(-)
create mode 100644 src/main/resources/gui/docs/configurations.md
create mode 100644 src/main/resources/gui/lib/License
diff --git a/src/main/resources/gui/README.md b/src/main/resources/gui/README.md
index 7c3955f..40aad58 100644
--- a/src/main/resources/gui/README.md
+++ b/src/main/resources/gui/README.md
@@ -2,6 +2,8 @@
[API usage](docs/api)
+[configurations](docs/configurations)
+
# Server information
View [validator configuration](/server/config) or health information
diff --git a/src/main/resources/gui/docs/api.md b/src/main/resources/gui/docs/api.md
index dc84852..34d0c71 100644
--- a/src/main/resources/gui/docs/api.md
+++ b/src/main/resources/gui/docs/api.md
@@ -1,3 +1,4 @@
+# API Usage
The validation service listens to `POST`-requests to any server uri. You need to supply the xml/object to validate in the post body.
The service expects a single plain input in the post body, e.g. `multipart/form-data` is not supported.
diff --git a/src/main/resources/gui/docs/configurations.md b/src/main/resources/gui/docs/configurations.md
new file mode 100644
index 0000000..3e8678d
--- /dev/null
+++ b/src/main/resources/gui/docs/configurations.md
@@ -0,0 +1,15 @@
+# Configurations
+
+The validator needs a scenario configuration for working properly.
+
+Currently, there are two public third party validation configurations available.
+
+* Validation Configuration for [XRechnung](http://www.xoev.de/de/xrechnung):
+ * Source code is available on [GitHub](https://github.com/itplr-kosit/validator-configuration-xrechnung)
+ * [Releases](https://github.com/itplr-kosit/validator-configuration-xrechnung/releases) can also be downloaded
+* Validation Configuration for [XGewerbeanzeige](https://xgewerbeanzeige.de/)
+ * Source code is available on [GitHub](https://github.com/itplr-kosit/validator-configuration-xgewerbeanzeige)
+ * [Releases](https://github.com/itplr-kosit/validator-configuration-xgewerbeanzeige/releases) can also be downloaded
+
+For creating custom configurations see [configaration documentation](https://github.com/itplr-kosit/validator/blob/master/docs/configurations.md)
+for details
\ No newline at end of file
diff --git a/src/main/resources/gui/index.html b/src/main/resources/gui/index.html
index 4919302..1eb48c4 100644
--- a/src/main/resources/gui/index.html
+++ b/src/main/resources/gui/index.html
@@ -3,36 +3,48 @@
Validator
-
-
-
+
+
+
+
+
diff --git a/src/main/resources/gui/lib/License b/src/main/resources/gui/lib/License
new file mode 100644
index 0000000..4a5b531
--- /dev/null
+++ b/src/main/resources/gui/lib/License
@@ -0,0 +1,23 @@
+Sources in this diretory are based on https://github.com/docsifyjs/docsify/
+
+MIT License
+
+Copyright (c) 2016 - present cinwell.li
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
From e26cbc9613226e4bb9ad654356916d83c2fcce32 Mon Sep 17 00:00:00 2001
From: "Andreas Penski (init)"
Date: Fri, 29 May 2020 09:58:04 +0200
Subject: [PATCH 34/36] make java snippet explicit
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 60de6b9..385e94f 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ A concrete example with a specific validator configuration can be found on
### Application User Interface (API / embedded usage)
The validator can also be used in own Java Applications via the API. Usage would be something like this:
-```
+```java
Path scenarios = Paths.get("scenarios.xml");
Configuration config = Configuration.load(scenarios.toUri());
Input document = InputFactory.read(testDocument);
From b04aa344bc23075d89b585cf22191f01e20ce814 Mon Sep 17 00:00:00 2001
From: Renzo Kottmann
Date: Sat, 20 Jun 2020 16:41:19 +0000
Subject: [PATCH 35/36] Minor typos and grammer
---
README.md | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 385e94f..be743c4 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ The validator distribution contains the following artifacts:
The validator is just an engine and does not know anything about XML Documents and has no own validation rules.
Validation rules and details are defined in [validation scenarios](docs/configurations.md) which are used to fully configure the validation process.
-All configurations are self-contained modules and deployed and developed on their own.
+All configurations are self-contained modules which are deployed and developed on their own.
### Third Party Validation Configurations
@@ -59,7 +59,8 @@ A concrete example with a specific validator configuration can be found on
### Application User Interface (API / embedded usage)
-The validator can also be used in own Java Applications via the API. Usage would be something like this:
+The validator can also be used in own Java Applications via the API. An example use of the API as follows:
+
```java
Path scenarios = Paths.get("scenarios.xml");
Configuration config = Configuration.load(scenarios.toUri());
@@ -70,6 +71,7 @@ Result validationResult = validator.checkInput(document);
// examine the result here
```
+
The [API documentation](./docs/api.md) shows further configuration options.
### Daemon-Mode
From 6219eb6f4141946b3b6e085ad051228c6b5da2fa Mon Sep 17 00:00:00 2001
From: Renzo Kottmann
Date: Sat, 20 Jun 2020 16:51:05 +0000
Subject: [PATCH 36/36] Typos and Grammar on daemon.md
---
docs/daemon.md | 38 ++++++++++++++++++++++----------------
1 file changed, 22 insertions(+), 16 deletions(-)
diff --git a/docs/daemon.md b/docs/daemon.md
index 5be8c75..8702caa 100644
--- a/docs/daemon.md
+++ b/docs/daemon.md
@@ -1,11 +1,12 @@
-# Validator daemon
-You can also start the validator as a HTTP-Server. This is based [JDK http server](https://docs.oracle.com/javase/8/docs/jre/api/net/httpserver/spec/com/sun/net/httpserver/HttpServer.html) functionality
-and should work with OpenJDK based Java distributions. Keep that mind, if you want to deploy this
-in production scenarios with heavy load.
+# Validator HTTP Daemon
+
+You can start the validator as an HTTP-Server. This server is based on the [JDK HTTP server](https://docs.oracle.com/javase/8/docs/jre/api/net/httpserver/spec/com/sun/net/httpserver/HttpServer.html) functionality
+and should work with OpenJDK based distributions. Keep this in mind, if you want to deploy this in production scenarios with heavy load.
## Basic usage
-To just use the validator daemon as is, start the _Daemon-Mode_ with the `-D` option and supply a suitable
- [validator configuration](configurations.md)
+
+To use the validator daemon as is, start the _Daemon-Mode_ with the `-D` option and supply a suitable
+ [validator configuration](configurations.md).
```shell
java -jar validationtool--standalone.jar -s -D
@@ -20,7 +21,8 @@ java -jar validationtool--standalone.jar -s -D
```
## Customized usage
-You can also leverage the API to create a customized version of the Daemon. Just instantiate, configure and start the daemon like this:
+
+You can also leverage the API to create a customized version of the daemon. Just instantiate, configure and start the daemon like this:
````java
Configuration config = Configuration.load(...);
@@ -36,11 +38,13 @@ The possible customizations are:
* `bindAddress` - the interface to bind the daemon to
* `port` - the port to expose
* `threadCount` - number of worker threads to handle results
-* `guiEnabled` - enable or disable the basic gui with usage information
+* `guiEnabled` - enable or disable the basic GUI with usage information
-## Access the http interface
-The validation service listens to `POST`-requests to any server uri. You need to supply the xml/object to validate in the post body.
-The service expects a single xml input in the post body, e.g. `multipart/form-data` is not supported.
+## Access the HTTP interface
+
+The validation service listens to `POST`-requests on any server URL. You need to supply the xml/object to validate in the HTTP body.
+
+The service expects a single XML input in the HTTP body, e.g. `multipart/form-data` is not supported.
Examples:
@@ -80,15 +84,17 @@ fetch("http://localhost:8080", requestOptions)
.then(result => console.log(result))
.catch(error => console.log('error', error));
```
+
## Authorization
-There is no mechanism to check, whether client is allowed to consume the service or not. The user is responsible to secure access to the service
-This can be done using infrastructural service like a forwarding proxy (e.g. `nginx` or `apache http server`) or by implementing a custom solution
+There is no mechanism to check, whether client is allowed to consume the service or not. The user is responsible to secure access to the service.
+This can be done using infrastructural service like a forwarding proxies (e.g. `nginx` or `Apache http server`) or by implementing a custom solution.
## Monitoring and administration
-The validation service can be integrated in monitoring solutions like `Icinga` or `Nagios`. There is a `health` endpoint exposed under `/server/health` wich returns
-some basic information about the service like memory consumption, general information about the version and a status `UP` as an XML file.
+
+The validation service can be integrated in monitoring solutions like `Icinga` or `Nagios`. There is a `health` endpoint exposed under `/server/health` wich returns some basic information about the service like memory consumption, general information about the version and a status `UP` as an XML file.
## GUI
+
The daemon provides a simple GUI when issuing `GET` requests providing the following:
1. usage information
@@ -99,4 +105,4 @@ The daemon provides a simple GUI when issuing `GET` requests providing the follo
```shell script
java -jar validationtool--standalone.jar -s -D --disable-gui
-```
\ No newline at end of file
+```