Initialize EXISchema grammars lazily 47/80047/7
authorJakub Morvay <jmorvay@frinx.io>
Wed, 30 Jan 2019 19:19:54 +0000 (20:19 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 30 Jan 2019 22:39:09 +0000 (23:39 +0100)
Creating schema-backed netconf grammar is costly operation. Do not
create it until we actually need it.

This way we do not introduce any performance regression for netconf
sessions that do not want to use schema-informed grammars for EXI
encoding.

Also do not force grammar instantiation when a mismatching option
is specified by the peer.

JIRA: NETCONF-584
Change-Id: I83f5e18f423cd21c1dc993980f42e030f36a12ed
Signed-off-by: Jakub Morvay <jmorvay@frinx.io>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/exi/EXIParameters.java
netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/exi/EXISchema.java

index 837beeeab25039fa694be905a8f98a2a9598ec89..d15f441c6960e92f9c8888591e9c62e091366fdb 100644 (file)
@@ -20,7 +20,6 @@ import org.opendaylight.netconf.shaded.exificient.core.FidelityOptions;
 import org.opendaylight.netconf.shaded.exificient.core.SchemaIdResolver;
 import org.opendaylight.netconf.shaded.exificient.core.exceptions.EXIException;
 import org.opendaylight.netconf.shaded.exificient.core.exceptions.UnsupportedOption;
-import org.opendaylight.netconf.shaded.exificient.core.grammars.Grammars;
 import org.opendaylight.netconf.shaded.exificient.core.helpers.DefaultEXIFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,10 +51,8 @@ public final class EXIParameters {
         if (schemaId.isEmpty()) {
             return EXISchema.BUILTIN.getGrammar();
         }
-
-        final Grammars g = EXISchema.BASE_1_1.getGrammar();
-        if (g.getSchemaId().equals(schemaId)) {
-            return g;
+        if (schemaId.equals(EXISchema.BASE_1_1.getOption())) {
+            return EXISchema.BASE_1_1.getGrammar();
         }
 
         throw new EXIException("Cannot resolve schema " + schemaId);
index 52f93751733b8a7d9c586d443a48f25ba5949967..72c9f1779f14395bb07a4e40eed31bf26c37f917 100644 (file)
@@ -9,10 +9,13 @@ package org.opendaylight.netconf.nettyutil.handler.exi;
 
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.base.Suppliers;
 import com.google.common.io.ByteSource;
 import com.google.common.io.Resources;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.function.Supplier;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.netconf.shaded.exificient.core.exceptions.EXIException;
 import org.opendaylight.netconf.shaded.exificient.core.grammars.Grammars;
 import org.opendaylight.netconf.shaded.exificient.grammars.GrammarFactory;
@@ -21,52 +24,72 @@ import org.opendaylight.netconf.shaded.exificient.grammars.GrammarFactory;
  * Enumeration of schema modes defined by the NETCONF EXI capability.
  */
 public enum EXISchema {
-    NONE("none", GrammarFactory.newInstance().createSchemaLessGrammars()),
-    BUILTIN("builtin", createBuiltinGrammar()),
-    BASE_1_1("base:1.1", createNetconfGrammar());
+    NONE("none") {
+        @Override
+        Grammars createGrammar() {
+            return GrammarFactory.newInstance().createSchemaLessGrammars();
+        }
+    },
+    BUILTIN("builtin") {
+        @Override
+        Grammars createGrammar() {
+            try {
+                return GrammarFactory.newInstance().createXSDTypesOnlyGrammars();
+            } catch (EXIException e) {
+                throw new IllegalStateException("Failed to create builtin grammar", e);
+            }
+        }
+    },
+    BASE_1_1("base:1.1") {
+        @Override
+        Grammars createGrammar() {
+            final ByteSource source = Resources.asByteSource(EXISchema.class.getResource("/rfc6241.xsd"));
+            try (InputStream is = source.openStream()) {
+                final Grammars g = GrammarFactory.newInstance().createGrammars(is);
+                g.setSchemaId(getOption());
+                return g;
+            } catch (EXIException | IOException e) {
+                throw new IllegalStateException("Failed to create RFC6241 grammar", e);
+            }
+        }
+    };
 
     private String option;
-    private Grammars grammar;
+    private Supplier<Grammars> grammarsSupplier;
 
-    EXISchema(final String option, final Grammars grammar) {
+    EXISchema(final String option) {
         this.option = requireNonNull(option);
-        this.grammar = requireNonNull(grammar);
+        // Grammar instantiation can be CPU-intensive, hence we instantiate it lazily through a memoizing supplier
+        this.grammarsSupplier = Suppliers.memoize(this::createGrammar);
     }
 
     final String getOption() {
         return option;
     }
 
+    /**
+     * Return the grammar associated with this EXISchema.
+     *
+     * @return An EXI grammar.
+     */
     final Grammars getGrammar() {
-        return grammar;
+        return grammarsSupplier.get();
     }
 
-    static EXISchema forOption(final String id) {
+    /**
+     * Create grammars associated with this EXISchema. This is a potentially expensive operation for internal use only,
+     * use {@link #getGrammar()} instead.
+     *
+     * @return An EXI grammar.
+     */
+    abstract Grammars createGrammar();
+
+    static @Nullable EXISchema forOption(final String id) {
         for (EXISchema s : EXISchema.values()) {
             if (id.equals(s.getOption())) {
                 return s;
             }
         }
-
         return null;
     }
-
-    private static Grammars createNetconfGrammar() {
-        final ByteSource source = Resources.asByteSource(EXISchema.class.getResource("/rfc6241.xsd"));
-        try (InputStream is = source.openStream()) {
-            final Grammars g = GrammarFactory.newInstance().createGrammars(is);
-            g.setSchemaId("base:1.1");
-            return g;
-        } catch (EXIException | IOException e) {
-            throw new IllegalStateException("Failed to create RFC6241 grammar", e);
-        }
-    }
-
-    private static Grammars createBuiltinGrammar() {
-        try {
-            return GrammarFactory.newInstance().createXSDTypesOnlyGrammars();
-        } catch (EXIException e) {
-            throw new IllegalStateException("Failed to create builtin grammar", e);
-        }
-    }
 }