I am using Javax Transformer class to transform xmls using xslts. Everything works fine when there is less load. But things start to break when load comes. Basically transformer shows weird behaviour and is set to null thereby throwing the exception.
Code
@Service
public class XmlProcessorUtil {
private static final ObjectPool<XPath> pool = new GenericObjectPool<XPath>(new
XPathPoolFactory());
public String transformXmlUsingXsltWithParams(String xsltPath, Document xml,
HashMap<String, String> params) {
Source xslt = new StreamSource(new File(xsltPath));
Source xmlSource = new DOMSource(xml);
return transformXmlUsingXsltWithParams(xslt, xmlSource, params);
}
public String transformXmlUsingXsltWithParams(Source xslt, Source xml, HashMap<String, String> params)
{
String result = "";
StringWriter writer = new StringWriter();
if(xslt == null)
return null;
Transformer transformer = null;
try
{
transformer = TransformerFactory.newInstance().newTransformer(xslt);
}
catch (Exception e)
{
return null;
}
if(params != null && !params.isEmpty())
{
for(String key : params.keySet())
{
if(transformer != null)
transformer.setParameter(key, params.get(key));
}
}
try
{
if(transformer==null) {
LOGGER.info("Transformer is null!!");
}
if(xml==null) {
LOGGER.info("XML is null!!");
}
transformer.transform(xml, new StreamResult(writer));
}
catch (TransformerException e)
{
e.printStackTrace();
return null;
}
result = writer.toString();
return result;
}
}
When searching in logs there are many instances of "Transformer is null!!" . Further the error is intermittent. The class is a bean and method used for transformation(transformXmlUsingXsltWithParams) is an instance method as evident . No exception comes while setting this
transformer = TransformerFactory.newInstance().newTransformer(xslt);
but transformer is still null.
PS : this method is highly called by multiple threads hence I have used transformer as a local variable in a function
Can someone provide a workaround.
EDIT :
@Bean
TransformerFactory transformerFactory() {
return TransformerFactory.newInstance();
}
@Service
public class XmlProcessorUtil {
private static final ObjectPool<XPath> pool = new GenericObjectPool<XPath>(new XPathPoolFactory());
public static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(XmlProcessorUtil.class);
@Autowired
TransformerFactory transformerFactory ;
public String transformXmlUsingXsltWithParams(String xsltPath, Document xml, HashMap<String, String> params) {
Source xslt = new StreamSource(new File(xsltPath));
Source xmlSource = new DOMSource(xml);
return transformXmlUsingXsltWithParams(xslt, xmlSource, params);
}
public String transformXmlUsingXsltWithParams(Source xslt, Source xml, HashMap<String, String> params)
{
String result = "";
StringWriter writer = new StringWriter();
if(xslt == null)
return null;
Transformer transformer = null;
try
{
transformer = transformerFactory.newTemplates(xslt).newTransformer();
}
catch (Exception e)
{
LOGGER.warn("Error in setting transformer",e);
return null;
}
if(params != null && !params.isEmpty())
{
for(String key : params.keySet())
{
if(transformer != null)
transformer.setParameter(key, params.get(key));
}
}
try
{
if(transformer==null) {
LOGGER.info("Transformer is null!!");
}
if(xml==null) {
LOGGER.info("XML is null!!");
}
transformer.transform(xml, new StreamResult(writer));
}
catch (TransformerException e)
{
e.printStackTrace();
return null;
}
result = writer.toString();
return result;
}
}
It's not entirely clear what's happening here, but your use of JAXP in a multi-threaded environment looks all wrong.
You should be trying to create a single TransformerFactory
instance for the entire application. You should be creating one Templates
object for each stylesheet, caching this if the same stylesheet is to be used for multiple transformations. And you should create one Transformer
(using Templates.newTransformer()
) for each transformation, taking care that the Transformer
can only be used in a single thread.