Search code examples
javaspringspring-bootxsltjavabeans

Javax Transformer is null in mutli threaded service


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;
    }
}


Solution

  • 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.