8 Commits

Author SHA1 Message Date
Wojciech Czop
b3666e21df Fix headers 2023-01-04 15:23:36 +01:00
Wojciech Czop
98025d763f T285 Added OpenApi 2021-05-07 10:43:06 +02:00
767051f1ad T285 added readme file 2021-05-06 17:02:15 +02:00
708cc415d6 T285 removed drafts 2021-05-06 16:44:10 +02:00
4413405b7d T285 Added Javadoc 2021-05-06 16:11:09 +02:00
7a7834fb06 T285 javadoc added 2021-04-30 18:08:39 +02:00
2268f8f741 T288 SparkInitializer created. Added package for java classes 2021-04-30 17:48:00 +02:00
0b0ed6af98 Added .gitignore to backend 2021-04-30 17:31:14 +02:00
32 changed files with 475 additions and 42 deletions

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

9
REST_XSLT/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
target/
nbactions.xml
.idea/
.settings/
.classpath
.settings
.project
*.iml
.vscode

2
REST_XSLT/RestTMP.iml Normal file
View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4" />

View File

@@ -24,7 +24,7 @@
<!-- <manifest>-->
<!-- <addClasspath>true</addClasspath>-->
<!-- <classpathPrefix>lib/</classpathPrefix>-->
<!-- <mainClass>Main</mainClass>-->
<!-- <mainClass>r11.mltx.restxslt.Main</mainClass>-->
<!-- </manifest>-->
<!-- </archive>-->
<!-- </configuration>-->
@@ -48,7 +48,7 @@
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>Main</mainClass>
<mainClass>r11.mltx.restxslt.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>

View File

@@ -0,0 +1,15 @@
package r11.mltx.restxslt;
/**
* Application initializer
* @author Wojciech Czop
*/
public class Main {
/**
* Initializes the application
* @param args
*/
public static void main(String[] args) {
SparkInitializer.run();
}
}

View File

