Disable caching in Javax xml transformer - java

In this article (https://www.ahoi-it.de/ahoi/news/java-xslt-memory-leak/4830) it is explained that Javax xml transformer caches XML contents to its internal HashMap for later use.
This is my issue: I am reading XML messages from activemq and if something fails, I retry to convert them again using Javax XML transformer and send them to certain endpoint. The problem is that eventually my Docker container restarts because it runs out of memory.
What I would like to do is disable caching, unfortunately, after 3 hours of research I still have no idea how to do so.
I have a utils class with static methods and this is how my Javax XML Transformer looks like:
public static String getTransformedXml(Object input, String transformerFileName)
throws IOException, TransformerException {
ClassPathResource classPathResource = new ClassPathResource(transformerFileName);
InputStream xsltStream = classPathResource.getInputStream();
TransformerFactory factory = TransformerFactory.newInstance();
Source xslt = new StreamSource(xsltStream);
Transformer transformer = factory.newTransformer(xslt);
transformer.setErrorListener(new XsltTransformerErrorListener(transformerFileName));
Source text = new StreamSource(new StringReader(XmlUtils.encode(input, input.getClass())));
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(text, result);
return result.getWriter().toString();
}

Try to change the TransformerFactoryImpl :
TransformerFactory tFactory = TransformerFactory.newInstance("org.apache.xalan.processor.TransformerFactoryImpl",null);

Related

How do I write a DOM Document to File?

How do I write this document to the local filesystem?
public void docToFile(org.w3c.dom.Document document, URI path) throws Exception {
File file = new File(path);
}
I need to iterate the document, or might there be a "to xml/html/string" method? I was looking at:
document.getXmlEncoding();
Not quite what I'm after -- but something like that. Looking for the String representation and then to write that to file like:
Path file = ...;
byte[] buf = ...;
Files.write(file, buf);
https://docs.oracle.com/javase/tutorial/essential/io/file.html
I would use a transformer class to convert the DOM content to an xml file, something like below:
Document doc =...
// write the content into xml file
DOMSource source = new DOMSource(doc);
FileWriter writer = new FileWriter(new File("/tmp/output.xml"));
StreamResult result = new StreamResult(writer);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(source, result);
I hope this ends up working for you!

Using stream to avoid temp file

I'm trying to create a PDF document with Apache Library FOP. It works well but I have to create a temp file to store an intermediate file (test.fop).
Here is my function:
#RequestMapping(value = "/pdf")
public void showPdfReport(OutputStream pdfOutStream) throws Exception
{
// Setup
FopFactory fopFactory = FopFactory.newInstance();
TransformerFactory factory = TransformerFactory.newInstance();
// Transformation XML to XML-FO
StreamSource xsltSource = new StreamSource(servletContext.getResourceAsStream("resources/xsl/pdfTemplate.fo"));
Transformer transformer = factory.newTransformer(xsltSource);
OutputStream fopOutStream = new BufferedOutputStream(new FileOutputStream(new File(
"C:/Temp/tests/test.fop")));
StreamSource xmlSource = new StreamSource(servletContext.getResourceAsStream("resources/xsl/test.xml"));
StreamResult fopResult = new StreamResult(fopOutStream);
transformer.transform(xmlSource, fopResult);
// Transformation XSL-FO to PDF
Source fopSrc = new StreamSource(new File("C:/Temp/tests/test.fop"));
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, pdfOutStream);
Result res = new SAXResult(fop.getDefaultHandler());
transformer = factory.newTransformer();
transformer.transform(fopSrc, res);
}
Is it possible to store test.fop in a stream or any buffer instead of a file?
There's an example on the FOP website that explains exactly this. The SAX events emitted from the XSLT transformation are directly fed into FOP. No buffering in a file or in memory.
Sure, you can pass any Reader or InputStream to the StreamSource constructor
You could use StringReader or ByteArrayInputStream for example.

Replace text in XML before XSLT

I need to replace a certain text in a XML file before giving it to the XSL-Transformer.
It's the DTD-URL in the DOCTYPE tag. It points to a webserver, but I want it to be usable offline, so I want to change it to a URL pointing to a local file.
However I mustn't edit the original XML directly. I thought of reading the file into a string, use String.replaceAll() on the text and save it into another file, which I pass to the Transformer. I already tried it, but it's really slow; the file I'm using has a size of ca. 500kiB.
Is there any better (=faster) way to accomplish this?
EDIT: The code used for the transformation:
public String getPlaylist(String playlist) {
Source source = new StreamSource(library);
StreamSource xsl = new StreamSource(getClass().getResourceAsStream("M3Utransformation.xml"));
StringWriter w = new StringWriter();
Result result = new StreamResult(w);
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer(xsl);
transformer.setParameter("playlist", playlist);
transformer.transform(source, result);
return w.getBuffer().toString();
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
You can create an entity resolver, and make use of it.
The following example uses the JAXP DocumentBuilder, and a CatalogResolver
public static void main(String[] args) throws ParserConfigurationException,
SAXException, IOException, TransformerConfigurationException, TransformerException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
db.setEntityResolver(new CatalogResolver());
File src = new File("src/article.xml");
Document doc = db.parse(src);
// Here, we execute the transformation
// Use a Transformer for output
File stylesheet = new File("src/aticle.xsl");
TransformerFactory tFactory = TransformerFactory.newInstance();
StreamSource stylesource = new StreamSource(stylesheet);
Transformer transformer = tFactory.newTransformer(stylesource);
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(System.out);
transformer.transform(source, result);
}
create a catalog properties file, and place it on your classpath
CatalogManager.properties has to be the name, see CatalogManager API documentation
define a catalog XML file, point your properties file, above to it. From
http://www.xml.com/pub/a/2004/03/03/catalogs.html you can find a very simple catalog XML file :
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
<public publicId="-//OYRM/foo" uri="src/bar.dtd"/>
</catalog>
With the above catalog.xml and CatalogManager.properties, you'll end up resolving references to the publicId "-//OYRM/foo" to the uri src/bar.dtd
xml-commons contains the resolver :
http://xerces.apache.org/mirrors.cgi#binary
for a more complete treatment of the topic of Resolvers read Tom White's article from XML.com
The transformer application was cribbed from the Java trail for Extensible StyleSheet Language Transformations > Transforming Data with XSLT

