From 8123dba37b873c6e95108ba1ce8f021d950f6541 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Wed, 2 Jul 2014 21:00:03 +0200 Subject: [PATCH] BUG-1281: cache TransformerFactory and Transformer The code used to instantiate a TransformerFactory for each individual request, where a single factory can be shared between threads as long as it is not reconfigured. As a futher optimization, we also create a thread-local cache for transformers, as they can be use for multiple transformations. Change-Id: I836148208be62f66cb6b509b0746e9fb92a569f0 Signed-off-by: Robert Varga --- .../impl/StructuredDataToXmlProvider.java | 63 +++++++++++++------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java index bcb3c422ff..e5a56cf475 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/StructuredDataToXmlProvider.java @@ -21,6 +21,7 @@ import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; @@ -40,46 +41,68 @@ import org.w3c.dom.Document; @Provider @Produces({ Draft02.MediaTypes.API + RestconfService.XML, Draft02.MediaTypes.DATA + RestconfService.XML, - Draft02.MediaTypes.OPERATION + RestconfService.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML }) + Draft02.MediaTypes.OPERATION + RestconfService.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML }) public enum StructuredDataToXmlProvider implements MessageBodyWriter { INSTANCE; - private final static Logger logger = LoggerFactory.getLogger(StructuredDataToXmlProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(StructuredDataToXmlProvider.class); + private static final TransformerFactory FACTORY = TransformerFactory.newInstance(); + private static final ThreadLocal TRANSFORMER = new ThreadLocal() { + @Override + protected Transformer initialValue() { + final Transformer ret; + try { + ret = FACTORY.newTransformer(); + } catch (TransformerConfigurationException e) { + LOG.error("Failed to instantiate XML transformer", e); + throw new IllegalStateException("XML encoding currently unavailable", e); + } + + ret.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); + ret.setOutputProperty(OutputKeys.METHOD, "xml"); + ret.setOutputProperty(OutputKeys.INDENT, "yes"); + ret.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + ret.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + + return ret; + } + }; @Override - public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + public boolean isWriteable(final Class type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return type.equals( StructuredData.class ); } @Override - public long getSize(StructuredData t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + public long getSize(final StructuredData t, final Class type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return -1; } @Override - public void writeTo(StructuredData t, Class type, Type genericType, Annotation[] annotations, - MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) - throws IOException, WebApplicationException { + public void writeTo(final StructuredData t, final Class type, final Type genericType, final Annotation[] annotations, + final MediaType mediaType, final MultivaluedMap httpHeaders, final OutputStream entityStream) + throws IOException, WebApplicationException { CompositeNode data = t.getData(); if (data == null) { throw new RestconfDocumentedException(Response.Status.NOT_FOUND); } - XmlMapper xmlMapper = new XmlMapper(); - Document domTree = xmlMapper.write(data, (DataNodeContainer) t.getSchema()); + final Transformer trans; + try { + trans = TRANSFORMER.get(); + } catch (RuntimeException e) { + throw new RestconfDocumentedException(e.getMessage(), ErrorType.TRANSPORT, + ErrorTag.OPERATION_FAILED); + } + + // FIXME: BUG-1281: eliminate the intermediate Document + final Document domTree = new XmlMapper().write(data, (DataNodeContainer) t.getSchema()); try { - TransformerFactory tf = TransformerFactory.newInstance(); - Transformer transformer = tf.newTransformer(); - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); - transformer.setOutputProperty(OutputKeys.METHOD, "xml"); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); - transformer.transform(new DOMSource(domTree), new StreamResult(entityStream)); + trans.transform(new DOMSource(domTree), new StreamResult(entityStream)); } catch (TransformerException e) { - logger.error("Error during translation of Document to OutputStream", e); - throw new RestconfDocumentedException( e.getMessage(), ErrorType.TRANSPORT, - ErrorTag.OPERATION_FAILED ); + LOG.error("Error during translation of Document to OutputStream", e); + throw new RestconfDocumentedException(e.getMessage(), ErrorType.TRANSPORT, + ErrorTag.OPERATION_FAILED); } } -- 2.36.6