@@ -1,14 +1,24 @@
package r11.mltx.restxslt;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import processors.Saxon;
import processors.Xalan;
import r11.mltx.restxslt.processors.Saxon;
import r11.mltx.restxslt.processors.Xalan;
import spark.*;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
/**
* Class contains spark configuration and method initializing spark framework
* @author Wojciech Czop
*/
public class SparkInitializer {
/**
* Initializes spark framework
*/
public static void run(){
// TODO: Port value as property
Spark.port(8081);
Spark.after((Filter) (request, response) -> {
@@ -25,9 +35,11 @@ public class Main {
System.out.println("Server is online");
}
static Route procinfoHandler = (Request req, Response resp) -> {
/**
* Handler that returns processor version
*/
private static Route procinfoHandler = (Request req, Response resp) -> {
try {
// req.session().id
resp.header("processor", "Saxon " + Saxon.getVersion() + " over s9api");
return Saxon.getVersion();
} catch (Exception ex) {
@@ -35,7 +47,11 @@ public class Main {
}
};
static Route xsdHandler = (Request req, Response resp) -> {
/**
* Handler that returns info if document is valid
* Also provides info about request time and processor
*/
private static Route xsdHandler = (Request req, Response resp) -> {
String body = req.body();
System.out.println("Request: " + body);
ObjectMapper mapper = new ObjectMapper();
@@ -72,7 +88,10 @@ public class Main {
return resp;
};
static Route xpathHandler = (Request req, Response resp) -> {
/**
* Handler that returns output of xpath query and processor data
*/
private static Route xpathHandler = (Request req, Response resp) -> {
String body = req.body();
System.out.println("Request: " + body);
@@ -148,7 +167,10 @@ public class Main {
}
};
static Route xsltHandler = (Request req, Response resp) -> {
/**
* Handler that returns outcome of xslt transformation and processor data
*/
private static Route xsltHandler = (Request req, Response resp) -> {
String body = req.body();
ObjectMapper mapper = new ObjectMapper();
Map<String, String> jsonMap = null;
@@ -173,7 +195,6 @@ public class Main {
long duration = 0;
switch (processor) {
case "saxon":
// resp.header("processor", "Saxon " + Saxon.getVersion());
timeStart = System.currentTimeMillis();
try {
tmp = Saxon.processXSLT(data, query);
@@ -215,6 +236,5 @@ public class Main {
} catch (Exception ex) {
return ex.getMessage();
}
};
}

View File

@@ -1,4 +1,4 @@
package processors;
package r11.mltx.restxslt.processors;
import net.sf.saxon.om.NamespaceBinding;
import net.sf.saxon.om.NamespaceMap;
@@ -7,29 +7,50 @@ import net.sf.saxon.s9api.XdmNode;
import java.util.Iterator;
/**
* Handler for saxon namespace scan engine.
* All found namespaces are stored within {@link #namespaceMap}
* @author Wojciech Czop
*/
public class NewNamespaceResolver {
private NamespaceMap namespaceMap;
/**
* Initializes {@link #namespaceMap} with namespace values
* @param doc dom structure object
* @return map of namespaces
*/
public NamespaceMap process(XdmNode doc) {
namespaceMap = NamespaceMap.emptyMap();
Iterator<XdmNode> it = doc.children().iterator();
// TODO: remove
while (it.hasNext()) {
XdmNode tmp = it.next();
extractNamespace(tmp);
}
// end
return namespaceMap;
}
/**
* Iterates through {@link #namespaceMap} and declares namespaces in {@link XPathCompiler}
* @param compiler compiler used to compile xpath statements
*/
public void exportNamespaces(XPathCompiler compiler){
Iterator<NamespaceBinding> it = namespaceMap.iterator();
// TODO: remove
while(it.hasNext()){
System.out.println(it.next());
}
// end
namespaceMap.forEach(namespaceBinding -> compiler.declareNamespace(namespaceBinding.getPrefix(), namespaceBinding.getURI()));
}
/**
* Uses recurrency to dive deep dom structure and appends {@link #namespaceMap} with every found namespace
* @param node dom structure object
*/
private void extractNamespace(XdmNode node) {
NamespaceMap tmp;
if ((tmp = node.getUnderlyingNode().getAllNamespaces()) != null) {
@@ -40,6 +61,7 @@ public class NewNamespaceResolver {
Iterator<XdmNode> it = node.children().iterator();
while (it.hasNext()) {
XdmNode rNode = it.next();
// TODO: remove
if(rNode.getUnderlyingNode().getPrefix().isEmpty() && !rNode.getParent().getUnderlyingNode().getPrefix().isEmpty()){
System.out.println("prefix missing, parent has "+rNode.getParent().getUnderlyingNode().getPrefix() + ", but child has none");
@@ -47,6 +69,7 @@ public class NewNamespaceResolver {
NamespaceMap nsTMP= rNode.getUnderlyingNode().getAllNamespaces();
System.out.println();
}
// end
extractNamespace(rNode);
}
}

View File

@@ -1,4 +1,4 @@
package processors;
package r11.mltx.restxslt.processors;
import net.sf.saxon.om.NamespaceMap;
import net.sf.saxon.s9api.*;
@@ -6,7 +6,19 @@ import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.io.StringWriter;
/**
* Handler for Saxon engine
* @author Wojciech Czop
*/
public class Saxon {
/**
* Transforms string containing xml document via xslt
* @param data xml to be transformed
* @param transform xslt
* @return transformed xml
* @throws SaxonApiException
*/
public static String processXSLT(String data, String transform) throws SaxonApiException {
Processor processor = new Processor(false);
XsltCompiler compiler = processor.newXsltCompiler();
@@ -21,14 +33,20 @@ public class Saxon {
return sw.toString();
}
/**
* Process xpath and return either node or wrapped atomic value
* @param data xml to be querried
* @param query xpath queryy
* @param version processor version
* @return string xml representation of the node
* @throws Exception
*/
public static String processXPath(String data, String query, String version) throws Exception {
Processor p = new Processor(false);
XPathCompiler compiler = p.newXPathCompiler();
DocumentBuilder builder = p.newDocumentBuilder();
XdmNode doc = builder.build(new StreamSource(new StringReader(data)));
// System.out.println(version);
compiler.setLanguageVersion(version);
NewNamespaceResolver resolver = new NewNamespaceResolver();
@@ -47,6 +65,10 @@ public class Saxon {
}
/**
* Returns version of the processor
* @return version of the processor
*/
public static String getVersion() {
return new Processor(false).getSaxonProductVersion();
}

View File

@@ -1,10 +1,9 @@
package processors;
package r11.mltx.restxslt.processors;
import org.apache.xpath.domapi.XPathEvaluatorImpl;
import net.sf.saxon.s9api.SaxonApiException;
import org.w3c.dom.Document;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
@@ -18,29 +17,33 @@ import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Handler for Xalan engine
* @author Wojciech Czop
*/
public class Xalan {
/**
* Transforms string containing xml document via xslt
* @param data xml to be transformed
* @param transform xslt
* @return transformed xml
* @throws Exception
*/
public static String processXSLT(String data, String transform) throws Exception{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(data)));
StreamSource stylesource = new StreamSource(new StringReader(transform));
Transformer transformer = TransformerFactory.newInstance().newTransformer(stylesource);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
Source source = new DOMSource(document);
StringWriter sw = new StringWriter();
Result outputTarget = new StreamResult(sw);
@@ -49,6 +52,15 @@ public class Xalan {
return sw.toString();
}
/**
* Process xpath and return either node or wrapped atomic value
* @deprecated
* Xalan needs assumption of the outcome, which is not implemented. Therefore method is deprecated
* @param data
* @param transform
* @return
* @throws Exception
*/
public static String processXPath(String data, String transform) throws Exception{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
@@ -61,31 +73,30 @@ public class Xalan {
String result = exp.evaluate(new InputSource(new StringReader(data)));
return result;
}
/**
* Returns version of the processor
* @return version of the processor
*/
public static String getVersion(){
return org.apache.xalan.Version.getVersion();
}
/**
* Validates string representation of the xml document against xsd schema
* @param data xml document
* @param xsd xsd schema
* @return statement of validity
* @throws Exception
*/
public static String validate(String data, String xsd) throws Exception{
Source dataSource = new StreamSource(new StringReader(data));
Source xsdSource = new StreamSource(new StringReader(xsd));
SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// try {
Schema schema = schemaFactory.newSchema(xsdSource);
Validator validator = schema.newValidator();
validator.validate(dataSource);
// System.out.println(dataSource.getSystemId() + " is valid");
return "XML file is valid";
// } catch (SAXException e) {
// System.out.println("Invalid: "+e.getMessage());
// return "XML file is NOT valid: " + e.getMessage();
//// System.out.println(dataSource.getSystemId() + " is NOT valid reason:" + e);
// } catch (IOException e) {
// return "IO error: "+e.getMessage();
// }
}
}

View File

@@ -1,4 +1,4 @@
package processors;
package r11.mltx.restxslt.processors;
import org.w3c.dom.*;
@@ -8,6 +8,12 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Contains methods that scan document for namespaces and store them in map objects.
* @deprecated
* Class no longer in use. It has been replaced by {@link NewNamespaceResolver}
* @author Wojciech Czop
*/
public class XalanNamespaceResolver implements NamespaceContext {
private static final String DEFAULT_NS = "DEFAULT";
private Map<String, String> prefix2Uri = new HashMap<String, String>();
@@ -84,6 +90,11 @@ public class XalanNamespaceResolver implements NamespaceContext {
}
/**
* Stores namespace prefix ass well as its uri in map
* @param prefix
* @param uri
*/
private void putInCache(String prefix, String uri) {
prefix2Uri.put(prefix, uri);
uri2Prefix.put(uri, prefix);

279
openapi.yml Normal file
View File

@@ -0,0 +1,279 @@
swagger: "2.0"
info:
description: "This is a simple API allowing you to perform XSLT and XPath operations <br>
Currently we support Xalan and Saxon (libxml2 is in progress) for XSLT and Saxon for XPath"
version: "0.3"
title: "XML Tools API"
host: "gordon.zipper.release11.com:8081"
schemes:
- http
tags:
- name: "XPath"
description: "XPath processing API"
- name: "XSLT"
description: "XSLT processing API"
- name: "XSD"
description: "XSD verification API"
paths:
/xpathpost:
post:
tags:
- "XPath"
summary: "Perform XPath transform on XML file"
description: ""
operationId: "xpath"
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- in: "body"
name: "request"
description: "A transform that is to be performed"
required: true
schema:
$ref: "#/definitions/RequestXPath"
responses:
"200":
description: "successful operation"
schema:
type: "array"
items:
$ref: "#/definitions/Response"
"400":
description: "Invalid status value"
schema:
type: "array"
items:
$ref: "#/definitions/ResponseError"
/xsltpost:
post:
tags:
- "XSLT"
summary: "Perform XSLT transform on XML file"
description: ""
operationId: "xslt"
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- in: "body"
name: "request"
description: "A transform that is to be performed"
required: true
schema:
$ref: "#/definitions/RequestXSLT"
responses:
"200":
description: "successful operation"
schema:
type: "array"
items:
$ref: "#/definitions/Response"
"400":
description: "Invalid status value"
schema:
type: "array"
items:
$ref: "#/definitions/ResponseError"
/xsdpost:
post:
tags:
- "XSD"
summary: "Verify XML file using XSD"
description: ""
operationId: "xsd"
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- in: "body"
name: "request"
description: "A XML that is to be verified"
required: true
schema:
$ref: "#/definitions/RequestXSD"
responses:
"200":
description: "successful operation"
schema:
type: "array"
items:
$ref: "#/definitions/XSDResponse"
"400":
description: "Invalid status value"
schema:
type: "array"
items:
$ref: "#/definitions/XSDError"
definitions:
RequestXPath:
type: "object"
properties:
data:
type: "string"
example: "<values>
<value>Test1</value>
<value>Test3</value>
</values>"
description: "The XML data to be processed"
process:
type: "string"
example: "count(//value)"
description: "XPath tranform to be executed"
processor:
type: "string"
enum:
- "saxon"
- "xalan"
version:
type: "string"
enum:
- "2.0"
- "3.0"
- "3.1"
RequestXSLT:
type: "object"
properties:
data:
type: "string"
example: "<values>
<value>Test1</value>
<value>Test3</value>
</values>"
description: "The XML data to be processed"
process:
type: "string"
example: "count(//value)"
description: "XSLT tranform to be executed"
processor:
type: "string"
enum:
- "saxon"
- "xalan"
version:
type: "string"
enum:
- "2.0"
- "3.0"
- "3.1"
RequestXSD:
type: "object"
properties:
data:
type: "string"
example: "<shiporder orderid=\"889923\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">
<shipto>
<name>Ola Nordmann</name>
<address>Langgt 23</address>
<city>4000 Stavanger</city>
<country>Norway</country>
</shipto>
</shiporder>"
description: "The XML data to be processed"
process:
type: "string"
example: "<shiporder orderid=\"889923\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">
<shipto>
<name>Ola Nordmann</name>
<address>Langgt 23</address>
<city>4000 Stavanger</city>
<country>Norway</country>
</shipto>
</shiporder>"
description: "XSD to perform verification with"
processor:
type: "string"
enum:
- "saxon"
- "xalan"
version:
type: "string"
enum:
- "2.0"
- "3.0"
- "3.1"
Response:
type: "object"
properties:
result:
type: "string"
example: "4"
description: "Result of performing transformation on provided XML"
time:
type: "string"
example: "320"
description: "Computation time in milliseconds"
processor:
type: "string"
enum:
- "Saxon 10.3 2.0 over s9api"
- "Xalan Java 2.7.2"
status:
type: "string"
enum:
- "OK"
ResponseError:
type: "object"
properties:
result:
type: "string"
example: "Concatenation operator ('||') requires XPath 3.0 to be enabled"
description: "Error from XPath processor"
time:
type: "string"
example: "40"
description: "Computation time in milliseconds"
processor:
type: "string"
enum:
- "Saxon 10.3 2.0 over s9api"
- "Xalan Java 2.7.2"
status:
type: "string"
enum:
- "ERR"
XSDResponse:
type: "object"
properties:
result:
type: "string"
example: "XML file is valid"
description: "Validation result"
time:
type: "string"
example: "7"
description: "Computation time in milliseconds"
processor:
type: "string"
enum:
- "Xalan Java 2.7.2"
status:
type: "string"
enum:
- "OK"
XSDError:
type: "object"
properties:
result:
type: "string"
example: "The end-tag for element type \"xs:complexType\" must end with a '>' delimiter."
description: "Validation result"
time:
type: "string"
example: "7"
description: "Computation time in milliseconds"
processor:
type: "string"
enum:
- "Xalan Java 2.7.2"
status:
type: "string"
enum:
- "ERR"
externalDocs:
description: "Find out more about Swagger"
url: "http://swagger.io"

41
readme.md Normal file
View File

@@ -0,0 +1,41 @@
# XML Tools
Content of this project is a service hosting tools for xml documents including xslt transformer,
xpath selector and xsd validator.
## About project
Service is split into two microservices one containing JavaSpark web application and second hosting
static documents.
## JavaSpark backend
Backend is hosted by default on port 8081. Rest api documentation is contained in OpenApi document
openapi.yml
Rest API accepts xml documents, given querry and processor version to call requested xml engine to perform given
operation and finally returns outcome in response body.
## Frontend
All the static files are available by default on port 8086 and are located within folder.
To preview given document user is required to post GET request to following path:
```aidl
/Frontend/{document name}
```
Documents are hosted by docker container running on ngix image and to modify container content
one must modify Dockerfile located in Frontend folder. Files published must be located in ngix directory
within created container.
## How to run
### Localy
In order to run application use
```aidl
docker-compose up --build -d
```
Which will launch service on localhost.
### Remote
To launch service on remote host, exchange ssh keys with destination server and declare docker context ex. DEV.
Then use following command
```aidl
docker-compose up --context DEV --build -d
```