mirror of
https://github.com/itplr-kosit/validator.git
synced 2026-05-25 16:55:39 +00:00
Merge branch 'configuration_api' into 'master'
New configuration api and xml security configuration See merge request kosit/validator!24
This commit is contained in:
commit
a78a0861e8
118 changed files with 5782 additions and 1455 deletions
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
|
|
@ -2,11 +2,13 @@
|
|||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="true" />
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="validationtool" />
|
||||
<module name="validator" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
|
|
|
|||
1
.idea/encodings.xml
generated
1
.idea/encodings.xml
generated
|
|
@ -2,6 +2,7 @@
|
|||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/generated/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/model" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
|
|
|
|||
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
|
|
@ -1,6 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
<component name="MarkdownProjectSettings">
|
||||
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="LINE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true">
|
||||
<PanelProvider>
|
||||
|
|
@ -76,7 +79,7 @@
|
|||
<component name="NodePackageJsonFileManager">
|
||||
<packageJsonPaths />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="false" project-jdk-name="openjdk-14" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<component name="jetbrains.communicator.idea.IdProvider" IDEtalkID="4B2DA906C3A7DF4F7B6EA28093E19A3F" />
|
||||
|
|
|
|||
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -6,19 +6,23 @@ 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
|
||||
|
||||
- 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
|
||||
- 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
|
||||
|
|
|
|||
54
README.md
54
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,33 +14,21 @@ The validator distribution contains the following artifacts:
|
|||
1. **validationtool-`<version`>-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
|
||||
|
||||
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 on their own.
|
||||
All configurations are self-contained modules which are 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
|
||||
|
||||
|
|
@ -58,36 +48,40 @@ The general way using the CLI is:
|
|||
java -jar validationtool-<version>-standalone.jar -s <scenario-config-file> [OPTIONS] [FILE] [FILE] [FILE] ...
|
||||
```
|
||||
|
||||
You can more CLI options by
|
||||
The help option displays further CLI options to customize the process:
|
||||
|
||||
```shell
|
||||
java -jar validationtool-<version>-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)
|
||||
|
||||
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. An example use of the API as follows:
|
||||
|
||||
```java
|
||||
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
|
||||
```
|
||||
|
||||
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-<version>-standalone.jar -s <scenario-config-file> -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-<version>-standalone.jar -s <scenario-config-file> -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.
|
||||
|
||||
The [daemon documentation](./docs/daemon.md) shows more usage details and further configuration options.
|
||||
|
||||
|
|
|
|||
131
docs/api.md
131
docs/api.md
|
|
@ -1,11 +1,10 @@
|
|||
# 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
|
||||
|
||||
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)).
|
||||
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
|
||||
|
||||
|
|
@ -29,8 +28,7 @@ dependencies {
|
|||
|
||||
## Usage
|
||||
|
||||
Prerequisite for use is a valid [scenario definition](configurations.md) and the a folder with all necessary artifacts for validation
|
||||
(repository) either on the filesystem or on the classpath.
|
||||
Prerequisite for use is a valid [scenario definition](configurations.md) and the a folder with all necessary artifacts for validation (repository) either on the filesystem or on the classpath.
|
||||
|
||||
The following example demonstrates loading scenario.xml and whole configuration from classpath and validating one XML document:
|
||||
|
||||
|
|
@ -56,7 +54,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
|
||||
|
|
@ -86,20 +84,23 @@ public class StandardExample {
|
|||
The `Result` interface has convenience methods to retrieve details about XSD validation errors and Schematron messages and other processing results. See
|
||||
[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.
|
||||
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 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)
|
||||
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)).
|
||||
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:
|
||||
|
||||
|
|
@ -109,15 +110,101 @@ The three defined states are:
|
|||
|
||||
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)
|
||||
1. Schema and Schematron validation results
|
||||
1. or 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`
|
||||
recommendation.
|
||||
For your own configuration you can add an `acceptMatch` element in each scenario. It can contain an XPATH expression over your own defined `Report` to compute a boolean value. An XPATH expression evaluating to true will lead to an `ACCEPTABLE` and otherwise to a `REJECT` 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.
|
||||
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
|
||||
|
||||
Instead of pre-configured [scenario files](configurations.md) it is 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)
|
||||
* a valid 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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The build API provides various methods to configure your scenarios and the validation process.
|
||||
|
||||
It is also possible to provide runtime artifacts like `XsltExecutable`, `XPathExecutable` or `Schema` to configure the validator.
|
||||
This gives you complete control over loading these artifacts.
|
||||
|
||||
---
|
||||
**Note:** Creating these objects requires usage of the same instance of the saxon `Processor` as used during validation later. Therefore, you need to supply a custom `ResolvingConfigurationStrategy` or use the internal one to create these objects. See below.
|
||||
|
||||
---
|
||||
|
||||
## Configure SML Security and Resolving
|
||||
|
||||
When using XML related technologies you are supposed to handle certain security issues properly. The KoSIT validator pursues a rather strict strategy. The default configuration:
|
||||
|
||||
* disables 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 necessary 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 implementations 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 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
|
||||
|
||||
You can configure usage of one of these implementations using the `ResolvingMode` 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();
|
||||
````
|
||||
|
||||
---
|
||||
|
||||
:warning: **Attention:** If you decide to implement a custom strategy you need to handle XML security risks on your own. Please make sure, that you prevent XXE 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.
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
108
docs/daemon.md
Normal file
108
docs/daemon.md
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
# 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 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-<version>-standalone.jar -s <scenario-config-file> -D
|
||||
```
|
||||
|
||||
Per default the HTTP-Server listens on _localhost_ at Port 8080.
|
||||
|
||||
You can configure the daemon with `-H` for IP Adress and `-P` for port number:
|
||||
|
||||
```shell
|
||||
java -jar validationtool-<version>-standalone.jar -s <scenario-config-file> -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 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:
|
||||
|
||||
* `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 = "<file contents here>";
|
||||
|
||||
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 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.
|
||||
|
||||
## 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-<version>-standalone.jar -s <scenario-config-file> -D --disable-gui
|
||||
```
|
||||
103
pom.xml
103
pom.xml
|
|
@ -39,7 +39,7 @@
|
|||
</developers>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<version.jacoco>0.8.4</version.jacoco>
|
||||
<version.jacoco>0.8.5</version.jacoco>
|
||||
<version.lombok>1.18.8</version.lombok>
|
||||
<version.saxon-he>9.9.1-3</version.saxon-he>
|
||||
<version.slf4j>1.7.25</version.slf4j>
|
||||
|
|
@ -129,6 +129,12 @@
|
|||
<version>1.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.8</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
@ -145,6 +151,26 @@
|
|||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>reserve-network-port</id>
|
||||
<goals>
|
||||
<goal>reserve-network-port</goal>
|
||||
</goals>
|
||||
<phase>process-resources</phase>
|
||||
<configuration>
|
||||
<portNames>
|
||||
<portName>validator.server.port</portName>
|
||||
<portName>jacoco.tcp.port</portName>
|
||||
</portNames>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
|
|
@ -266,28 +292,6 @@
|
|||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
<plugin>
|
||||
<!-- Integrate the /src/main/generated folder -->
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>src/generated/java</source>
|
||||
</sources>
|
||||
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Generate model classes -->
|
||||
<plugin>
|
||||
<groupId>org.jvnet.jaxb2.maven2</groupId>
|
||||
|
|
@ -304,7 +308,6 @@
|
|||
<extension>true</extension>
|
||||
<schemaDirectory>src/main/model/xsd</schemaDirectory>
|
||||
<bindingDirectory>src/main/model/binding</bindingDirectory>
|
||||
<generateDirectory>src/generated/java</generateDirectory>
|
||||
<packageLevelAnnotations>false</packageLevelAnnotations>
|
||||
<args>
|
||||
<arg>-Xinheritance</arg>
|
||||
|
|
@ -332,14 +335,40 @@
|
|||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepareJacocoJUnitArgLine</id>
|
||||
<id>prepareJacocoSurefireArgLine</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<propertyName>jacocoArgumentsJUnit</propertyName>
|
||||
<propertyName>jacocoSurefire</propertyName>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>prepareJacocoFailsafeArgLine</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<propertyName>jacocoFailsafe</propertyName>
|
||||
<output>tcpserver</output>
|
||||
<address>localhost</address>
|
||||
<port>${jacoco.tcp.port}</port>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>dump</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>dump</goal>
|
||||
|
||||
</goals>
|
||||
<configuration>
|
||||
<address>localhost</address>
|
||||
<port>${jacoco.tcp.port}</port>
|
||||
<append>true</append>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>generateJacocoReport</id>
|
||||
<goals>
|
||||
|
|
@ -355,7 +384,7 @@
|
|||
<version>2.22.0</version>
|
||||
<configuration>
|
||||
<!--suppress MavenModelInspection -->
|
||||
<argLine>-Dfile.encoding=UTF-8 ${jacocoArgumentsJUnit}</argLine>
|
||||
<argLine>-Dfile.encoding=UTF-8 ${jacocoSurefire}</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
|
@ -387,20 +416,21 @@
|
|||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>1.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<execution>
|
||||
<id>run</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<configuration>
|
||||
<executable>java</executable>
|
||||
<longClasspath>true</longClasspath>
|
||||
<async>true</async>
|
||||
<asyncDestroyOnShutdown>true</asyncDestroyOnShutdown>
|
||||
<arguments>
|
||||
<argument>${jacocoFailsafe}</argument>
|
||||
<argument>-classpath</argument>
|
||||
<classpath />
|
||||
<argument>de.kosit.validationtool.cmd.CommandLineApplication</argument>
|
||||
|
|
@ -408,6 +438,8 @@
|
|||
<argument>${project.build.testOutputDirectory}/examples/simple/scenarios.xml</argument>
|
||||
<argument>-r</argument>
|
||||
<argument>${project.build.testOutputDirectory}/examples/simple/repository</argument>
|
||||
<argument>--port</argument>
|
||||
<argument>${validator.server.port}</argument>
|
||||
<argument>-D</argument>
|
||||
|
||||
</arguments>
|
||||
|
|
@ -421,7 +453,8 @@
|
|||
<configuration>
|
||||
<target>
|
||||
<!-- schlafen um den Start des Daemon abzuwarten -->
|
||||
<sleep seconds="5" />
|
||||
<sleep seconds="10" />
|
||||
<echo>${jacoco.tcp.port}</echo>
|
||||
</target>
|
||||
</configuration>
|
||||
<executions>
|
||||
|
|
@ -445,6 +478,10 @@
|
|||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<!--suppress MavenModelInspection -->
|
||||
<argLine>-Dfile.encoding=UTF-8 -Ddaemon.port=${validator.server.port}</argLine>
|
||||
</configuration>
|
||||
|
||||
</execution>
|
||||
</executions>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,24 +20,30 @@
|
|||
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.impl.RelativeUriResolver;
|
||||
import de.kosit.validationtool.config.ConfigurationLoader;
|
||||
import de.kosit.validationtool.impl.ContentRepository;
|
||||
import de.kosit.validationtool.impl.Scenario;
|
||||
|
||||
/**
|
||||
* 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 +55,51 @@ public class CheckConfiguration {
|
|||
*/
|
||||
private URI scenarioRepository;
|
||||
|
||||
private ConfigurationLoader loader;
|
||||
|
||||
/**
|
||||
* 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 Configuration delegate;
|
||||
|
||||
private Configuration getDelegate() {
|
||||
if (this.delegate == null) {
|
||||
this.delegate = Configuration.load(this.scenarioDefinition, this.scenarioRepository).build();
|
||||
}
|
||||
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<Scenario> getScenarios() {
|
||||
return getDelegate().getScenarios();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scenario getFallbackScenario() {
|
||||
return getDelegate().getFallbackScenario();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDate() {
|
||||
return getDelegate().getDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAdditionalParameters() {
|
||||
return this.delegate.getAdditionalParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getDelegate().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthor() {
|
||||
return getDelegate().getAuthor();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public ContentRepository getContentRepository() {
|
||||
return getDelegate().getContentRepository();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
108
src/main/java/de/kosit/validationtool/api/Configuration.java
Normal file
108
src/main/java/de/kosit/validationtool/api/Configuration.java
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
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.ConfigurationLoader;
|
||||
import de.kosit.validationtool.impl.ContentRepository;
|
||||
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:
|
||||
*
|
||||
* <ol>
|
||||
* <li>{@link ConfigurationLoader} implements loading {@link Check} configurations from a scenario.xml file</li>
|
||||
* <li>Using a builder style api {@link de.kosit.validationtool.config.ConfigurationBuilder}to configure the
|
||||
* {@link Check}</li>
|
||||
* </ol>
|
||||
* <p>
|
||||
* Both methods can be used via convinience methods. See below.
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
|
||||
public interface Configuration {
|
||||
|
||||
/**
|
||||
* Returns a list of configured scenarios.
|
||||
*
|
||||
* @return the list of scenarios
|
||||
*/
|
||||
List<Scenario> 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<String, Object> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -169,8 +164,10 @@ public class InputFactory {
|
|||
}
|
||||
|
||||
/**
|
||||
* Reads a test document from a {@link Source}.
|
||||
*
|
||||
* Reads a test document from a {@link Source}. <br/>
|
||||
* 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}
|
||||
*/
|
||||
|
|
@ -180,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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
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 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. Your would be
|
||||
* responsible for this!
|
||||
*
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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 is used for
|
||||
* dereferencing an absolute URI (after resolution) to return a {@link javax.xml.transform.Source}. It <b>can</b> be
|
||||
* used for resolving relative URIs against a base URI or restrict access to certain URIs.
|
||||
* <p>
|
||||
* This URIResolver is used to dereference the URIs appearing in <code>xsl:import</code>, <code>xsl:include</code>, and
|
||||
* <code>xsl:import-schema</code> declarations.
|
||||
* </p>
|
||||
*
|
||||
* @param scenarioRepository an optional repository, your implementation might not need this
|
||||
* @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);
|
||||
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ public interface Result {
|
|||
|
||||
/**
|
||||
* Returns {@link org.oclc.purl.dsdl.svrl.FailedAssert FailedAsserts} of a schematron evaluation.
|
||||
*
|
||||
*
|
||||
* @return list of {@link org.oclc.purl.dsdl.svrl.FailedAssert FailedAsserts}, if any, empty list otherwise
|
||||
*/
|
||||
List<FailedAssert> getFailedAsserts();
|
||||
|
|
@ -93,7 +93,7 @@ public interface Result {
|
|||
|
||||
/**
|
||||
* Returns true, if schematron has been checked and the result does not contain any {@link FailedAssert FailedAsserts}.
|
||||
*
|
||||
*
|
||||
* @return true, if valid
|
||||
*/
|
||||
boolean isSchematronValid();
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -93,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();
|
||||
|
|
@ -169,9 +175,12 @@ 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));
|
||||
if (cmd.hasOption(DISABLE_GUI.getOpt())) {
|
||||
validDaemon.setGuiEnabled(false);
|
||||
}
|
||||
validDaemon.startServer(config.build());
|
||||
return DAEMON_SIGNAL;
|
||||
}
|
||||
|
||||
|
|
@ -203,25 +212,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());
|
||||
|
|
@ -374,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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.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.
|
||||
*
|
||||
* @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 Scenarios scenarios;
|
||||
|
||||
HealthHandler(final Scenarios scenarios) {
|
||||
this.scenarios = scenarios;
|
||||
}
|
||||
|
||||
@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(check.getRepository().getScenarios()));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,12 +66,12 @@ 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);
|
||||
} catch (final SaxonApiException e) {
|
||||
log.info("Error extracting html content to {}", file.toAbsolutePath(), e);
|
||||
log.error("Error extracting html content to {}", file.toAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,117 +0,0 @@
|
|||
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;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import de.kosit.validationtool.model.scenarios.Scenarios;
|
||||
|
||||
/**
|
||||
* 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 Scenarios scenarios;
|
||||
|
||||
Health(Scenarios scenarios) {
|
||||
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
freeMemory = runtime.freeMemory();
|
||||
maxMemory = runtime.maxMemory();
|
||||
totalMemory = runtime.totalMemory();
|
||||
this.scenarios = scenarios;
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode, die schreibt das Health Xml für optimale Status
|
||||
*
|
||||
*/
|
||||
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);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode, die schreibt das System Status Node im Xml File
|
||||
*
|
||||
* @param doc Vom Typ Dokument.
|
||||
*
|
||||
*/
|
||||
private Node getState(Document doc) {
|
||||
Element state = doc.createElement("state");
|
||||
state.setAttribute("indicator", "OK");
|
||||
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 scenarios Vom Typ {@link Scenarios} 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()));
|
||||
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(Document doc, long freeMemory, long maxMemory, long totalMemory) {
|
||||
Element memory = doc.createElement("memoryState");
|
||||
String freeM = Long.toString(freeMemory);
|
||||
Element freeMNode = doc.createElement("freeMemory");
|
||||
freeMNode.appendChild(doc.createTextNode(freeM));
|
||||
memory.appendChild(freeMNode);
|
||||
String maxM = Long.toString(maxMemory);
|
||||
Element maxMNode = doc.createElement("maxMemory");
|
||||
maxMNode.appendChild(doc.createTextNode(maxM));
|
||||
memory.appendChild(maxMNode);
|
||||
String totalM = Long.toString(totalMemory);
|
||||
Element totalMNode = doc.createElement("totalMemory");
|
||||
totalMNode.appendChild(doc.createTextNode(totalM));
|
||||
memory.appendChild(totalMNode);
|
||||
return memory;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
20
src/main/java/de/kosit/validationtool/config/Builder.java
Normal file
20
src/main/java/de/kosit/validationtool/config/Builder.java
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package de.kosit.validationtool.config;
|
||||
|
||||
import de.kosit.validationtool.impl.ContentRepository;
|
||||
import de.kosit.validationtool.impl.model.Result;
|
||||
|
||||
/**
|
||||
* Internal interface for creating object builders.
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
interface Builder<T> {
|
||||
|
||||
/**
|
||||
* 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<T, String> build(ContentRepository repository);
|
||||
}
|
||||
|
|
@ -0,0 +1,370 @@
|
|||
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;
|
||||
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 org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
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 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;
|
||||
|
||||
/**
|
||||
* Implements a builder style creation of a {@link Configuration}.
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
@Slf4j
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
public class ConfigurationBuilder {
|
||||
|
||||
private final List<ScenarioBuilder> 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<String, Object> parameters = new HashMap<>();
|
||||
|
||||
private URI repository;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(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");
|
||||
}
|
||||
this.fallbackBuilder = builder;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new scenario configuration.
|
||||
*
|
||||
* @return the scenario configuration builder
|
||||
*/
|
||||
public static ScenarioBuilder scenario() {
|
||||
return scenario(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.processor = resolving.getProcessor();
|
||||
}
|
||||
final ContentRepository contentRepository = new ContentRepository(resolving, this.repository);
|
||||
|
||||
final List<Scenario> 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);
|
||||
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");
|
||||
}
|
||||
final Result<Scenario, String> result = this.fallbackBuilder.build(contentRepository);
|
||||
if (result.isInvalid()) {
|
||||
throw new IllegalStateException("Invalid fallback configuration: " + String.join(",", result.getErrors()));
|
||||
}
|
||||
return result.getObject();
|
||||
}
|
||||
|
||||
private List<Scenario> initializeScenarios(final ContentRepository contentRepository) {
|
||||
if (this.scenarios.isEmpty()) {
|
||||
throw new IllegalStateException("No scenario specified");
|
||||
}
|
||||
return this.scenarios.stream().map(s -> {
|
||||
final Result<Scenario, String> 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
package de.kosit.validationtool.config;
|
||||
|
||||
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.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.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.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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ConfigurationLoader {
|
||||
|
||||
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;
|
||||
|
||||
protected ResolvingMode resolvingMode = ResolvingMode.STRICT_RELATIVE;
|
||||
|
||||
protected ResolvingConfigurationStrategy resolvingConfigurationStrategy;
|
||||
|
||||
protected final Map<String, Object> parameters = new HashMap<>();
|
||||
|
||||
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, final Processor processor) {
|
||||
try {
|
||||
final Result<XdmNode, XMLSyntaxError> 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'",
|
||||
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 ResourceType noscenarioResource = scenarios.getNoScenarioReport().getResource();
|
||||
return new FallbackBuilder().source(noscenarioResource.getLocation()).name(noscenarioResource.getName()).build(repository)
|
||||
.getObject();
|
||||
|
||||
}
|
||||
|
||||
public Configuration build() {
|
||||
final ResolvingConfigurationStrategy resolving = getResolvingConfigurationStrategy();
|
||||
final Processor processor = resolving.getProcessor();
|
||||
final ContentRepository contentRepository = new ContentRepository(resolving, getScenarioRepository());
|
||||
|
||||
final Scenarios def = loadScenarios(contentRepository.getScenarioSchema(), processor);
|
||||
final List<Scenario> scenarios = initializeScenarios(def, contentRepository);
|
||||
final Scenario fallbackScenario = createFallback(def, contentRepository);
|
||||
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);
|
||||
configuration.getAdditionalParameters().put(Keys.SCENARIOS_FILE, this.scenarioDefinition);
|
||||
configuration.getAdditionalParameters().put(Keys.SCENARIO_DEFINITION, def);
|
||||
return (configuration);
|
||||
}
|
||||
|
||||
private static List<Scenario> initializeScenarios(final Scenarios def, final ContentRepository contentRepository) {
|
||||
return def.getScenario().stream().map(s -> initialize(s, contentRepository)).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();
|
||||
}
|
||||
|
||||
private Scenarios loadScenarios(final Schema scenarioSchema, final Processor processor) {
|
||||
final ConversionService conversionService = new ConversionService();
|
||||
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, scenarioSchema, handler);
|
||||
if (!handler.hasErrors()) {
|
||||
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()));
|
||||
}
|
||||
return scenarios;
|
||||
|
||||
}
|
||||
|
||||
private static Scenario initialize(final ScenarioType def, final ContentRepository repository) {
|
||||
final Scenario s = new Scenario(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Scenario> scenarios;
|
||||
|
||||
private final Scenario fallbackScenario;
|
||||
|
||||
private ContentRepository contentRepository;
|
||||
|
||||
private String name;
|
||||
|
||||
private String author;
|
||||
|
||||
private String date;
|
||||
|
||||
private Map<String, Object> additionalParameters;
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* Create a fallback {@link Scenario} configuration.
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
public class FallbackBuilder implements Builder<Scenario> {
|
||||
|
||||
private final ReportBuilder internal = new ReportBuilder().name("fallback");
|
||||
|
||||
@Override
|
||||
public Result<Scenario, String> build(final ContentRepository repository) {
|
||||
final ScenarioType object = createObject();
|
||||
final Result<Pair<CreateReportType, Transformation>, String> build = this.internal.build(repository);
|
||||
final Result<Scenario, String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
23
src/main/java/de/kosit/validationtool/config/Keys.java
Normal file
23
src/main/java/de/kosit/validationtool/config/Keys.java
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package de.kosit.validationtool.config;
|
||||
|
||||
/**
|
||||
* Defines some keys used for supplying additional parameters internally.
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
116
src/main/java/de/kosit/validationtool/config/ReportBuilder.java
Normal file
116
src/main/java/de/kosit/validationtool/config/ReportBuilder.java
Normal file
|
|
@ -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<Pair<CreateReportType, Transformation>> {
|
||||
|
||||
private static final String DEFAULT_NAME = "manually created report";
|
||||
|
||||
private XsltExecutable executable;
|
||||
|
||||
private URI source;
|
||||
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public Result<Pair<CreateReportType, Transformation>, String> build(final ContentRepository repository) {
|
||||
if (this.executable == null && this.source == null) {
|
||||
return createError(String.format("Must supply source location and/or executable for report '%s'", this.name));
|
||||
}
|
||||
final CreateReportType object = createObject();
|
||||
Result<Pair<CreateReportType, Transformation>, 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<Pair<CreateReportType, Transformation>, 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,280 @@
|
|||
package de.kosit.validationtool.config;
|
||||
|
||||
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.apache.commons.lang3.StringUtils;
|
||||
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;
|
||||
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.XPathExecutable;
|
||||
|
||||
/**
|
||||
* Builder for {@link Scenario} configuration.
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
public class ScenarioBuilder implements Builder<Scenario> {
|
||||
|
||||
private static int nameCount = 0;
|
||||
|
||||
private static final String DEFAULT_DESCRIPTION = "Dieses Scenario wurde per API erstellt";
|
||||
|
||||
private final Map<String, String> namespaces = new HashMap<>();
|
||||
|
||||
private final XPathBuilder matchConfig = new XPathBuilder("match");
|
||||
|
||||
private final XPathBuilder acceptConfig = new XPathBuilder("accept");
|
||||
|
||||
private String name;
|
||||
|
||||
private SchemaBuilder schemaBuilder;
|
||||
|
||||
private final List<SchematronBuilder> schematronBuilders = new ArrayList<>();
|
||||
|
||||
private ReportBuilder reportBuilder;
|
||||
|
||||
private String description;
|
||||
|
||||
@Override
|
||||
public Result<Scenario, String> build(final ContentRepository repository) {
|
||||
final List<String> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<NamespaceType> 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<String> errors, final Scenario scenario) {
|
||||
this.matchConfig.setNamespaces(this.namespaces);
|
||||
final Result<XPathExecutable, String> 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<String> errors, final Scenario scenario) {
|
||||
this.acceptConfig.setNamespaces(this.namespaces);
|
||||
if (this.acceptConfig.isAvailable()) {
|
||||
final Result<XPathExecutable, String> 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 {
|
||||
log.debug("No accept configuration available");
|
||||
}
|
||||
}
|
||||
|
||||
private void buildReport(final ContentRepository repository, final List<String> errors, final Scenario scenario) {
|
||||
if (this.reportBuilder == null) {
|
||||
errors.add("Must supply report configuration");
|
||||
} else {
|
||||
final Result<Pair<CreateReportType, Transformation>, 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<String> errors, final Scenario scenario) {
|
||||
this.schematronBuilders.forEach(e -> {
|
||||
final Result<Pair<ValidateWithSchematron, Transformation>, 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<String> errors, final Scenario scenario) {
|
||||
if (this.schemaBuilder == null) {
|
||||
errors.add("Must supply schema for validation");
|
||||
} else {
|
||||
final Result<Pair<ValidateWithXmlSchema, Schema>, 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;
|
||||
}
|
||||
|
||||
public ScenarioBuilder name(final String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
121
src/main/java/de/kosit/validationtool/config/SchemaBuilder.java
Normal file
121
src/main/java/de/kosit/validationtool/config/SchemaBuilder.java
Normal file
|
|
@ -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<Pair<ValidateWithXmlSchema, Schema>> {
|
||||
|
||||
private static final String DEFAULT_NAME = "manually configured";
|
||||
|
||||
private Schema schema;
|
||||
|
||||
private URI schemaLocation;
|
||||
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public Result<Pair<ValidateWithXmlSchema, Schema>, String> build(final ContentRepository repository) {
|
||||
if (this.schema == null && this.schemaLocation == null) {
|
||||
return createError(String.format("Must supply source location and/or executable for schema '%s'", this.name));
|
||||
}
|
||||
Result<Pair<ValidateWithXmlSchema, Schema>, 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 != null ? this.schemaLocation.toASCIIString() : "manuelly configured");
|
||||
o.getResource().add(r);
|
||||
return o;
|
||||
}
|
||||
|
||||
private static Result<Pair<ValidateWithXmlSchema, Schema>, 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Pair<ValidateWithSchematron, Transformation>> {
|
||||
|
||||
private static final String DEFAULT_NAME = "manually configured";
|
||||
|
||||
private XsltExecutable executable;
|
||||
|
||||
private URI source;
|
||||
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
public Result<Pair<ValidateWithSchematron, Transformation>, String> build(final ContentRepository repository) {
|
||||
if (this.executable == null && this.source == null) {
|
||||
return createError(String.format("Must supply source location and/or executable for schematron '%s'", this.name));
|
||||
}
|
||||
final ValidateWithSchematron object = createObject();
|
||||
Result<Pair<ValidateWithSchematron, Transformation>, 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<Pair<ValidateWithSchematron, Transformation>, 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;
|
||||
}
|
||||
}
|
||||
109
src/main/java/de/kosit/validationtool/config/XPathBuilder.java
Normal file
109
src/main/java/de/kosit/validationtool/config/XPathBuilder.java
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
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;
|
||||
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.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;
|
||||
|
||||
import net.sf.saxon.s9api.XPathExecutable;
|
||||
|
||||
/**
|
||||
* Internal class to represent xpath configuration.
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@Slf4j
|
||||
class XPathBuilder implements Builder<XPathExecutable> {
|
||||
|
||||
private static final String[] IGNORED_PREFIXES = new String[] { "xsd", "saxon", "xsl", "xs" };
|
||||
|
||||
private final String name;
|
||||
|
||||
private String xpath;
|
||||
|
||||
private XPathExecutable executable;
|
||||
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private Map<String, String> namespaces;
|
||||
|
||||
Map<String, String> getNamespaces() {
|
||||
if (this.namespaces == null) {
|
||||
this.namespaces = new HashMap<>();
|
||||
}
|
||||
return this.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;
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
return this.executable != null || isNotEmpty(this.xpath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<XPathExecutable, String> build(final ContentRepository repository) {
|
||||
if (!isAvailable()) {
|
||||
return createError(String.format("No configuration for %s xpath expression found", this.name));
|
||||
}
|
||||
try {
|
||||
if (this.executable == null) {
|
||||
this.executable = repository.createXPath(this.xpath, getNamespaces());
|
||||
} else {
|
||||
this.xpath = extractExpression();
|
||||
extractNamespaces();
|
||||
}
|
||||
} catch (final IllegalStateException 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));
|
||||
|
||||
}
|
||||
return new Result<>(this.executable);
|
||||
}
|
||||
|
||||
private void extractNamespaces() {
|
||||
|
||||
final Map<String, String> ns = new HashMap<>();
|
||||
final Iterator<String> iterator = this.executable.getUnderlyingExpression().getInternalExpression().getRetainedStaticContext()
|
||||
.iteratePrefixes();
|
||||
final Iterable<String> 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)));
|
||||
getNamespaces().putAll(ns);
|
||||
|
||||
}
|
||||
|
||||
private String extractExpression() {
|
||||
return this.executable.getUnderlyingExpression().getInternalExpression().toString();
|
||||
}
|
||||
|
||||
private static Result<XPathExecutable, String> createError(final String msg) {
|
||||
return new Result<>(null, Collections.singletonList(msg));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
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
|
||||
*/
|
||||
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 {
|
||||
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(OK, 0);
|
||||
final OutputStream os = exchange.getResponseBody();
|
||||
write.write(os);
|
||||
os.close();
|
||||
}
|
||||
|
||||
protected static void error(final HttpExchange exchange, final int statusCode, final String message) throws IOException {
|
||||
final byte[] bytes = message.getBytes();
|
||||
exchange.getResponseHeaders().add("Content-Type", "text/plain");
|
||||
exchange.sendResponseHeaders(statusCode, bytes.length);
|
||||
final OutputStream os = exchange.getResponseBody();
|
||||
os.write(bytes);
|
||||
os.close();
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface Write {
|
||||
|
||||
public void write(OutputStream out) throws IOException;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
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 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
|
||||
@RequiredArgsConstructor
|
||||
class CheckHandler extends BaseHandler {
|
||||
|
||||
private static final AtomicLong counter = new AtomicLong(0);
|
||||
|
||||
private final Check implemenation;
|
||||
|
||||
private final Processor processor;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
"supplied_instance_" + counter.incrementAndGet());
|
||||
final Result result = this.implemenation.checkInput(serverInput);
|
||||
write(httpExchange, serialize(result), APPLICATION_XML);
|
||||
} else {
|
||||
error(httpExchange, 400, "No content supplied");
|
||||
}
|
||||
|
||||
} else {
|
||||
error(httpExchange, 405, "Method not supported");
|
||||
}
|
||||
} catch (final Exception 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
class ConfigHandler extends BaseHandler {
|
||||
|
||||
private final Configuration configuration;
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
@Override
|
||||
public void handle(final HttpExchange exchange) throws IOException {
|
||||
try {
|
||||
final Optional<String> 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<String> getSource() {
|
||||
final URI fileUri = (URI) this.configuration.getAdditionalParameters().get(Keys.SCENARIOS_FILE);
|
||||
return fileUri != null ? loadFile(fileUri) : loadFromConfig();
|
||||
}
|
||||
|
||||
private static Optional<String> 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<String> loadFromConfig() {
|
||||
final Optional<String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
101
src/main/java/de/kosit/validationtool/daemon/Daemon.java
Normal file
101
src/main/java/de/kosit/validationtool/daemon/Daemon.java
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
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.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.model.daemon.HealthType;
|
||||
|
||||
/**
|
||||
* HTTP-Daemon für die Bereitstellung der Prüf-Funktionalität via http.
|
||||
*
|
||||
* @author Roula Antoun
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Setter
|
||||
@Slf4j
|
||||
public class Daemon {
|
||||
|
||||
private static final String DEFAULT_HOST = "localhost";
|
||||
|
||||
private static final int DEFAULT_PORT = 8080;
|
||||
|
||||
private String bindAddress;
|
||||
|
||||
private int port;
|
||||
|
||||
private int threadCount;
|
||||
|
||||
private boolean guiEnabled = 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Methode zum Starten des Servers
|
||||
*
|
||||
* @param config the configuration to use
|
||||
*/
|
||||
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);
|
||||
server.createContext("/", createRootHandler(config));
|
||||
server.createContext("/server/health", new HealthHandler(config, healthConverter));
|
||||
server.createContext("/server/config", new ConfigHandler(config, converter));
|
||||
server.setExecutor(createExecutor());
|
||||
server.start();
|
||||
log.info("Server {} started", server.getAddress());
|
||||
} catch (final IOException e) {
|
||||
log.error("Error starting HttpServer for Valdidator: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private HttpHandler createRootHandler(Configuration config) {
|
||||
HttpHandler rootHandler;
|
||||
final DefaultCheck check = new DefaultCheck(config);
|
||||
final CheckHandler checkHandler = new CheckHandler(check, config.getContentRepository().getProcessor());
|
||||
if (guiEnabled) {
|
||||
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 : Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
|
||||
private InetSocketAddress getSocket() {
|
||||
return new InetSocketAddress(defaultIfBlank(this.bindAddress, DEFAULT_HOST), this.port > 0 ? this.port : DEFAULT_PORT);
|
||||
}
|
||||
}
|
||||
52
src/main/java/de/kosit/validationtool/daemon/GuiHandler.java
Normal file
52
src/main/java/de/kosit/validationtool/daemon/GuiHandler.java
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package de.kosit.validationtool.daemon;
|
||||
|
||||
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;
|
||||
|
||||
import de.kosit.validationtool.api.Configuration;
|
||||
import de.kosit.validationtool.impl.ConversionService;
|
||||
import de.kosit.validationtool.model.daemon.HealthType;
|
||||
import de.kosit.validationtool.model.daemon.MemoryType;
|
||||
|
||||
/**
|
||||
* Handler that implements a simple health check. Useful for monitoring the service.
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
class HealthHandler extends BaseHandler {
|
||||
|
||||
private final Configuration scenarios;
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
@Override
|
||||
public void handle(final HttpExchange httpExchange) throws IOException {
|
||||
final HealthType health = createHealth();
|
||||
final String xml = this.conversionService.writeXml(health);
|
||||
write(httpExchange, xml.getBytes(), APPLICATION_XML);
|
||||
|
||||
}
|
||||
|
||||
private HealthType createHealth() {
|
||||
final HealthType h = new HealthType();
|
||||
h.setMemory(createMemory());
|
||||
h.setApplication(createApplication());
|
||||
h.setStatus(scenarios.getScenarios().size() > 0 ? "UP" : "DOWN");
|
||||
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;
|
||||
}
|
||||
|
||||
private static ApplicationType createApplication() {
|
||||
ApplicationType a = new ApplicationType();
|
||||
a.setBuild(EngineInformation.getBuild());
|
||||
a.setName(EngineInformation.getName());
|
||||
a.setVersion(EngineInformation.getVersion());
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package de.kosit.validationtool.daemon;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
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
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,6 +34,8 @@ import lombok.RequiredArgsConstructor;
|
|||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import de.kosit.validationtool.impl.xml.RelativeUriResolver;
|
||||
|
||||
/**
|
||||
* {@link LSResourceResolver} der objekte relativ zu einem Basis-Pfad aus dem Classpath der Anwendung laden kann.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -24,9 +24,14 @@ 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;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
|
|
@ -39,6 +44,14 @@ import lombok.Getter;
|
|||
import lombok.RequiredArgsConstructor;
|
||||
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;
|
||||
import net.sf.saxon.s9api.XPathCompiler;
|
||||
|
|
@ -55,13 +68,36 @@ 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 URIResolver resolver;
|
||||
|
||||
private final SchemaFactory schemaFactory;
|
||||
|
||||
@Getter
|
||||
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.getProcessor();
|
||||
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());
|
||||
|
|
@ -70,9 +106,9 @@ 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();
|
||||
final SchemaFactory sf = this.schemaFactory;
|
||||
sf.setResourceResolver(resourceResolver);
|
||||
return sf.newSchema(schemaSources);
|
||||
} catch (final SAXException e) {
|
||||
|
|
@ -80,7 +116,7 @@ public class ContentRepository {
|
|||
}
|
||||
}
|
||||
|
||||
private static Schema createSchema(final Source[] schemaSources) {
|
||||
private Schema createSchema(final Source[] schemaSources) {
|
||||
return createSchema(schemaSources, null);
|
||||
}
|
||||
|
||||
|
|
@ -96,15 +132,18 @@ public class ContentRepository {
|
|||
final CollectingErrorEventHandler listener = new CollectingErrorEventHandler();
|
||||
try {
|
||||
xsltCompiler.setErrorListener(listener);
|
||||
xsltCompiler.setURIResolver(createResolver());
|
||||
if (getResolver() != null) {
|
||||
// otherwise use default resolver
|
||||
xsltCompiler.setURIResolver(getResolver());
|
||||
}
|
||||
|
||||
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);
|
||||
} 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));
|
||||
}
|
||||
}
|
||||
|
|
@ -116,11 +155,15 @@ 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 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);
|
||||
}
|
||||
|
|
@ -130,7 +173,7 @@ public class ContentRepository {
|
|||
*
|
||||
* @return Scenario-Schema
|
||||
*/
|
||||
public static Schema getScenarioSchema() {
|
||||
public Schema getScenarioSchema() {
|
||||
return createSchema(ContentRepository.class.getResource("/xsd/scenarios.xsd"));
|
||||
}
|
||||
|
||||
|
|
@ -154,12 +197,37 @@ public class ContentRepository {
|
|||
* @return das Schema
|
||||
*/
|
||||
public Schema createSchema(final Collection<String> uris) {
|
||||
return createSchema(uris.stream().map(s -> resolve(URI.create(s))).toArray(Source[]::new));
|
||||
return createSchema(uris.stream().map(s -> resolveInRepository(URI.create(s))).toArray(Source[]::new));
|
||||
}
|
||||
|
||||
private Source resolve(final URI source) {
|
||||
final URI resolved = RelativeUriResolver.resolve(source, this.repository);
|
||||
return new StreamSource(resolved.toASCIIString());
|
||||
/**
|
||||
* 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<String> schemaResources = s.getValidateWithXmlSchema().getResource().stream().map(ResourceType::getLocation)
|
||||
.collect(Collectors.toList());
|
||||
schema = createSchema(schemaResources);
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -187,7 +255,43 @@ public class ContentRepository {
|
|||
*
|
||||
* @return ein neuer Resolver
|
||||
*/
|
||||
public RelativeUriResolver createResolver() {
|
||||
return new RelativeUriResolver(this.repository);
|
||||
public URIResolver getResolver() {
|
||||
return this.resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt eine Transformation zurück.
|
||||
*
|
||||
* @return initialisierte Transformation
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
public XPathExecutable createMatchExecutable(final ScenarioType s) {
|
||||
final Map<String, String> namespaces = s.getNamespace().stream()
|
||||
.collect(Collectors.toMap(NamespaceType::getPrefix, NamespaceType::getValue));
|
||||
return createXPath(s.getMatch(), namespaces);
|
||||
}
|
||||
|
||||
public XPathExecutable createAccepptExecutable(final ScenarioType s) {
|
||||
final Map<String, String> namespaces = s.getNamespace().stream()
|
||||
.collect(Collectors.toMap(NamespaceType::getPrefix, NamespaceType::getValue));
|
||||
return createXPath(s.getAcceptMatch(), namespaces);
|
||||
}
|
||||
|
||||
public List<Transformation> createSchematronTransformations(final ScenarioType s) {
|
||||
return s.getValidateWithSchematron().isEmpty() ? Collections.emptyList()
|
||||
: s.getValidateWithSchematron().stream().map(this::createSchematronTransformation).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Transformation createSchematronTransformation(final ValidateWithSchematron validateWithSchematron) {
|
||||
return createTransformation(validateWithSchematron.getResource());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ import javax.xml.bind.Unmarshaller;
|
|||
import javax.xml.bind.ValidationEventHandler;
|
||||
import javax.xml.bind.annotation.XmlRegistry;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
|
|
@ -47,7 +46,6 @@ import javax.xml.transform.stream.StreamSource;
|
|||
import javax.xml.validation.Schema;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
|
@ -86,6 +84,15 @@ public class ConversionService {
|
|||
// context setup
|
||||
private JAXBContext jaxbContext;
|
||||
|
||||
public JAXBContext
|
||||
|
||||
getJaxbContext() {
|
||||
if (this.jaxbContext == null) {
|
||||
initialize();
|
||||
}
|
||||
return this.jaxbContext;
|
||||
}
|
||||
|
||||
private static <T> QName createQName(final T model) {
|
||||
return new QName(model.getClass().getSimpleName().toLowerCase());
|
||||
}
|
||||
|
|
@ -141,13 +148,6 @@ public class ConversionService {
|
|||
}
|
||||
}
|
||||
|
||||
private JAXBContext getJaxbContext() {
|
||||
if (this.jaxbContext == null) {
|
||||
initialize();
|
||||
}
|
||||
return this.jaxbContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshalls a specifc xml model into a defined java object.
|
||||
*
|
||||
|
|
@ -233,24 +233,8 @@ public class ConversionService {
|
|||
}
|
||||
}
|
||||
|
||||
public <T> Document writeDocument(final T input) {
|
||||
if (input == null) {
|
||||
throw new ConversionExeption("Can not serialize null");
|
||||
}
|
||||
final DocumentBuilder builder = ObjectFactory.createDocumentBuilder(false);
|
||||
final Document document = builder.newDocument();
|
||||
|
||||
// Marshal the Object to a Document
|
||||
Marshaller marshaller = null;
|
||||
try {
|
||||
marshaller = getJaxbContext().createMarshaller();
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||
marshaller.marshal(input, document);
|
||||
return document;
|
||||
} catch (final JAXBException e) {
|
||||
throw new ConversionExeption(String.format("Error serializing Object %s to document", input.getClass().getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public <T> T readDocument(final Source source, final Class<T> type) {
|
||||
try {
|
||||
|
|
|
|||
27
src/main/java/de/kosit/validationtool/impl/DateFactory.java
Normal file
27
src/main/java/de/kosit/validationtool/impl/DateFactory.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package de.kosit.validationtool.impl;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
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() {
|
||||
final GregorianCalendar cal = new GregorianCalendar();
|
||||
cal.setTime(new Date());
|
||||
return DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package de.kosit.validationtool.impl;
|
||||
|
||||
import static de.kosit.validationtool.impl.DateFactory.createTimestamp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
|
@ -28,13 +30,14 @@ 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;
|
||||
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 +59,11 @@ import net.sf.saxon.s9api.Processor;
|
|||
@Slf4j
|
||||
public class DefaultCheck implements Check {
|
||||
|
||||
@Getter
|
||||
private final ScenarioRepository repository;
|
||||
|
||||
@Getter
|
||||
private final ContentRepository contentRepository;
|
||||
|
||||
@Getter
|
||||
private final ConversionService conversionService;
|
||||
|
||||
private final Configuration configuration;
|
||||
|
||||
@Getter
|
||||
private final List<CheckAction> checkSteps;
|
||||
|
||||
|
|
@ -73,20 +72,20 @@ public class DefaultCheck implements Check {
|
|||
*
|
||||
* @param configuration die Konfiguration
|
||||
*/
|
||||
public DefaultCheck(final CheckConfiguration configuration) {
|
||||
final Processor processor = ObjectFactory.createProcessor();
|
||||
public DefaultCheck(final Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
final ContentRepository content = configuration.getContentRepository();
|
||||
final Processor processor = content.getProcessor();
|
||||
this.conversionService = new ConversionService();
|
||||
this.contentRepository = new ContentRepository(processor, configuration.getScenarioRepository());
|
||||
this.repository = new ScenarioRepository(this.contentRepository);
|
||||
this.repository.initialize(configuration);
|
||||
|
||||
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, this.contentRepository.getReportInputSchema()));
|
||||
this.checkSteps.add(new CreateReportAction(processor, this.conversionService, this.repository, 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());
|
||||
}
|
||||
|
||||
|
|
@ -95,11 +94,13 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Result checkInput(final Input input) {
|
||||
final CheckAction.Bag t = new CheckAction.Bag(input, createReport());
|
||||
|
|
@ -122,7 +123,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) {
|
||||
|
|
@ -139,5 +141,4 @@ public class DefaultCheck implements Check {
|
|||
return (List<XmlError>) (List<?>) errors;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -6,13 +6,16 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import net.sf.saxon.dom.NodeOverNodeInfo;
|
||||
import net.sf.saxon.s9api.Processor;
|
||||
import net.sf.saxon.s9api.SaxonApiException;
|
||||
import net.sf.saxon.s9api.Serializer;
|
||||
import net.sf.saxon.s9api.XPathCompiler;
|
||||
import net.sf.saxon.s9api.XPathExecutable;
|
||||
import net.sf.saxon.s9api.XPathSelector;
|
||||
import net.sf.saxon.s9api.XdmItem;
|
||||
|
|
@ -26,38 +29,51 @@ import net.sf.saxon.s9api.XdmNode;
|
|||
@RequiredArgsConstructor
|
||||
public class HtmlExtractor {
|
||||
|
||||
private final ContentRepository repository;
|
||||
private final Processor processor;
|
||||
|
||||
private XPathExecutable executable;
|
||||
|
||||
public List<XdmNode> extract(XdmNode xdmSource) {
|
||||
public List<XdmNode> extract(final XdmNode xdmSource) {
|
||||
try {
|
||||
final XPathSelector selector = getSelector();
|
||||
selector.setContextItem(xdmSource);
|
||||
return selector.stream().map(this::castToNode).collect(Collectors.toList());
|
||||
return selector.stream().map(HtmlExtractor::castToNode).collect(Collectors.toList());
|
||||
|
||||
} catch (SaxonApiException e) {
|
||||
} catch (final SaxonApiException e) {
|
||||
throw new IllegalStateException("Can not extract html content", e);
|
||||
}
|
||||
}
|
||||
|
||||
private XdmNode castToNode(final XdmItem xdmItem) {
|
||||
private static XdmNode castToNode(final XdmItem xdmItem) {
|
||||
return (XdmNode) xdmItem;
|
||||
}
|
||||
|
||||
private XPathSelector getSelector() {
|
||||
if (executable == null) {
|
||||
Map<String, String> ns = new HashMap<>();
|
||||
if (this.executable == null) {
|
||||
final Map<String, String> ns = new HashMap<>();
|
||||
ns.put("html", "http://www.w3.org/1999/xhtml");
|
||||
executable = repository.createXPath("//html:html", ns);
|
||||
this.executable = createXPath("//html:html", ns);
|
||||
}
|
||||
return executable.load();
|
||||
return this.executable.load();
|
||||
}
|
||||
|
||||
private static String convertToString(final XdmNode element) {
|
||||
private XPathExecutable createXPath(final String expression, final Map<String, String> namespaces) {
|
||||
try {
|
||||
final XPathCompiler compiler = this.processor.newXPathCompiler();
|
||||
if (namespaces != null) {
|
||||
namespaces.forEach(compiler::declareNamespace);
|
||||
}
|
||||
return compiler.compile(expression);
|
||||
} catch (final SaxonApiException e) {
|
||||
throw new IllegalStateException(String.format("Can not compile xpath match expression '%s'",
|
||||
StringUtils.isNotBlank(expression) ? expression : "EMPTY EXPRESSION"), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String convertToString(final XdmNode element) {
|
||||
try {
|
||||
final StringWriter writer = new StringWriter();
|
||||
final Serializer serializer = ObjectFactory.createProcessor().newSerializer(writer);
|
||||
final Serializer serializer = this.processor.newSerializer(writer);
|
||||
serializer.serializeNode(element);
|
||||
return writer.toString();
|
||||
} catch (final SaxonApiException e) {
|
||||
|
|
@ -72,7 +88,7 @@ public class HtmlExtractor {
|
|||
* @return HTML-Fragment als String
|
||||
*/
|
||||
public List<String> extractAsString(final XdmNode node) {
|
||||
return extract(node).stream().map(HtmlExtractor::convertToString).collect(Collectors.toList());
|
||||
return extract(node).stream().map(this::convertToString).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Element> extractAsElement(final XdmNode node) {
|
||||
|
|
|
|||
|
|
@ -1,260 +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 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;
|
||||
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 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;
|
||||
|
||||
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 <a href="https://www.owasp.org/index.php/XML_Security_Cheat_Sheet">OWASP-Empfehlungen</a>.
|
||||
*
|
||||
* 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
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
private 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 {
|
||||
transformer = TransformerFactory.newInstance().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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
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);
|
||||
//hier fehlt eigentlich noch der UriResolver für unparsed text, wird erst ab Saxon 9.8 unterstützt
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package de.kosit.validationtool.impl;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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(new StrictRelativeResolvingStrategy()) {
|
||||
|
||||
},
|
||||
|
||||
STRICT_LOCAL(new StrictLocalResolvingStrategy()),
|
||||
|
||||
ALLOW_REMOTE(new RemoteResolvingStrategy()),
|
||||
|
||||
CUSTOM(null);
|
||||
|
||||
@Getter
|
||||
private final ResolvingConfigurationStrategy strategy;
|
||||
|
||||
}
|
||||
82
src/main/java/de/kosit/validationtool/impl/Scenario.java
Normal file
82
src/main/java/de/kosit/validationtool/impl/Scenario.java
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
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<Transformation> schematronValidations;
|
||||
|
||||
private Transformation reportTransformation;
|
||||
|
||||
public List<Transformation> 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<XPathSelector> getAcceptSelector() {
|
||||
final XPathSelector selector = this.acceptExecutable != null ? this.acceptExecutable.load() : null;
|
||||
return Optional.ofNullable(selector);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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,28 @@ 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;
|
||||
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<XdmNode, XMLSyntaxError> 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<Scenario> 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 +72,9 @@ public class ScenarioRepository {
|
|||
* @param document das Eingabedokument
|
||||
* @return ein Ergebnis-Objekt zur weiteren Verarbeitung
|
||||
*/
|
||||
public Result<ScenarioType, String> selectScenario(final XdmNode document) {
|
||||
final Result<ScenarioType, String> result;
|
||||
final List<ScenarioType> collect = this.scenarios.getScenario().stream().filter(s -> match(document, s))
|
||||
public Result<Scenario, String> selectScenario(final XdmNode document) {
|
||||
final Result<Scenario, String> result;
|
||||
final List<Scenario> collect = getScenarios().stream().filter(s -> match(document, s))
|
||||
.collect(Collectors.toList());
|
||||
if (collect.size() == 1) {
|
||||
result = new Result<>(collect.get(0));
|
||||
|
|
@ -162,23 +88,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 +99,4 @@ public class ScenarioRepository {
|
|||
return false;
|
||||
}
|
||||
|
||||
void initialize(final Scenarios def) {
|
||||
this.scenarios = def;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}. <br/>
|
||||
* <p>
|
||||
* 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:
|
||||
* <ul>
|
||||
* <li>{@link StreamSource} - both {@link java.io.InputStream} based and {@link java.io.Reader} based</li>
|
||||
* <li>{@link javax.xml.transform.dom.DOMSource}</li>
|
||||
* <li>{@link javax.xml.bind.util.JAXBSource}</li>
|
||||
* </ul>
|
||||
*
|
||||
* Other {@link Source Sources} may work as well, please try and let us know.
|
||||
* </p>
|
||||
*
|
||||
* @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,23 +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) {
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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<Transformation> 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<String> 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<Transformation> 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<String, String> 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<NamespaceType> getNamespace();
|
||||
|
||||
/**
|
||||
* Getter aus dem schema.
|
||||
*
|
||||
* @return Validierungsanweisungen
|
||||
*/
|
||||
public abstract ValidateWithXmlSchema getValidateWithXmlSchema();
|
||||
|
||||
/**
|
||||
* Getter aus dem schema.
|
||||
*
|
||||
* @return Validierungsanweisungne
|
||||
*/
|
||||
public abstract List<ValidateWithSchematron> getValidateWithSchematron();
|
||||
|
||||
/**
|
||||
* Getter aus dem schema.
|
||||
*
|
||||
* @return report informationen
|
||||
*/
|
||||
public abstract CreateReportType getCreateReport();
|
||||
|
||||
}
|
||||
|
|
@ -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<ScenarioType, String> scenarioSelectionResult;
|
||||
private Result<Scenario, String> scenarioSelectionResult;
|
||||
|
||||
@Setter(AccessLevel.NONE)
|
||||
private CreateReportInput reportInput;
|
||||
|
|
|
|||
|
|
@ -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<XPathSelector> acceptMatch = results.getScenarioSelectionResult().getObject().getAcceptSelector();
|
||||
if (results.getSchemaValidationResult().isValid() && acceptMatch.isPresent()) {
|
||||
evaluateAcceptanceMatch(results, acceptMatch.get());
|
||||
} else {
|
||||
evaluateSchemaAndSchematron(results);
|
||||
}
|
||||
|
|
@ -53,15 +53,14 @@ 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) {
|
||||
final String msg = "Error evaluating accept recommendation: %s";
|
||||
log.error(msg);
|
||||
results.addProcessingError(msg);
|
||||
final String msg = String.format("Error evaluating accept recommendation: %s", selector.getUnderlyingXPathContext().toString());
|
||||
log.error(msg, e);
|
||||
results.stopProcessing(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package de.kosit.validationtool.impl;
|
||||
package de.kosit.validationtool.impl.tasks;
|
||||
|
||||
import de.kosit.validationtool.impl.tasks.CheckAction;
|
||||
import de.kosit.validationtool.model.reportInput.DocumentIdentificationType;
|
||||
|
||||
/**
|
||||
|
|
@ -8,7 +7,7 @@ import de.kosit.validationtool.model.reportInput.DocumentIdentificationType;
|
|||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
class CreateDocumentIdentificationAction implements CheckAction {
|
||||
public class CreateDocumentIdentificationAction implements CheckAction {
|
||||
|
||||
@Override
|
||||
public void check(final Bag transporter) {
|
||||
|
|
@ -19,26 +19,34 @@
|
|||
|
||||
package de.kosit.validationtool.impl.tasks;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.util.JAXBSource;
|
||||
import javax.xml.transform.URIResolver;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.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;
|
||||
|
|
@ -57,18 +65,117 @@ import net.sf.saxon.s9api.XsltTransformer;
|
|||
* @author Andreas Penski
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
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 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) {
|
||||
this.delegate = xmlReader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getFeature(final String name) throws SAXNotRecognizedException, SAXNotSupportedException {
|
||||
if (SAX_FEATURES_NAMESPACES.equals(name)) {
|
||||
return true;
|
||||
} else if (SAX_FEATURES_NAMESPACE_PREFIXES.equals(name)) {
|
||||
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(SAX_FEATURES_NAMESPACES) && !value) {
|
||||
throw new SAXNotRecognizedException(name);
|
||||
}
|
||||
if (name.equals(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 ScenarioRepository scenarioRepository;
|
||||
private final URIResolver resolver;
|
||||
|
||||
private final ContentRepository contentRepository;
|
||||
|
||||
private static XsltExecutable loadFromScenario(final ScenarioType object) {
|
||||
private static XsltExecutable loadFromScenario(final Scenario object) {
|
||||
return object.getReportTransformation().getExecutable();
|
||||
}
|
||||
|
||||
|
|
@ -80,15 +187,18 @@ 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 RelativeUriResolver resolver = this.contentRepository.createResolver();
|
||||
transformer.setMessageListener(e);
|
||||
transformer.setURIResolver(resolver);
|
||||
transformer.getUnderlyingController().setUnparsedTextURIResolver(resolver);
|
||||
transformer.setURIResolver(this.resolver);
|
||||
// transformer.getUnderlyingController().setUnparsedTextURIResolver(resolver);
|
||||
if (parsedDocument != null) {
|
||||
transformer.setParameter(new QName("input-document"), parsedDocument);
|
||||
}
|
||||
|
|
@ -97,13 +207,14 @@ public class CreateReportAction implements CheckAction {
|
|||
transformer.transform();
|
||||
results.setReport(destination.getXdmNode());
|
||||
|
||||
} catch (final SaxonApiException | SAXException e) {
|
||||
throw new IllegalStateException("Can not create final report", e);
|
||||
} catch (final SaxonApiException | SAXException | JAXBException e) {
|
||||
log.error("Error creating final report", e);
|
||||
results.stopProcessing("Can not create final report: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static XdmNode createErrorInformation(final Collection<XMLSyntaxError> errors) throws SaxonApiException, SAXException {
|
||||
final BuildingContentHandler contentHandler = ObjectFactory.createProcessor().newDocumentBuilder().newBuildingContentHandler();
|
||||
private XdmNode createErrorInformation(final Collection<XMLSyntaxError> errors) throws SaxonApiException, SAXException {
|
||||
final BuildingContentHandler contentHandler = this.processor.newDocumentBuilder().newBuildingContentHandler();
|
||||
contentHandler.startDocument();
|
||||
contentHandler.startElement(EngineInformation.getFrameworkNamespace(), ERROR_MESSAGE_ELEMENT, ERROR_MESSAGE_ELEMENT,
|
||||
new AttributesImpl());
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ import lombok.RequiredArgsConstructor;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import de.kosit.validationtool.api.Input;
|
||||
import de.kosit.validationtool.impl.ObjectFactory;
|
||||
import de.kosit.validationtool.impl.model.Result;
|
||||
import de.kosit.validationtool.model.reportInput.ValidationResultsWellformedness;
|
||||
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
|
||||
import de.kosit.validationtool.model.reportInput.XMLSyntaxErrorSeverity;
|
||||
|
||||
import net.sf.saxon.s9api.DocumentBuilder;
|
||||
import net.sf.saxon.s9api.Processor;
|
||||
import net.sf.saxon.s9api.SaxonApiException;
|
||||
import net.sf.saxon.s9api.XdmNode;
|
||||
|
||||
|
|
@ -46,6 +46,7 @@ import net.sf.saxon.s9api.XdmNode;
|
|||
@RequiredArgsConstructor
|
||||
public class DocumentParseAction implements CheckAction {
|
||||
|
||||
private final Processor processor;
|
||||
/**
|
||||
* Parsed und überprüft ein übergebenes Dokument darauf ob es well-formed ist. Dies stellt den ersten
|
||||
* Verarbeitungsschritt des Prüf-Tools dar. Diese Funktion verzichtet explizit auf die Validierung gegenüber einem
|
||||
|
|
@ -54,13 +55,13 @@ public class DocumentParseAction implements CheckAction {
|
|||
* @param content ein Dokument
|
||||
* @return Ergebnis des Parsings inklusive etwaiger Fehler
|
||||
*/
|
||||
public static Result<XdmNode, XMLSyntaxError> parseDocument(final Input content) {
|
||||
public Result<XdmNode, XMLSyntaxError> parseDocument(final Input content) {
|
||||
if (content == null) {
|
||||
throw new IllegalArgumentException("Input may not be null");
|
||||
}
|
||||
Result<XdmNode, XMLSyntaxError> result;
|
||||
try {
|
||||
final DocumentBuilder builder = ObjectFactory.createProcessor().newDocumentBuilder();
|
||||
final DocumentBuilder builder = this.processor.newDocumentBuilder();
|
||||
builder.setLineNumbering(true);
|
||||
final XdmNode doc = builder.build(content.getSource());
|
||||
result = new Result<>(doc, Collections.emptyList());
|
||||
|
|
|
|||
|
|
@ -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<ScenarioType, String> scenarioTypeResult;
|
||||
final Result<Scenario, String> 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<ScenarioType, String> determineScenario(final XdmNode document) {
|
||||
final Result<ScenarioType, String> result = this.repository.selectScenario(document);
|
||||
private Result<Scenario, String> determineScenario(final XdmNode document) {
|
||||
final Result<Scenario, String> result = this.repository.selectScenario(document);
|
||||
if (result.isInvalid()) {
|
||||
return new Result<>(this.repository.getFallbackScenario());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,19 +36,21 @@ 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;
|
||||
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.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,21 +138,25 @@ 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;
|
||||
|
||||
private Result<Boolean, XMLSyntaxError> validate(final Bag results, final ScenarioType scenarioType) {
|
||||
log.debug("Validating document using scenario {}", scenarioType.getName());
|
||||
private Result<Boolean, XMLSyntaxError> 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 = this.factory.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 +166,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<Boolean, XMLSyntaxError> 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());
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -22,26 +22,23 @@ 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;
|
||||
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.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.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;
|
||||
|
||||
|
|
@ -54,32 +51,33 @@ import net.sf.saxon.s9api.XsltTransformer;
|
|||
@Slf4j
|
||||
public class SchematronValidationAction implements CheckAction {
|
||||
|
||||
private final ContentRepository repository;
|
||||
private final URIResolver resolver;
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
private List<ValidationResultsSchematron> validate(final Bag results, final XdmNode document, final ScenarioType scenario) {
|
||||
private List<ValidationResultsSchematron> 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();
|
||||
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) {
|
||||
|
|
@ -107,7 +105,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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
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.SAXException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import de.kosit.validationtool.api.ResolvingConfigurationStrategy;
|
||||
|
||||
import net.sf.saxon.s9api.Processor;
|
||||
|
||||
/**
|
||||
* @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";
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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 SAXException 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 SAXException;
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -82,22 +81,18 @@ public class RelativeUriResolver implements URIResolver, UnparsedTextURIResolver
|
|||
}
|
||||
|
||||
private boolean isUnderBaseUri(final URI resolved) {
|
||||
final String base = this.baseUri.toASCIIString().replaceAll("file:/+", "");
|
||||
return isUnderBaseUri(resolved, this.baseUri);
|
||||
}
|
||||
|
||||
private static boolean isUnderBaseUri(final URI resolved, final URI baseUri) {
|
||||
if (resolved == null || baseUri == null) {
|
||||
return false;
|
||||
}
|
||||
final String base = baseUri.toASCIIString().replaceAll("file:/+", "");
|
||||
final String r = resolved.toASCIIString().replaceAll("file:/+", "");
|
||||
return r.startsWith(base);
|
||||
}
|
||||
|
||||
@Override
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Slf4j
|
||||
public class StrictLocalResolvingStrategy extends StrictRelativeResolvingStrategy {
|
||||
|
||||
/**
|
||||
* 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");
|
||||
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
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Validator createValidator(final Schema schema) {
|
||||
final Validator validator = super.createValidator(schema);
|
||||
allowExternalSchema(validator, "file");
|
||||
return validator;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package de.kosit.validationtool.impl.xml;
|
||||
|
||||
import java.io.Reader;
|
||||
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 lombok.SneakyThrows;
|
||||
|
||||
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
|
||||
protected 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);
|
||||
processor.setConfigurationProperty(FeatureKeys.XML_PARSER_FEATURE + encode(XMLConstants.ACCESS_EXTERNAL_DTD), false);
|
||||
return processor;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static String encode(final String input) {
|
||||
return URLEncoder.encode(input, StandardCharsets.UTF_8.name());
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -42,21 +42,21 @@
|
|||
<jaxb:schemaBindings>
|
||||
<jaxb:package name="de.kosit.validationtool.model.scenarios"/>
|
||||
</jaxb:schemaBindings>
|
||||
<jaxb:bindings node="//xs:complexType[@name='ScenarioType']">
|
||||
<inheritance:extends>de.kosit.validationtool.impl.model.BaseScenario</inheritance:extends>
|
||||
</jaxb:bindings>
|
||||
</jaxb:bindings>
|
||||
<jaxb:bindings schemaLocation="../xsd/assertions.xsd">
|
||||
<jaxb:schemaBindings>
|
||||
<jaxb:package name="de.kosit.validationtool.cmd.assertions"/>
|
||||
<jaxb:package name="de.kosit.validationtool.cmd.assertions" />
|
||||
</jaxb:schemaBindings>
|
||||
</jaxb:bindings>
|
||||
|
||||
<jaxb:bindings schemaLocation="../xsd/svrl-kosit.xsd">
|
||||
|
||||
<jaxb:bindings node="//xs:element[@name='schematron-output']/xs:complexType">
|
||||
<inheritance:extends>de.kosit.validationtool.impl.model.BaseOutput</inheritance:extends>
|
||||
</jaxb:bindings>
|
||||
</jaxb:bindings>
|
||||
<jaxb:bindings schemaLocation="../xsd/daemon.xsd">
|
||||
<jaxb:schemaBindings>
|
||||
<jaxb:package name="de.kosit.validationtool.model.daemon" />
|
||||
</jaxb:schemaBindings>
|
||||
</jaxb:bindings>
|
||||
|
||||
</jaxb:bindings>
|
||||
41
src/main/model/xsd/daemon.xsd
Normal file
41
src/main/model/xsd/daemon.xsd
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<schema xmlns:d="http://www.xoev.de/de/validator/framework/1/daemon" targetNamespace="http://www.xoev.de/de/validator/framework/1/daemon"
|
||||
elementFormDefault="qualified"
|
||||
xmlns="http://www.w3.org/2001/XMLSchema" version="1.0.0">
|
||||
|
||||
<element name="health" type="d:HealthType"></element>
|
||||
|
||||
<complexType name="ApplicationType">
|
||||
<sequence>
|
||||
<element name="name" type="string"></element>
|
||||
<element name="version" type="string"></element>
|
||||
<element name="build" type="string"></element>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="HealthType">
|
||||
<sequence>
|
||||
<element name="status">
|
||||
<simpleType>
|
||||
<restriction base="string">
|
||||
<enumeration value="UP"></enumeration>
|
||||
<enumeration value="DOWN"></enumeration>
|
||||
<enumeration value="WARN"></enumeration>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
</element>
|
||||
<element name="application" type="d:ApplicationType"></element>
|
||||
|
||||
<element name="memory" type="d:MemoryType"></element>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
|
||||
<complexType name="MemoryType">
|
||||
<sequence>
|
||||
<element name="freeMemory" type="long"></element>
|
||||
<element name="maxMemory" type="long"></element>
|
||||
<element name="totalMemory" type="long"></element>
|
||||
</sequence>
|
||||
</complexType>
|
||||
</schema>
|
||||
22
src/main/resources/gui/README.md
Normal file
22
src/main/resources/gui/README.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# KoSIT Validator - Daemon
|
||||
|
||||
[API usage](docs/api)
|
||||
|
||||
[configurations](docs/configurations)
|
||||
|
||||
# Server information
|
||||
View [validator configuration](/server/config) or <a href="/server/health" target="_blank">health information</a>
|
||||
|
||||
# Try it
|
||||
<div>
|
||||
<form>
|
||||
<div>
|
||||
<label for="file">Choose a file</label>
|
||||
<input type="file" id="file" name="myFile">
|
||||
<input type="submit" id="submit" value="Validate" onclick="return validate();">
|
||||
</div>
|
||||
<div id="result"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
42
src/main/resources/gui/docs/api.md
Normal file
42
src/main/resources/gui/docs/api.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# 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.
|
||||
|
||||
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 = "<file contents here>";
|
||||
|
||||
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));
|
||||
```
|
||||
15
src/main/resources/gui/docs/configurations.md
Normal file
15
src/main/resources/gui/docs/configurations.md
Normal file
|
|
@ -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
|
||||
65
src/main/resources/gui/index.html
Normal file
65
src/main/resources/gui/index.html
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Validator</title>
|
||||
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
|
||||
<meta content="width=device-width,initial-scale=1" name="viewport">
|
||||
<link href="lib/vue.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
|
||||
#result {
|
||||
border: 1pt solid black;
|
||||
margin-top: 20px
|
||||
}
|
||||
|
||||
#result:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
|
||||
|
||||
var validate = function () {
|
||||
const input = document.getElementById('file');
|
||||
const output = document.getElementById('result');
|
||||
|
||||
var headers = new Headers();
|
||||
headers.append('Content-Type', 'application/xml');
|
||||
|
||||
var requestOptions = {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: input.files[0],
|
||||
redirect: 'follow',
|
||||
};
|
||||
|
||||
fetch('/', requestOptions)
|
||||
.then(response => response.text())
|
||||
.then(result => output.innerText = result)
|
||||
.catch(error => output.innerText = result);
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div data-app id="app">Loading validator... </div>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
repo: "itplr-kosit/validator",
|
||||
loadSidebar: false,
|
||||
hideSidebar: true,
|
||||
autoHeader: true,
|
||||
}
|
||||
</script>
|
||||
<script src="lib/docsify.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
23
src/main/resources/gui/lib/License
Normal file
23
src/main/resources/gui/lib/License
Normal file
|
|
@ -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.
|
||||
1
src/main/resources/gui/lib/docsify.min.js
vendored
Normal file
1
src/main/resources/gui/lib/docsify.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
828
src/main/resources/gui/lib/vue.css
Normal file
828
src/main/resources/gui/lib/vue.css
Normal file
|
|
@ -0,0 +1,828 @@
|
|||
/*@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");*/
|
||||
* {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
-webkit-text-size-adjust: none;
|
||||
-webkit-touch-callout: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body:not(.ready) {
|
||||
overflow: hidden;
|
||||
}
|
||||
body:not(.ready) [data-cloak],
|
||||
body:not(.ready) .app-nav,
|
||||
body:not(.ready) > 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;
|
||||
}
|
||||
|
|
@ -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<XdmNode, XMLSyntaxError> parsed = Helper.parseDocument(domInput);
|
||||
assertThat(parsed.isValid()).isTrue();
|
||||
|
||||
// read twice
|
||||
assertThat(Helper.parseDocument(domInput).getObject()).isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ public class CommandlineApplicationTest {
|
|||
|
||||
private final Path output = Paths.get("target/test-output");
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
this.commandLine = new CommandLine();
|
||||
|
|
@ -105,12 +106,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 +121,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 +131,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 +139,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 +148,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 +158,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 +168,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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
|
|
@ -174,7 +178,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 +187,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);
|
||||
|
|
@ -200,4 +204,12 @@ public class CommandlineApplicationTest {
|
|||
assertThat(this.commandLine.getErrorOutput()).contains("at de.kosit.validationtool");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrintMemoryStats() {
|
||||
final String[] args = new String[] { "-m", "-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);
|
||||
assertThat(this.commandLine.getErrorOutput()).contains("total");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
package de.kosit.validationtool.config;
|
||||
|
||||
import static de.kosit.validationtool.config.TestScenarioFactory.createScenario;
|
||||
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}.
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
public class ScenarioBuilderTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException exceptions = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void simpleValid() {
|
||||
final Result<Scenario, String> result = createScenario().build(Simple.createContentRepository());
|
||||
assertThat(result.isValid()).isTrue();
|
||||
assertThat(result.getObject().getConfiguration()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoSchema() {
|
||||
final ScenarioBuilder builder = createScenario();
|
||||
builder.validate((SchemaBuilder) null);
|
||||
final Result<Scenario, String> result = builder.build(Simple.createContentRepository());
|
||||
assertThat(result.isValid()).isFalse();
|
||||
assertThat(result.getErrors()).anyMatch(e -> e.contains("schema"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoMatch() {
|
||||
final ScenarioBuilder builder = createScenario();
|
||||
builder.match((String) null);
|
||||
final Result<Scenario, String> result = builder.build(Simple.createContentRepository());
|
||||
assertThat(result.isValid()).isFalse();
|
||||
assertThat(result.getErrors()).anyMatch(e -> e.contains("match"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidMatch() {
|
||||
final ScenarioBuilder builder = createScenario();
|
||||
builder.match("/////");
|
||||
final Result<Scenario, String> result = builder.build(Simple.createContentRepository());
|
||||
assertThat(result.isValid()).isFalse();
|
||||
assertThat(result.getErrors()).anyMatch(e -> e.contains("match"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoAccept() {
|
||||
final ScenarioBuilder builder = createScenario();
|
||||
builder.acceptWith((String) null);
|
||||
final Result<Scenario, String> result = builder.build(Simple.createContentRepository());
|
||||
assertThat(result.isValid()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidAccept() {
|
||||
final ScenarioBuilder builder = createScenario();
|
||||
builder.acceptWith("/////");
|
||||
final Result<Scenario, String> result = builder.build(Simple.createContentRepository());
|
||||
assertThat(result.isValid()).isFalse();
|
||||
assertThat(result.getErrors()).anyMatch(e -> e.contains("accept"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCombinedNamespaces() {
|
||||
final ContentRepository repository = Simple.createContentRepository();
|
||||
final Map<String, String> ns1 = new HashMap<>();
|
||||
ns1.put("n1", "http://n1.org");
|
||||
final XPathExecutable match = repository.createXPath("//n1:*", ns1);
|
||||
|
||||
final Map<String, String> ns2 = new HashMap<>();
|
||||
ns2.put("n2", "http://n2.org");
|
||||
final XPathExecutable accept = repository.createXPath("//n2:*", ns2);
|
||||
|
||||
final ScenarioBuilder builder = createScenario();
|
||||
builder.getNamespaces().clear();
|
||||
|
||||
builder.match(match).acceptWith(accept).declareNamespace("n3", "http://n3.org");
|
||||
final Result<Scenario, String> result = builder.build(repository);
|
||||
|
||||
assertThat(result.isValid()).isTrue();
|
||||
final Scenario scenario = result.getObject();
|
||||
final List<NamespaceType> 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 = createScenario();
|
||||
builder.getNamespaces().clear();
|
||||
|
||||
builder.match(match);
|
||||
builder.acceptWith(accept);
|
||||
final Result<Scenario, String> 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 = createScenario();
|
||||
builder.name(random).description(random);
|
||||
final Result<Scenario, String> 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 = createScenario();
|
||||
builder.name(null);
|
||||
final Result<Scenario, String> 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
package de.kosit.validationtool.config;
|
||||
|
||||
import static de.kosit.validationtool.config.ConfigurationBuilder.schema;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import javax.xml.validation.Schema;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
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 de.kosit.validationtool.model.scenarios.ResourceType;
|
||||
import de.kosit.validationtool.model.scenarios.ValidateWithXmlSchema;
|
||||
|
||||
/**
|
||||
* Tests {@link SchemaBuilder}.
|
||||
*
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
public class SchemaBuilderTest {
|
||||
|
||||
@Test
|
||||
public void testBuildSchema() {
|
||||
final SchemaBuilder builder = schema(Simple.SCHEMA);
|
||||
final Result<Pair<ValidateWithXmlSchema, Schema>, 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<Pair<ValidateWithXmlSchema, Schema>, 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<Pair<ValidateWithXmlSchema, Schema>, 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<Pair<ValidateWithXmlSchema, Schema>, 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<Pair<ValidateWithXmlSchema, Schema>, 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<Pair<ValidateWithXmlSchema, Schema>, 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<Pair<ValidateWithXmlSchema, Schema>, 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<Pair<ValidateWithXmlSchema, Schema>, String> result = builder.build(repository);
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.isValid()).isTrue();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package de.kosit.validationtool.config;
|
||||
|
||||
import static de.kosit.validationtool.config.ConfigurationBuilder.fallback;
|
||||
import static de.kosit.validationtool.config.TestScenarioFactory.createScenario;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
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 = 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(createScenario()
|
||||
// .description("awesome api")
|
||||
).with(fallback().name("default").source("report.xsl"))
|
||||
|
||||
.resolvingMode(ResolvingMode.STRICT_RELATIVE).useRepository(Simple.REPOSITORY_URI);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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<Scenario> scenarios;
|
||||
|
||||
private Scenario fallbackScenario;
|
||||
|
||||
private String author;
|
||||
|
||||
private String name;
|
||||
|
||||
private String date;
|
||||
|
||||
private ContentRepository contentRepository;
|
||||
|
||||
private Map<String, Object> additionalParameters;
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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<XPathExecutable, String> 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<String, String> ns = new HashMap<>();
|
||||
ns.put("p", "http://somens");
|
||||
b.setNamespaces(ns);
|
||||
b.setXpath("//p:*");
|
||||
final Result<XPathExecutable, String> 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<String, String> ns = new HashMap<>();
|
||||
ns.put("p", "http://somens");
|
||||
b.setNamespaces(ns);
|
||||
b.setXpath("//u:*");
|
||||
final Result<XPathExecutable, String> 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<XPathExecutable, String> 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<String, String> 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<XPathExecutable, String> 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<XPathExecutable, String> 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<XPathExecutable, String> result = b.build(Simple.createContentRepository());
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.isValid()).isFalse();
|
||||
}
|
||||
}
|
||||
26
src/test/java/de/kosit/validationtool/daemon/BaseIT.java
Normal file
26
src/test/java/de/kosit/validationtool/daemon/BaseIT.java
Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package de.kosit.validationtool.cmd;
|
||||
package de.kosit.validationtool.daemon;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
|
||||
|
|
@ -6,39 +6,23 @@ 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;
|
||||
|
||||
/**
|
||||
* Testet the Daemon-Mode input , Methoden , Output Content-Type and the success case
|
||||
*
|
||||
* @author Roula Antoun
|
||||
* @author Andreas Penski
|
||||
*/
|
||||
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 {
|
||||
try ( final InputStream io = Simple.SIMPLE_VALID.toURL().openStream() ) {
|
||||
|
|
@ -63,15 +47,7 @@ public class DaemonIT {
|
|||
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 {
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -55,12 +55,12 @@ public class ContentRepositoryTest {
|
|||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.repository = new ContentRepository(ObjectFactory.createProcessor(), Simple.REPOSITORY);
|
||||
this.repository = Simple.createContentRepository();
|
||||
}
|
||||
|
||||
@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
|
||||
|
|
@ -114,7 +114,8 @@ public class ContentRepositoryTest {
|
|||
|
||||
@Test
|
||||
public void loadFromJar() throws URISyntaxException {
|
||||
this.repository = new ContentRepository(ObjectFactory.createProcessor(), Helper.JAR_REPOSITORY.toURI());
|
||||
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,16 +123,28 @@ 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"));
|
||||
assert main != null;
|
||||
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,
|
||||
assert main != null;
|
||||
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(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());
|
||||
// ScenarioRepository.initialize(conf);
|
||||
// assertThat(this.repository.getScenarios()).isNotNull();
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -72,7 +70,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 +78,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 +86,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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -28,16 +27,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;
|
||||
|
||||
/**
|
||||
|
|
@ -47,6 +47,7 @@ import net.sf.saxon.s9api.XdmNode;
|
|||
*/
|
||||
|
||||
public class Helper {
|
||||
|
||||
public static class Simple {
|
||||
|
||||
public static final URI ROOT = EXAMPLES_DIR.resolve("simple/");
|
||||
|
|
@ -61,7 +62,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,10 +74,17 @@ 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 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);
|
||||
}
|
||||
|
||||
public static URI getSchemaLocation() {
|
||||
return ROOT.resolve("repository/simple.xsd");
|
||||
return SCHEMA;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,27 +97,27 @@ 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");
|
||||
|
||||
|
||||
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
|
||||
*
|
||||
|
|
@ -118,7 +126,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);
|
||||
|
||||
|
|
@ -134,27 +142,31 @@ 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(ObjectFactory.createProcessor(), 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 = ObjectFactory.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<XdmNode, XMLSyntaxError> parseDocument(final Processor processor, final Input input) {
|
||||
return new DocumentParseAction(processor).parseDocument(input);
|
||||
}
|
||||
|
||||
public static Result<XdmNode, XMLSyntaxError> parseDocument(final Input 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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
@ -56,20 +58,20 @@ 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();
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,20 +24,20 @@ 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 org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import de.kosit.validationtool.api.CheckConfiguration;
|
||||
import de.kosit.validationtool.config.TestConfiguration;
|
||||
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.XPathExecutable;
|
||||
import net.sf.saxon.s9api.XdmNode;
|
||||
|
||||
/**
|
||||
|
|
@ -51,72 +51,69 @@ public class ScenarioRepositoryTest {
|
|||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
ContentRepository content;
|
||||
|
||||
private ScenarioRepository repository;
|
||||
|
||||
private TestConfiguration 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);
|
||||
this.configInstance = new TestConfiguration();
|
||||
this.configInstance.setContentRepository(new ContentRepository(ResolvingMode.STRICT_RELATIVE.getStrategy(), null));
|
||||
|
||||
final Scenario s = createScenario();
|
||||
this.configInstance.setScenarios(new ArrayList<>());
|
||||
this.configInstance.getScenarios().add(s);
|
||||
this.repository = new ScenarioRepository(this.configInstance);
|
||||
}
|
||||
|
||||
private Scenario createScenario() {
|
||||
final Scenario s = new Scenario(new ScenarioType());
|
||||
s.setMatchExecutable(createXpath("//*:name"));
|
||||
return s;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHappyCase() throws Exception {
|
||||
final Result<ScenarioType, String> scenario = this.repository.selectScenario(load(Simple.SCENARIOS));
|
||||
final Result<Scenario, String> 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<ScenarioType, String> scenario = this.repository.selectScenario(load(Simple.SCENARIOS));
|
||||
this.configInstance.setScenarios(new ArrayList<>());
|
||||
final Scenario fallback = createFallback();
|
||||
this.configInstance.setFallbackScenario(fallback);
|
||||
final Result<Scenario, String> 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<ScenarioType, String> scenario = this.repository.selectScenario(load(Simple.SCENARIOS));
|
||||
this.configInstance.getScenarios().add(createScenario());
|
||||
this.configInstance.setFallbackScenario(createFallback());
|
||||
final Result<Scenario, String> scenario = this.repository.selectScenario(load(Simple.SCENARIOS));
|
||||
assertThat(scenario).isNotNull();
|
||||
assertThat(scenario.isValid()).isFalse();
|
||||
assertThat(scenario.getObject().getName()).isEqualTo("fallback");
|
||||
}
|
||||
|
||||
private static XdmNode load(final URI uri) throws IOException {
|
||||
final DocumentParseAction p = new DocumentParseAction();
|
||||
return DocumentParseAction.parseDocument(read(uri.toURL())).getObject();
|
||||
private XdmNode load(final URI uri) throws IOException {
|
||||
return Helper.parseDocument(this.configInstance.getContentRepository().getProcessor(), 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 XPathExecutable createXpath(final String expression) {
|
||||
return this.configInstance.getContentRepository().createXPath(expression, new HashMap<>());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue