BUG-472: Add EXI codecs without wiring them 94/5494/6
authorRobert Varga <rovarga@cisco.com>
Sat, 8 Feb 2014 03:06:48 +0000 (04:06 +0100)
committerRobert Varga <rovarga@cisco.com>
Wed, 16 Apr 2014 10:05:19 +0000 (12:05 +0200)
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>
opendaylight/commons/opendaylight/pom.xml
opendaylight/distribution/opendaylight/pom.xml
opendaylight/netconf/netconf-util/pom.xml
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfEXICodec.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfEXIToMessageDecoder.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/handler/NetconfMessageToEXIEncoder.java [new file with mode: 0644]
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/EXIParameters.java [new file with mode: 0644]

index 140a6ddfe128785edde35853eadac67ae698bcd0..fef04bb60346156702511111af8b19f5ac909e5b 100644 (file)
     <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>
index 776fb7a0f6816b7de10b41b087739a71f482bbd9..291fbdd4a783a7772e91bca874e3534a9df434fb 100644 (file)
                     <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>
index b75d3382b169b382c0b63039a4f7c7488ed2582d..e6cd3daad2bf0192ad01076349d92062ccb574b5 100644 (file)
             <groupId>xmlunit</groupId>
             <artifactId>xmlunit</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller.thirdparty</groupId>
+            <artifactId>nagasena</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
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 (file)
index 0000000..c28db18
--- /dev/null
@@ -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 (file)
index 0000000..e4c14ab
--- /dev/null
@@ -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<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()));
+    }
+}
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 (file)
index 0000000..70d9c2b
--- /dev/null
@@ -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<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()));
+        }
+    }
+}
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 (file)
index 0000000..2c8a16c
--- /dev/null
@@ -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;
+    }
+}