Reputation: 58083
I have a code which transform xml file using xsl, my peace of code as following. My problem is when i run the execution point it gives me following error.
StackTrace: javax.xml.transform.TransformerException: javax.xml.transform.TransformerException: com.sun.org.apache.xml.internal.utils.WrappedRuntimeException: /home/app/myapp/bin/xhtml11-flat.dtd (No such file or directory)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:720)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:313)
at com.core.util.XmlUtils.transform(XmlUtils.java:151)
at com.core.util.XmlUtils.transform(XmlUtils.java:147)
Long story short it is trying to find dtd file inside the bin directory from where i executed the application.
/home/app/myapp/bin/xhtml11-flat.dtd
I have the xhtml11-flat.dtd file if i copy this file in bin directory it works fine, instead of the bin directory i want to load it from classpath any idea how can i achieve this with minimum changes ? I don't know from where it is laoding .dtd code so that i can set my path in it.
//Execution Point
function transform(){
Templates templates = getTemplates();
StringWriter result = new StringWriter();
XmlUtils.transform(templates.newTransformer(), input, new StreamResult(result));
...
}
private Templates getTemplates() throws Exception {
if (templates == null) {
templates = XmlUtils.createTemplates(XslRdcSourceDocTransformer.class.getResourceAsStream("/xsl/" + getXslFileName()));
}
return templates;
}
public static Templates createTemplates(InputStream stream) throws Exception {
TransformerFactory tfactory = TransformerFactory.newInstance();
return tfactory.newTemplates(new StreamSource(stream));
}
Upvotes: 1
Views: 2014
Reputation: 34014
Your xml files are probably containing a doctype declaration containing a relative path to the dtd:
<!DOCTYPE html SYSTEM "xhtml11-flat.dtd">
The transformer api tries to resolve this path to the current working directory of the java program. To customize how the path is resolved you need to implement a EntityResolver
. This EntityResolver
can return an InputSource
referring to a copy of the dtd loaded from the classpath.
public InputSource resolveEntity(final String publicId, final String systemId) throws SAXException {
if ("xhtml11-flat.dtd".equals(systemId)) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
InputSource is = new InputSource();
is.setSystemId(systemId);
is.setByteStream(cl.getResourceAsStream("/com/example/dtd/xhtml11-flat.dtd"));
return is;
} else {
return null;
}
}
How you use this class depends on the type of source for your transformation. For a DOMSource
you have to configure the DocumentBuilder
:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
DocumentBuilder builder = ...
builder.setEntityResolver(entityResolver);
Source source = new DOMSource(builder.parse(inputStream));
For a SAXSource
the setting is on the XMLReader
instance:
SAXParserFactory factory1 = SAXParserFactory.newInstance();
factory1.setValidating(false);
factory1.setNamespaceAware(true);
SAXParser parser = factory1.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
xmlreader.setEntityResolver(entityResolver);
Source source = new SAXSource(xmlreader, new InputSource(stream));
The code for the transformation is the same regardless of the source type and should look similar to the code you currently have in your XmlUtils
class:
Templates templates = ...
Result result = new StreamResult(...);
Transformer transformer = templates.newTransformer();
transformer.transform(source, result);
Upvotes: 5