From d0e96f9658459b47315b8fd6ec96060a6108e308 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Tue, 2 Dec 2014 20:01:53 +0100 Subject: [PATCH] BUG-2459: use thread-local cache of transformers As it turns out, we can reuse transformers in the encode path. Do precisely that, keeping them in thread-local variables. This is safe in context of Netty, as we are invoked only from the netty threadpool. This has the benefit of reusing the transformers across all sessions associated with the threadpool. Change-Id: If57933f68a9c9196b649baea17353fd2bd472e09 Signed-off-by: Robert Varga --- .../handler/NetconfMessageToEXIEncoder.java | 8 +- .../handler/NetconfMessageToXMLEncoder.java | 14 +--- .../handler/ThreadLocalTransformers.java | 82 +++++++++++++++++++ 3 files changed, 86 insertions(+), 18 deletions(-) create mode 100644 opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ThreadLocalTransformers.java diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToEXIEncoder.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToEXIEncoder.java index 55dcd9daba..f1e72ed85f 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToEXIEncoder.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/NetconfMessageToEXIEncoder.java @@ -14,11 +14,9 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import java.io.IOException; import java.io.OutputStream; -import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXResult; -import javax.xml.transform.sax.SAXTransformerFactory; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.openexi.proc.common.EXIOptionsException; import org.openexi.sax.Transmogrifier; @@ -26,10 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class NetconfMessageToEXIEncoder extends MessageToByteEncoder { - private static final Logger LOG = LoggerFactory.getLogger(NetconfMessageToEXIEncoder.class); - - private static final SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); private final NetconfEXICodec codec; public NetconfMessageToEXIEncoder(final NetconfEXICodec codec) { @@ -44,8 +39,7 @@ public final class NetconfMessageToEXIEncoder extends MessageToByteEncoder { private static final Logger LOG = LoggerFactory.getLogger(NetconfMessageToXMLEncoder.class); - private static final TransformerFactory FACTORY = TransformerFactory.newInstance(); private final Optional clientId; @@ -38,13 +34,13 @@ public class NetconfMessageToXMLEncoder extends MessageToByteEncoderabsent()); } - public NetconfMessageToXMLEncoder(Optional clientId) { + public NetconfMessageToXMLEncoder(final Optional clientId) { this.clientId = clientId; } @Override @VisibleForTesting - public void encode(ChannelHandlerContext ctx, NetconfMessage msg, ByteBuf out) throws IOException, TransformerException { + public void encode(final ChannelHandlerContext ctx, final NetconfMessage msg, final ByteBuf out) throws IOException, TransformerException { LOG.trace("Sent to encode : {}", msg); if (clientId.isPresent()) { @@ -53,14 +49,10 @@ public class NetconfMessageToXMLEncoder extends MessageToByteEncoder DEFAULT_TRANSFORMER = new ThreadLocal() { + @Override + protected Transformer initialValue() { + try { + return FACTORY.newTransformer(); + } catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) { + throw new IllegalStateException("Unexpected error while instantiating a Transformer", e); + } + }; + + @Override + public void set(final Transformer value) { + throw new UnsupportedOperationException(); + }; + }; + + private static final ThreadLocal PRETTY_TRANSFORMER = new ThreadLocal() { + @Override + protected Transformer initialValue() { + final Transformer ret; + + try { + ret = FACTORY.newTransformer(); + } catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) { + throw new IllegalStateException("Unexpected error while instantiating a Transformer", e); + } + + ret.setOutputProperty(OutputKeys.INDENT, "yes"); + ret.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + return ret; + }; + + @Override + public void set(final Transformer value) { + throw new UnsupportedOperationException(); + }; + }; + + private ThreadLocalTransformers() { + throw new UnsupportedOperationException("Utility class"); + } + + /** + * Get the transformer with default configuration. + * + * @return A transformer with default configuration based on the default implementation. + */ + public static Transformer getDefaultTransformer() { + return DEFAULT_TRANSFORMER.get(); + } + + /** + * Get the transformer with default configuration, but with automatic indentation + * and the XML declaration removed. + * + * @return A transformer with human-friendly configuration. + */ + public static Transformer getPrettyTransformer() { + return PRETTY_TRANSFORMER.get(); + } +} -- 2.36.6