From 7451e256fd9a658db243666bc7e734204519ceee Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Sat, 8 Feb 2014 04:06:48 +0100 Subject: [PATCH] BUG-472: Add EXI codecs without wiring them This patch implements the EXI codecs to be used in the NETCONF pipeline. No wiring into the protocol stack is done in this patch. Change-Id: Ic708ef26d92e138f8269a49a2759f01d88085bf1 Signed-off-by: Robert Varga --- opendaylight/commons/opendaylight/pom.xml | 13 +++ .../distribution/opendaylight/pom.xml | 8 ++ opendaylight/netconf/netconf-util/pom.xml | 4 + .../netconf/util/handler/NetconfEXICodec.java | 63 +++++++++++ .../handler/NetconfEXIToMessageDecoder.java | 65 +++++++++++ .../handler/NetconfMessageToEXIEncoder.java | 46 ++++++++ .../netconf/util/xml/EXIParameters.java | 103 ++++++++++++++++++ 7 files changed, 302 insertions(+) create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfEXICodec.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfEXIToMessageDecoder.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfMessageToEXIEncoder.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/EXIParameters.java diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 140a6ddfe1..fef04bb603 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -118,6 +118,9 @@ src/main/xtend-gen src/main/yang-gen-config src/main/yang-gen-sal + + + 0000.0002.0035.0 @@ -657,6 +660,16 @@ ganymed 1.1-SNAPSHOT + + org.opendaylight.controller.thirdparty + nagasena + ${exi.nagasena.version} + + + org.opendaylight.controller.thirdparty + nagasena-rta + ${exi.nagasena.version} + com.google.code.findbugs jsr305 diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 776fb7a0f6..291fbdd4a7 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -419,6 +419,14 @@ org.opendaylight.controller.thirdparty ganymed + + org.opendaylight.controller.thirdparty + nagasena + + + org.opendaylight.controller.thirdparty + nagasena-rta + org.zeromq jeromq diff --git a/opendaylight/netconf/netconf-util/pom.xml b/opendaylight/netconf/netconf-util/pom.xml index b75d3382b1..e6cd3daad2 100644 --- a/opendaylight/netconf/netconf-util/pom.xml +++ b/opendaylight/netconf/netconf-util/pom.xml @@ -54,6 +54,10 @@ xmlunit xmlunit + + org.opendaylight.controller.thirdparty + nagasena + diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfEXICodec.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfEXICodec.java new file mode 100644 index 0000000000..c28db18901 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfEXICodec.java @@ -0,0 +1,63 @@ +package org.opendaylight.controller.netconf.util.handler; + +import org.openexi.proc.HeaderOptionsOutputType; +import org.openexi.proc.common.AlignmentType; +import org.openexi.proc.common.EXIOptions; +import org.openexi.proc.common.EXIOptionsException; +import org.openexi.proc.common.GrammarOptions; +import org.openexi.proc.grammars.GrammarCache; +import org.openexi.sax.EXIReader; +import org.openexi.sax.Transmogrifier; + +import com.google.common.base.Preconditions; + +final class NetconfEXICodec { + /** + * NETCONF is XML environment, so the use of EXI cookie is not really needed. Adding it + * decreases efficiency of encoding by adding human-readable 4 bytes "EXI$" to the head + * of the stream. This is really useful, so let's output it now. + */ + private static final boolean OUTPUT_EXI_COOKIE = true; + private final AlignmentType alignmentType; + private final EXIOptions exiOptions; + + public NetconfEXICodec(final AlignmentType alignmentType, final EXIOptions exiOptions) { + this.alignmentType = Preconditions.checkNotNull(alignmentType); + this.exiOptions = Preconditions.checkNotNull(exiOptions); + } + + private GrammarCache getGrammarCache() { + short go = GrammarOptions.DEFAULT_OPTIONS; + if (exiOptions.getPreserveComments()) { + go = GrammarOptions.addCM(go); + } + if (exiOptions.getPreserveDTD()) { + go = GrammarOptions.addDTD(go); + } + if (exiOptions.getPreserveNS()) { + go = GrammarOptions.addNS(go); + } + if (exiOptions.getPreservePIs()) { + go = GrammarOptions.addPI(go); + } + + return new GrammarCache(null, go); + } + + EXIReader getReader() throws EXIOptionsException { + final EXIReader r = new EXIReader(); + r.setPreserveLexicalValues(exiOptions.getPreserveLexicalValues()); + r.setGrammarCache(getGrammarCache()); + return r; + } + + Transmogrifier getTransmogrifier() throws EXIOptionsException { + final Transmogrifier transmogrifier = new Transmogrifier(); + transmogrifier.setAlignmentType(alignmentType); + transmogrifier.setBlockSize(exiOptions.getBlockSize()); + transmogrifier.setGrammarCache(getGrammarCache()); + transmogrifier.setOutputCookie(OUTPUT_EXI_COOKIE); + transmogrifier.setOutputOptions(HeaderOptionsOutputType.all); + return transmogrifier; + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfEXIToMessageDecoder.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfEXIToMessageDecoder.java new file mode 100644 index 0000000000..e4c14abeaa --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfEXIToMessageDecoder.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.netconf.util.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; + +import java.io.InputStream; +import java.util.List; + +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; + +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.openexi.sax.EXIReader; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +import com.google.common.base.Preconditions; + +public final class NetconfEXIToMessageDecoder extends ByteToMessageDecoder { + private static final SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); + + // FIXME: is this needed? + // private static final SAXParserFactory saxParserFactory; + // static { + // saxParserFactory = SAXParserFactory.newInstance(); + // saxParserFactory.setNamespaceAware(true); + // } + + private final NetconfEXICodec codec; + + public NetconfEXIToMessageDecoder(final NetconfEXICodec codec) { + this.codec = Preconditions.checkNotNull(codec); + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + /* + * Note that we could loop here and process all the messages, but we can't do that. + * The reason is operation, which has the contract of immediately stopping + * the use of EXI, which means the next message needs to be decoded not by us, but rather + * by the XML decoder. + */ + final DOMResult result = new DOMResult(); + final EXIReader r = codec.getReader(); + + final TransformerHandler transformerHandler = saxTransformerFactory.newTransformerHandler(); + transformerHandler.setResult(result); + r.setContentHandler(transformerHandler); + + try (final InputStream is = new ByteBufInputStream(in)) { + r.parse(new InputSource(is)); + } + out.add(new NetconfMessage((Document) result.getNode())); + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfMessageToEXIEncoder.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfMessageToEXIEncoder.java new file mode 100644 index 0000000000..70d9c2bf3a --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfMessageToEXIEncoder.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.netconf.util.handler; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufOutputStream; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +import java.io.OutputStream; + +import javax.xml.transform.Transformer; +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.sax.Transmogrifier; + +import com.google.common.base.Preconditions; + +public final class NetconfMessageToEXIEncoder extends MessageToByteEncoder { + private static final SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); + + private final NetconfEXICodec codec; + + public NetconfMessageToEXIEncoder(final NetconfEXICodec codec) { + this.codec = Preconditions.checkNotNull(codec); + } + + @Override + protected void encode(ChannelHandlerContext ctx, NetconfMessage msg, ByteBuf out) throws Exception { + try (final OutputStream os = new ByteBufOutputStream(out)) { + final Transmogrifier transmogrifier = codec.getTransmogrifier(); + transmogrifier.setOutputStream(os); + + final Transformer transformer = saxTransformerFactory.newTransformer(); + transformer.transform(new DOMSource(msg.getDocument()), new SAXResult(transmogrifier.getSAXTransmogrifier())); + } + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/EXIParameters.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/EXIParameters.java new file mode 100644 index 0000000000..2c8a16cbe1 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/EXIParameters.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.netconf.util.xml; + +import org.openexi.proc.common.AlignmentType; +import org.openexi.proc.common.EXIOptions; +import org.openexi.proc.common.EXIOptionsException; + +import com.google.common.base.Preconditions; + +public final class EXIParameters { + private static final String EXI_PARAMETER_ALIGNMENT = "alignment"; + private static final String EXI_PARAMETER_BYTE_ALIGNED = "byte-aligned"; + private static final String EXI_PARAMETER_BIT_PACKED = "bit-packed"; + private static final String EXI_PARAMETER_COMPRESSED = "compressed"; + private static final String EXI_PARAMETER_PRE_COMPRESSION = "pre-compression"; + + private static final String EXI_PARAMETER_FIDELITY = "fidelity"; + private static final String EXI_FIDELITY_DTD = "dtd"; + private static final String EXI_FIDELITY_LEXICAL_VALUES = "lexical-values"; + private static final String EXI_FIDELITY_COMMENTS = "comments"; + private static final String EXI_FIDELITY_PIS = "pis"; + private static final String EXI_FIDELITY_PREFIXES = "prefixes"; + + private static final String EXI_PARAMETER_SCHEMA = "schema"; + private static final String EXI_PARAMETER_SCHEMA_NONE = "none"; + private static final String EXI_PARAMETER_SCHEMA_BUILT_IN = "builtin"; + private static final String EXI_PARAMETER_SCHEMA_BASE_1_1 = "base:1.1"; + + private final EXIOptions options; + + private EXIParameters(final EXIOptions options) { + this.options = Preconditions.checkNotNull(options); + } + + public static EXIParameters forXmlElement(final XmlElement root) throws EXIOptionsException { + final EXIOptions options = new EXIOptions(); + + options.setAlignmentType(AlignmentType.bitPacked); + if (root.getElementsByTagName(EXI_PARAMETER_ALIGNMENT).getLength() > 0) { + if (root.getElementsByTagName(EXI_PARAMETER_BIT_PACKED).getLength() > 0) { + options.setAlignmentType(AlignmentType.bitPacked); + } else if (root.getElementsByTagName(EXI_PARAMETER_BYTE_ALIGNED).getLength() > 0) { + options.setAlignmentType(AlignmentType.byteAligned); + } else if (root.getElementsByTagName(EXI_PARAMETER_COMPRESSED).getLength() > 0) { + options.setAlignmentType(AlignmentType.compress); + } else if (root.getElementsByTagName(EXI_PARAMETER_PRE_COMPRESSION).getLength() > 0) { + options.setAlignmentType(AlignmentType.preCompress); + } + } + + if (root.getElementsByTagName(EXI_PARAMETER_FIDELITY).getLength() > 0) { + if (root.getElementsByTagName(EXI_FIDELITY_DTD).getLength() > 0) { + options.setPreserveDTD(true); + } + if (root.getElementsByTagName(EXI_FIDELITY_LEXICAL_VALUES).getLength() > 0) { + options.setPreserveLexicalValues(true); + } + if (root.getElementsByTagName(EXI_FIDELITY_COMMENTS).getLength() > 0) { + options.setPreserveComments(true); + } + if (root.getElementsByTagName(EXI_FIDELITY_PIS).getLength() > 0) { + options.setPreservePIs(true); + } + if (root.getElementsByTagName(EXI_FIDELITY_PREFIXES).getLength() > 0) { + options.setPreserveNS(true); + } + } + + if (root.getElementsByTagName(EXI_PARAMETER_SCHEMA).getLength() > 0) { + + // GrammarFactory grammarFactory = GrammarFactory.newInstance(); + // if (operationElement + // .getElementsByTagName(EXI_PARAMETER_SCHEMA_NONE) + // .getLength() > 0) { + // this.grammars = grammarFactory.createSchemaLessGrammars(); + // } + // + // if (operationElement.getElementsByTagName( + // EXI_PARAMETER_SCHEMA_BUILT_IN).getLength() > 0) { + // this.grammars = grammarFactory.createXSDTypesOnlyGrammars(); + // } + // + // if (operationElement.getElementsByTagName( + // EXI_PARAMETER_SCHEMA_BASE_1_1).getLength() > 0) { + // this.grammars = grammarFactory + // .createGrammars(NETCONF_XSD_LOCATION); + // } + + } + + return new EXIParameters(options); + } + + public final EXIOptions getOptions() { + return options; + } +} -- 2.36.6