Added backend support for multiple files in XSLT. #272
| @@ -42,6 +42,7 @@ public class SparkApplication { | ||||
|         registry.registerController(new ProcessorInfoController(logger, saxon, xalan)); | ||||
|  | ||||
|         registry.registerController(new XmlController(gson, logger, saxon, xalan)); | ||||
|         registry.registerController(new MultipleXMLController(gson,logger, saxon)); | ||||
|         registry.registerController(new JsonController(gson, jsongson, logger)); | ||||
|  | ||||
|         registry.register(); | ||||
|   | ||||
| @@ -0,0 +1,96 @@ | ||||
| package com.r11.tools.controller; | ||||
|  | ||||
| import com.google.gson.Gson; | ||||
| import com.r11.tools.controller.internal.*; | ||||
| import com.r11.tools.model.XMLMultipleFilesBody; | ||||
| import com.r11.tools.model.XMLResponseBody; | ||||
| import com.r11.tools.xml.XmlEngine; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| import spark.Request; | ||||
| import spark.Response; | ||||
|  | ||||
| @GlobalControllerManifest | ||||
| public class MultipleXMLController implements RestController { | ||||
|  | ||||
|     private final Gson gson; | ||||
|     private final Logger logger; | ||||
|  | ||||
|     private final XmlEngine engine; | ||||
|  | ||||
|     public MultipleXMLController(Gson gson, Logger logger, XmlEngine engine) { | ||||
|         this.gson = gson; | ||||
|         this.logger = logger; | ||||
|         this.engine = engine; | ||||
|     } | ||||
|  | ||||
|     @ScopedControllerManifest(method = HandlerType.POST, path = "/multiple/xslt") | ||||
|     public void acceptRequestXslt(Request request, Response response) { | ||||
|         acceptRequest(request, response, XmlJobType.XSLT); | ||||
|     } | ||||
|  | ||||
|     private void acceptRequest(Request request, Response response, XmlJobType xmlJobType) { | ||||
|         XMLMultipleFilesBody requestBody; | ||||
|         try { | ||||
|             requestBody = this.gson.fromJson(request.body(), XMLMultipleFilesBody.class); | ||||
|         } catch (Exception e) { | ||||
|             requestErrorResponse(response, e); | ||||
|             return; | ||||
|         } | ||||
|         processRequest(new MultipleXmlJob(response, requestBody, engine, xmlJobType)); | ||||
|     } | ||||
|  | ||||
|     private void processRequest(MultipleXmlJob xmlJob) { | ||||
|         XMLResponseBody responseBody = null; | ||||
|         long timeStart = System.currentTimeMillis(); | ||||
|         long duration; | ||||
|  | ||||
|         try { | ||||
|             responseBody = processData(xmlJob); | ||||
|  | ||||
|             duration = System.currentTimeMillis() - timeStart; | ||||
|             responseBody.setDuration(duration); | ||||
|  | ||||
|             xmlJob.getResponse().status(200); | ||||
|  | ||||
|             this.logger.info("Request (" + xmlJob.getXmlJobType() + ", " + | ||||
|                     xmlJob.getEngine().getVersion() + | ||||
|                     ") processed in " + duration + " ms."); | ||||
|  | ||||
|         } catch (Exception ex) { | ||||
|             responseBody = processingErrorResponse(ex, xmlJob); | ||||
|  | ||||
|         } finally { | ||||
|             xmlJob.getResponse().body(this.gson.toJson(responseBody)); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private XMLResponseBody processData(MultipleXmlJob xmlJob) throws Exception { | ||||
|         XmlEngine engine = xmlJob.getEngine(); | ||||
|         XMLMultipleFilesBody requestBody = xmlJob.getRequestBody(); | ||||
|         String result = engine.processXSLT(requestBody.getData(), requestBody.getProcessorData()); | ||||
|         return new XMLResponseBody(result, "OK", requestBody.getVersion()); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private XMLResponseBody processingErrorResponse(Exception ex, MultipleXmlJob xmlJob) { | ||||
|         XmlEngine engine = xmlJob.getEngine(); | ||||
|         XmlJobType xmlJobType = xmlJob.getXmlJobType(); | ||||
|         Response response = xmlJob.getResponse(); | ||||
|  | ||||
|         XMLResponseBody responseBody = | ||||
|                 new XMLResponseBody(ex.getMessage(), "ERR", engine.getVersion(), -1); | ||||
|  | ||||
|         response.status(400); | ||||
|         this.logger.error("Error on processing " + xmlJobType + " using " + engine.getVersion() + ". " + ex); | ||||
|  | ||||
|         return responseBody; | ||||
|     } | ||||
|  | ||||
|     private void requestErrorResponse(Response response, Exception ex) { | ||||
|         XMLResponseBody responseBody = new XMLResponseBody(ex.getMessage(), "ERR", "N/A", -1); | ||||
|         response.status(400); | ||||
|         response.body(this.gson.toJson(responseBody)); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| package com.r11.tools.controller.internal; | ||||
|  | ||||
| import com.r11.tools.model.XMLMultipleFilesBody; | ||||
| import com.r11.tools.xml.XmlEngine; | ||||
|  | ||||
| import spark.Response; | ||||
| 
				
					
						bema
						commented  Same as above Same as above | ||||
|  | ||||
| public class MultipleXmlJob { | ||||
|  | ||||
|     private final Response response; | ||||
|     private final XMLMultipleFilesBody requestBody; | ||||
|     private final XmlEngine engine; | ||||
|     private final XmlJobType xmlJobType; | ||||
|  | ||||
|     public MultipleXmlJob(Response response, XMLMultipleFilesBody requestBody, XmlEngine engine, XmlJobType xmlJobType) { | ||||
|         this.response = response; | ||||
|         this.requestBody = requestBody; | ||||
|         this.engine = engine; | ||||
|         this.xmlJobType = xmlJobType; | ||||
|     } | ||||
|  | ||||
|     public Response getResponse() { | ||||
|         return response; | ||||
|     } | ||||
|  | ||||
|     public XMLMultipleFilesBody getRequestBody() { | ||||
|         return requestBody; | ||||
|     } | ||||
|  | ||||
|     public XmlEngine getEngine() { | ||||
|         return engine; | ||||
|     } | ||||
|  | ||||
|     public XmlJobType getXmlJobType() { | ||||
|         return xmlJobType; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,32 @@ | ||||
| package com.r11.tools.model; | ||||
|  | ||||
| import com.google.gson.annotations.SerializedName; | ||||
|  | ||||
| public class XMLMultipleFilesBody { | ||||
|  | ||||
|     @SerializedName("data") | ||||
|     private XMLMultipleFilesData[] data; | ||||
|     @SerializedName("processorData") | ||||
|     private String processorData; | ||||
|     @SerializedName("processor") | ||||
|     private String processor; | ||||
|     @SerializedName("version") | ||||
|     private String version; | ||||
|  | ||||
|  | ||||
|     public String getProcessorData() { | ||||
|         return processorData; | ||||
|     } | ||||
|  | ||||
|     public String getProcessor() { | ||||
|         return processor; | ||||
|     } | ||||
|  | ||||
|     public String getVersion() { | ||||
|         return version; | ||||
|     } | ||||
|  | ||||
|     public XMLMultipleFilesData[] getData() { | ||||
|         return data; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,18 @@ | ||||
| package com.r11.tools.model; | ||||
|  | ||||
| import com.google.gson.annotations.SerializedName; | ||||
|  | ||||
| public class XMLMultipleFilesData { | ||||
|     @SerializedName("fileName") | ||||
|     private String filename; | ||||
|     @SerializedName("fileData") | ||||
|     private String data; | ||||
|  | ||||
|     public String getFilename() { | ||||
|         return filename; | ||||
|     } | ||||
|  | ||||
|     public String getData() { | ||||
|         return data; | ||||
|     } | ||||
| } | ||||
| @@ -1,11 +1,16 @@ | ||||
| package com.r11.tools.xml; | ||||
|  | ||||
| import com.r11.tools.model.XMLMultipleFilesData; | ||||
| import com.r11.tools.model.XPathQueryResult; | ||||
| import net.sf.saxon.s9api.*; | ||||
|  | ||||
| import javax.xml.transform.stream.StreamSource; | ||||
| import java.io.StringReader; | ||||
| import java.io.StringWriter; | ||||
| import java.io.*; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.Paths; | ||||
| import java.util.Comparator; | ||||
| import java.util.UUID; | ||||
|  | ||||
| /** | ||||
|  * Handler for Saxon engine | ||||
| @@ -13,6 +18,64 @@ import java.io.StringWriter; | ||||
|  */ | ||||
| public class Saxon implements XmlEngine{ | ||||
|  | ||||
|     /** | ||||
|      *  Transforms many XML documents via XSLT. | ||||
|      * @param data XML Files to be transformed. | ||||
|      * @param transform XSLT | ||||
|      * @return transformed xml | ||||
|      * @throws SaxonApiException thrown on stylesheet or transformation error | ||||
|      * @throws IOException thrown when file does not exist, or cannot be read. | ||||
|      */ | ||||
| 
				
					
						bema
						commented  I would extract some parts of the code from this method to separate private methods to increase readability I would extract some parts of the code from this method to separate private methods to increase readability | ||||
|     public String processXSLT(XMLMultipleFilesData[] data, String transform) throws SaxonApiException, IOException{ | ||||
|         Processor processor = new Processor(false); | ||||
|         XsltCompiler compiler = processor.newXsltCompiler(); | ||||
|  | ||||
|         String filesPath = "/tmp/"+UUID.randomUUID()+"/"; | ||||
|         try{ | ||||
|             createXMLFilesFromData(data, filesPath); | ||||
|             Path transformPath = createXSLTFileAndReturnPath(transform,filesPath); | ||||
|             XsltExecutable stylesheet = compiler.compile( new StreamSource( transformPath.toFile() )); | ||||
|  | ||||
|             StringWriter sw = new StringWriter(); | ||||
|             Serializer out = processor.newSerializer(sw); | ||||
|             out.setOutputProperty(Serializer.Property.METHOD, "xml"); | ||||
|             out.setOutputProperty(Serializer.Property.INDENT, "yes"); | ||||
|             Xslt30Transformer transformer = stylesheet.load30(); | ||||
|             transformer.transform( new StreamSource( new File(filesPath+data[0].getFilename()) ) , out ); | ||||
|             return sw.toString(); | ||||
|         } finally { | ||||
|             deleteTemporaryFiles(filesPath); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void createXMLFilesFromData( XMLMultipleFilesData[] data , String filesPath ) throws IOException { | ||||
|         Files.createDirectories(Paths.get(filesPath)); | ||||
|         for (XMLMultipleFilesData fileData : data) { | ||||
|             Path filePath = Files.createFile( Paths.get(filesPath + fileData.getFilename() ) ); | ||||
|             try (FileWriter writer = new FileWriter(filePath.toFile())) { | ||||
|                 writer.write(fileData.getData()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private Path createXSLTFileAndReturnPath( String xsltTransform, String filesPath ) throws IOException { | ||||
|         Path transformPath = Files.createFile( Paths.get(filesPath + "transform.xsl") ); | ||||
|         FileWriter writer = new FileWriter(transformPath.toFile()); | ||||
|         writer.write(xsltTransform); | ||||
|         writer.close(); | ||||
|  | ||||
|         return transformPath; | ||||
|     } | ||||
|  | ||||
|     private void deleteTemporaryFiles(String filesPath) throws IOException { | ||||
|         Files | ||||
|                 .walk( Paths.get(filesPath) ) | ||||
|                 .sorted(Comparator.reverseOrder()) | ||||
|                 .map(Path::toFile) | ||||
|                 .forEach(File::delete); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Transforms string containing xml document via xslt | ||||
|      * @param data xml to be transformed | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| package com.r11.tools.xml; | ||||
|  | ||||
| import com.r11.tools.model.XMLMultipleFilesData; | ||||
| import com.r11.tools.model.XPathQueryResult; | ||||
| import org.apache.xpath.XPathAPI; | ||||
| import org.w3c.dom.Document; | ||||
| @@ -17,7 +18,10 @@ import javax.xml.transform.stream.StreamSource; | ||||
| import javax.xml.validation.Schema; | ||||
| import javax.xml.validation.SchemaFactory; | ||||
| import javax.xml.validation.Validator; | ||||
| import java.io.*; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.OutputStreamWriter; | ||||
| import java.io.StringReader; | ||||
| import java.io.StringWriter; | ||||
|  | ||||
| /** | ||||
|  * Handler for Xalan engine | ||||
| @@ -57,6 +61,11 @@ public class Xalan implements XmlEngine{ | ||||
|         return nodeType == Node.CDATA_SECTION_NODE || nodeType == Node.TEXT_NODE; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String processXSLT(XMLMultipleFilesData[] data, String transform) throws Exception { | ||||
|         throw new UnsupportedOperationException("Xalan does not support multiple files XSLT processing"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Process xpath and return either node or wrapped atomic value | ||||
|      * @param data xml | ||||
|   | ||||
| @@ -1,8 +1,11 @@ | ||||
| package com.r11.tools.xml; | ||||
|  | ||||
| import com.r11.tools.model.XMLMultipleFilesData; | ||||
| import com.r11.tools.model.XPathQueryResult; | ||||
|  | ||||
| public interface XmlEngine { | ||||
|  | ||||
|     String processXSLT(XMLMultipleFilesData[] data, String transform) throws Exception; | ||||
|     XPathQueryResult processXPath(String data, String query, String version) throws Exception; | ||||
|     String processXSLT(String data, String transform) throws Exception; | ||||
|     String validate(String data, String xsd) throws Exception; | ||||
|   | ||||
| @@ -108,6 +108,7 @@ function prepareRequestBody():string { | ||||
|         "processor": engine.value, | ||||
|         "version": version.value | ||||
|     }); | ||||
|     console.log(requestBody) | ||||
| 
				
					
						bema
						commented  We should left console.logs like this in production code as it serves no purpose, altough I understand that can happen ;) We should left console.logs like this in production code as it serves no purpose, altough I understand that can happen ;) | ||||
|     return requestBody; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	
Unused import, removal needed