What to do with generated SOAP calls from a WSDL file in Netbeans

I'm just starting out with Java, but my boss is pushing for this. I've taken the WSDL file we had, and generated a web service and a web client in Netbeans. Also, I've dragged the new service into my client and made sure, that the code works. But now I have a question. How do work with the result? The code in my JSP looks like this:
try {
Soap.PDFSignatureServiceService service = new Soap.PDFSignatureServiceService();
javax.xml.namespace.QName portQName = new javax.xml.namespace.QName("http://external.ltc.com/", "PDFSignatureServicePort");
String req = "<getTimestamp xmlns=\"http://external.ltc.com/\"><msisdn>ENTER VALUE</msisdn></getTimestamp>";
javax.xml.ws.Dispatch<javax.xml.transform.Source> sourceDispatch = null;
sourceDispatch = service.createDispatch(portQName, javax.xml.transform.Source.class, javax.xml.ws.Service.Mode.PAYLOAD);
javax.xml.transform.Source result = sourceDispatch.invoke(new javax.xml.transform.stream.StreamSource(new java.io.StringReader(req)));
out.print(result.toString());
} catch (Exception ex) {
out.print(ex.getMessage());
}
The code works, but what it is printing out is: com.sun.xml.ws.util.xml.StAXSource#90fe8e. What in the world do I do with this? I was expecting either a SOAP message, or an integer of some sorts. How do I get to somewhere from here?
Thanks!
What you are doing here is creating a dynamic client and actually you are working on the payload level and not on the SOAP message level.
For the SOAP level you would have to do:
Dispatch<SOAPMessage> dispatch
The result in your case is the XML payload of the response.
You can for example convert it to String and see it:
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
StringWriter writer = new StringWriter();
Result stringOut = new StreamResult(writer);
transformer.transform(result, stringOut);
writer.close();
System.out.println(writer.toString());
Or convert it to XML node:
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
DOMResult domResult = new DOMResult();
transformer.transform(result, domResult);
rootNode = domResult.getNode();
Disclaimer:Did not even attempt to compile the code
If you look at the Dispatch.invoke specs in javadocs, it is :
http://docs.oracle.com/javase/6/docs/api/javax/xml/ws/Dispatch.html#invoke(T).
And if you change your code to :
**javax.xml.transform.stream.StreamSource** result = sourceDispatch.invoke(new javax.xml.transform.stream.StreamSource(new java.io.StringReader(req)));
The StreamSource has methods to get the Reader and eventually some meaningful result can b obtained.

How to 'transform' a String object (containing XML) to an element on an existing JSP page

Currently, I have a String object that contains XML elements:
String carsInGarage = garage.getCars();
I now want to pass this String as an input/stream source (or some kind of source), but am unsure which one to choose and how to implement it.
Most of the solutions I have looked at import the package: javax.xml.transform and accept a XML file (stylerXML.xml) and output to a HTML file (outputFile.html) (See code below).
try
{
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(new StreamSource("styler.xsl"));
transformer.transform(new StreamSource("stylerXML.xml"), new StreamResult(new FileOutputStream("outputFile.html")));
}
catch (Exception e)
{
e.printStackTrace();
}
I want to accept a String object and output (using XSL) to a element within an existing JSP page. I just don't know how to implement this, even having looked at the code above.
Can someone please advise/assist. I have searched high and low for a solution, but I just can't pull anything out.
Use a StringReader and a StringWriter:
try {
StringReader reader = new StringReader("<xml>blabla</xml>");
StringWriter writer = new StringWriter();
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(
new javax.xml.transform.stream.StreamSource("styler.xsl"));
transformer.transform(
new javax.xml.transform.stream.StreamSource(reader),
new javax.xml.transform.stream.StreamResult(writer));
String result = writer.toString();
} catch (Exception e) {
e.printStackTrace();
}
If at some point you want the source to contain more than just a single string, or you don't want to generate the XML wrapper element manually, create a DOM document that contains your source and pass it to the transformer using a DOMSource.
This worked for me.
String str = "<my>xml</my>"
StreamSource src = new StreamSource(new StringReader(str));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Result res = new StreamResult(baos);
transformer.transform(src, res);

Categories