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 <rovarga@cisco.com>
<xtend.dstdir>src/main/xtend-gen</xtend.dstdir>
<jmxGeneratorPath>src/main/yang-gen-config</jmxGeneratorPath>
<salGeneratorPath>src/main/yang-gen-sal</salGeneratorPath>
+
+ <!-- OpenEXI third party lib for netconf-->
+ <exi.nagasena.version>0000.0002.0035.0</exi.nagasena.version>
</properties>
<dependencyManagement>
<artifactId>ganymed</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>nagasena</artifactId>
+ <version>${exi.nagasena.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>nagasena-rta</artifactId>
+ <version>${exi.nagasena.version}</version>
+ </dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>ganymed</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>nagasena</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>nagasena-rta</artifactId>
+ </dependency>
<dependency>
<groupId>org.zeromq</groupId>
<artifactId>jeromq</artifactId>
<groupId>xmlunit</groupId>
<artifactId>xmlunit</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.thirdparty</groupId>
+ <artifactId>nagasena</artifactId>
+ </dependency>
</dependencies>
<build>
--- /dev/null
+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;
+ }
+}
--- /dev/null
+/*
+ * 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<Object> out) throws Exception {
+ /*
+ * Note that we could loop here and process all the messages, but we can't do that.
+ * The reason is <stop-exi> 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()));
+ }
+}
--- /dev/null
+/*
+ * 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<NetconfMessage> {
+ 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()));
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}