Make DatabindContext codecs lazily-loaded 85/107885/2
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 15 Sep 2023 14:29:33 +0000 (16:29 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 15 Sep 2023 18:50:41 +0000 (20:50 +0200)
We may use temporary DatabindContexts and also we do not necessarily
want to instantiate both XML and JSON codecs for each. Make codec
loading lazy instead, allocated on the first request.

JIRA: NETCONF-773
Change-Id: Ie739629c23457937e73cc2f703b5d3a91989867d
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/DatabindContext.java

index 8faf5f09d7f7f9a150ee8341b2a12bc48807b890..9acf3ed3aa57d4afa47396f9f91665115f2a513e 100644 (file)
@@ -9,6 +9,8 @@ package org.opendaylight.restconf.nb.rfc8040.databind;
 
 import static java.util.Objects.requireNonNull;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.data.api.schema.MountPointContext;
 import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
@@ -19,14 +21,29 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 /**
  * An immutable context holding a consistent view of things related to data bind operations.
  */
-public record DatabindContext(
-        @NonNull MountPointContext mountContext,
-        @NonNull JSONCodecFactory jsonCodecs,
-        @NonNull XmlCodecFactory xmlCodecs) {
-    public DatabindContext {
-        requireNonNull(mountContext);
-        requireNonNull(jsonCodecs);
-        requireNonNull(xmlCodecs);
+public final class DatabindContext {
+    private static final VarHandle JSON_CODECS;
+    private static final VarHandle XML_CODECS;
+
+    static {
+        final var lookup = MethodHandles.lookup();
+        try {
+            JSON_CODECS = lookup.findVarHandle(DatabindContext.class, "jsonCodecs", JSONCodecFactory.class);
+            XML_CODECS = lookup.findVarHandle(DatabindContext.class, "xmlCodecs", XmlCodecFactory.class);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    private final @NonNull MountPointContext mountContext;
+
+    @SuppressWarnings("unused")
+    private volatile JSONCodecFactory jsonCodecs;
+    @SuppressWarnings("unused")
+    private volatile XmlCodecFactory xmlCodecs;
+
+    private DatabindContext(final @NonNull MountPointContext mountContext) {
+        this.mountContext = requireNonNull(mountContext);
     }
 
     public static @NonNull DatabindContext ofModel(final EffectiveModelContext modelContext) {
@@ -34,12 +51,32 @@ public record DatabindContext(
     }
 
     public static @NonNull DatabindContext ofMountPoint(final MountPointContext mountContext) {
-        return new DatabindContext(mountContext,
-            JSONCodecFactorySupplier.RFC7951.getShared(mountContext.getEffectiveModelContext()),
-            XmlCodecFactory.create(mountContext));
+        return new DatabindContext(mountContext);
     }
 
     public @NonNull EffectiveModelContext modelContext() {
         return mountContext.getEffectiveModelContext();
     }
+
+    public @NonNull JSONCodecFactory jsonCodecs() {
+        final var existing = (JSONCodecFactory) JSON_CODECS.getAcquire(this);
+        return existing != null ? existing : createJsonCodecs();
+    }
+
+    private @NonNull JSONCodecFactory createJsonCodecs() {
+        final var created = JSONCodecFactorySupplier.RFC7951.getShared(mountContext.getEffectiveModelContext());
+        final var witness = (JSONCodecFactory) JSON_CODECS.compareAndExchangeRelease(this, null, created);
+        return witness != null ? witness : created;
+    }
+
+    public @NonNull XmlCodecFactory xmlCodecs() {
+        final var existing = (XmlCodecFactory) XML_CODECS.getAcquire(this);
+        return existing != null ? existing : createXmlCodecs();
+    }
+
+    private @NonNull XmlCodecFactory createXmlCodecs() {
+        final var created = XmlCodecFactory.create(mountContext);
+        final var witness = (XmlCodecFactory) XML_CODECS.compareAndExchangeRelease(this, null, created);
+        return witness != null ? witness : created;
+    }
 }