Rework DataObjectSerializer implementations
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 20 Apr 2019 12:03:42 +0000 (14:03 +0200)
committerAnil Belur <abelur@linuxfoundation.org>
Wed, 19 Jun 2024 00:41:27 +0000 (10:41 +1000)
This patch rework generation of DataObjectSerializers to base
them on a common superclass and generates them into a target
CodecClassLoader.

This removes pollution of class loaders, while maintaining the
same level of functionality with slightly less byte code.

It also cleans up external interfaces, as the serializers
generation is a purely-internal implementation detail and is not
exposed to outside world -- hence this patch deprecates all
classes and interfaces involved in that former leak.

JIRA: MDSAL-401
Change-Id: I80daff7e05e7487af7dca4e4bdae6b2893c97037
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
19 files changed:
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/gen/impl/AbstractGenerator.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/gen/impl/AbstractStreamWriterGenerator.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/gen/impl/AugmentableDataNodeContainerEmitterSource.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/gen/impl/DataNodeContainerSerializerSource.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/gen/impl/DataObjectSerializerGenerator.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/gen/impl/DataObjectSerializerPrototype.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/gen/impl/DataObjectSerializerSource.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/gen/impl/StreamWriterGenerator.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/gen/spi/AbstractSource.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/gen/spi/StaticConstantDefinition.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingNormalizedNodeCodecRegistry.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamer.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerBridge.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerCustomizer.java [new file with mode: 0644]
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NodeCodecContext.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/AbstractBindingCodecTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/AugmentationClassDiscoveredAfterCodecTest.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/ExceptionReportingTest.java

index f6f37d5518aa6ed5d09a403d808e5230f0db620e..cc38139c61027af255c55fd3f0841c78855e2070 100644 (file)
@@ -9,7 +9,10 @@ package org.opendaylight.mdsal.binding.dom.codec.gen.impl;
 
 /**
  * Package-private base class for sharing the loading capability.
+ *
+ * @deprecated This class is superseded by an internal implementation.
  */
+@Deprecated
 abstract class AbstractGenerator {
     /**
      * Ensure that the serializer class for specified class is loaded and return its name.
index 847260010d7e7dcf8cf76d3e6bd01e9a97da6745..d1c501f5a8ece023b3e122b9ebec7508c097c0b8 100644 (file)
@@ -46,6 +46,7 @@ import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Deprecated
 abstract class AbstractStreamWriterGenerator extends AbstractGenerator implements DataObjectSerializerGenerator {
     private static final Logger LOG = LoggerFactory.getLogger(AbstractStreamWriterGenerator.class);
 
index 03bf419f0e6742a139811506baa7c1041bb2eff5..5e3bdd4fb4ee34d162103c7e9c61db029caf0c94 100644 (file)
@@ -11,6 +11,7 @@ import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.yangtools.yang.binding.DataObjectSerializerImplementation;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 
+@Deprecated
 abstract class AugmentableDataNodeContainerEmitterSource extends DataNodeContainerSerializerSource {
     private static final String AUGMENTABLE_SERIALIZER = "AUGMENTABLE_SERIALIZER";
 
index 375fe00bd709cba2540843e09dc60c808f727799..e4261182e9fe0083aeb4db06b9eab286163bc5a3 100644 (file)
@@ -34,6 +34,7 @@ import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Deprecated
 abstract class DataNodeContainerSerializerSource extends DataObjectSerializerSource {
 
     private static final Logger LOG = LoggerFactory.getLogger(DataNodeContainerSerializerSource.class);
index bbace44e966f6b687f6320785d0ec04bf19dc1e7..6cba594d266d8ea3ade5baf39b80ff6192c65d5e 100644 (file)
@@ -12,7 +12,10 @@ import org.opendaylight.yangtools.yang.binding.DataObjectSerializerImplementatio
 
 /**
  * Public interface exposed from generator implementation.
+ *
+ * @deprecated This interface is superseded by an internal implementation.
  */
+@Deprecated
 public interface DataObjectSerializerGenerator {
     /**
      * Get a serializer for a particular type.
index a3cd64164481a0496af9a7817dbdea71fa60d49f..6b5149588935667fe78c539b55db898e0da6ccef 100644 (file)
@@ -18,6 +18,7 @@ import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
  * basis. During that time, the {@link #serialize(DataObjectSerializerRegistry, DataObject, BindingStreamEventWriter)}
  * method will be replaced by the real implementation.
  */
+@Deprecated
 final class DataObjectSerializerPrototype implements DataObjectSerializerImplementation {
     private static final DataObjectSerializerPrototype INSTANCE = new DataObjectSerializerPrototype();
 
index 25ba79b376681fbab58e3b8dd873ae353f7b6e28..818dfee89ebdbeb34788a1762b54069c9e17f613 100644 (file)
@@ -18,6 +18,7 @@ import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
 
+@Deprecated
 abstract class DataObjectSerializerSource extends AbstractSource {
 
     private static final ClassLoadingStrategy STRATEGY = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy();
index 779edc9b2a5649c861f90a377c543d778ba4d0d6..99022f36acc0fbabcfcbcbdaaa91bd1b7a612cb2 100644 (file)
@@ -30,7 +30,10 @@ import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
  * and Augmentable codecs are static properties of parent codec and stateless implementations are used
  * ({@link ChoiceDispatchSerializer}, {@link AugmentableDispatchSerializer}), which uses registry to dispatch to
  * concrete item codec.
+ *
+ * @deprecated This class is superseded by an internal implementation.
  */
+@Deprecated
 public final class StreamWriterGenerator extends AbstractStreamWriterGenerator {
     private static final String UNKNOWN_SIZE = BindingStreamEventWriter.class.getName() + ".UNKNOWN_SIZE";
 
index 64c6c500a401cb8c913ffb06ff632cf6fce5569b..75f44710153b7562568d7010206a9eb5360930e0 100644 (file)
@@ -15,7 +15,12 @@ import java.util.Iterator;
 import java.util.Set;
 import org.opendaylight.mdsal.binding.model.api.Type;
 
-// FIXME: 5.0.0: consider optimizing streaming use through returning StringBuilder from common methods
+/**
+ * An abstract source.
+ *
+ * @deprecated This class is superseded by an internal implementation.
+ */
+@Deprecated
 public abstract class AbstractSource {
     private final Set<StaticConstantDefinition> staticConstants = new HashSet<>();
 
index 743b852535b671ed409ff69f7ae40d146af12d94..b231a523dbdc6d21550d20db4fa13ad5093bf998 100644 (file)
@@ -19,7 +19,10 @@ import static java.util.Objects.requireNonNull;
  * <li>type - Java type for property</li>
  * <li>value - value to which property should be initialized</li>
  * </ul>
+ *
+ * @deprecated This class is superseded by an internal implementation.
  */
+@Deprecated
 public class StaticConstantDefinition {
 
     private final String name;
index 1078bda96496e8ac0d1f959c4d5bf2ebc76b0f1d..2c12aaa26cb2e207605d0aa19fba46f4aacb3061 100644 (file)
@@ -12,7 +12,12 @@ import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Verify.verify;
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -25,6 +30,9 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import javassist.CannotCompileException;
+import javassist.NotFoundException;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
@@ -38,6 +46,7 @@ import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.concepts.Delegator;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.binding.Action;
@@ -45,6 +54,7 @@ import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.DataObjectSerializer;
+import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
 import org.opendaylight.yangtools.yang.binding.Identifiable;
 import org.opendaylight.yangtools.yang.binding.Identifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -70,9 +80,48 @@ import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-final class BindingCodecContext implements CodecContextFactory, BindingCodecTree, Immutable {
+final class BindingCodecContext implements CodecContextFactory, BindingCodecTree, DataObjectSerializerRegistry,
+        Immutable {
+    private final class DataObjectSerializerProxy implements DataObjectSerializer, Delegator<DataObjectStreamer<?>> {
+        private final @NonNull DataObjectStreamer<?> delegate;
+
+        DataObjectSerializerProxy(final DataObjectStreamer<?> delegate) {
+            this.delegate = requireNonNull(delegate);
+        }
+
+        @Override
+        public DataObjectStreamer<?> getDelegate() {
+            return delegate;
+        }
+
+        @Override
+        public void serialize(final DataObject obj, final BindingStreamEventWriter stream) throws IOException {
+            delegate.serialize(BindingCodecContext.this, obj, stream);
+        }
+    }
+
     private static final Logger LOG = LoggerFactory.getLogger(BindingCodecContext.class);
 
+    private final LoadingCache<Class<?>, DataObjectStreamer<?>> streamers = CacheBuilder.newBuilder().build(
+        new CacheLoader<Class<?>, DataObjectStreamer<?>>() {
+            @Override
+            public DataObjectStreamer<?> load(final Class<?> key) throws CannotCompileException, IOException,
+                    NotFoundException, ReflectiveOperationException {
+                final Class<?> streamer = loader.generateSubclass(DataObjectStreamerCustomizer.CT_DOS, key, "streamer",
+                    DataObjectStreamerCustomizer.create(BindingCodecContext.this, key));
+
+                final Field instance = streamer.getDeclaredField(DataObjectStreamerCustomizer.INSTANCE_FIELD);
+                return (DataObjectStreamer<?>) instance.get(null);
+            }
+        });
+    private final LoadingCache<Class<?>, DataObjectSerializer> serializers = CacheBuilder.newBuilder().build(
+        new CacheLoader<Class<?>, DataObjectSerializer>() {
+            @Override
+            public DataObjectSerializer load(final Class<?> key) throws ExecutionException {
+                return new DataObjectSerializerProxy(streamers.get(key));
+            }
+        });
+
     private final @NonNull CodecClassLoader loader = StaticClassPool.createLoader();
     private final InstanceIdentifierCodec instanceIdentifierCodec;
     private final IdentityCodec identityCodec;
@@ -112,6 +161,16 @@ final class BindingCodecContext implements CodecContextFactory, BindingCodecTree
         return registry.getSerializer((Class) type);
     }
 
+    @Override
+    public DataObjectStreamer<?> getDataObjectSerializer(final Class<?> type) {
+        return streamers.getUnchecked(type);
+    }
+
+    @Override
+    public DataObjectSerializer getSerializer(final Class<? extends DataObject> type) {
+        return serializers.getUnchecked(type);
+    }
+
     public Entry<YangInstanceIdentifier, BindingStreamEventWriter> newWriter(final InstanceIdentifier<?> path,
             final NormalizedNodeStreamWriter domWriter) {
         final List<YangInstanceIdentifier.PathArgument> yangArgs = new LinkedList<>();
index 3a9452922341d3a3ae832c9bedd921761c0d9cbd..6952efa8eea14bf613e68abfbaa984dd188198e0 100644 (file)
@@ -7,11 +7,9 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
+import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.io.IOException;
 import java.util.AbstractMap.SimpleEntry;
@@ -19,8 +17,10 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import java.util.function.BiFunction;
 import java.util.function.Function;
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeFactory;
@@ -33,13 +33,11 @@ import org.opendaylight.mdsal.binding.dom.codec.util.AbstractBindingLazyContaine
 import org.opendaylight.mdsal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
-import org.opendaylight.yangtools.concepts.Delegator;
 import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.DataObjectSerializer;
-import org.opendaylight.yangtools.yang.binding.DataObjectSerializerImplementation;
 import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
@@ -65,22 +63,33 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerRegistry,
-        BindingCodecTreeFactory, BindingNormalizedNodeWriterFactory,
-        BindingNormalizedNodeSerializer {
+        BindingCodecTreeFactory, BindingNormalizedNodeWriterFactory, BindingNormalizedNodeSerializer {
     private static final Logger LOG = LoggerFactory.getLogger(BindingNormalizedNodeCodecRegistry.class);
 
     private final DataObjectSerializerGenerator generator;
-    private final LoadingCache<Class<? extends DataObject>, DataObjectSerializer> serializers;
+
+    private static final AtomicReferenceFieldUpdater<BindingNormalizedNodeCodecRegistry, BindingCodecContext> UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(BindingNormalizedNodeCodecRegistry.class, BindingCodecContext.class,
+                "codecContext");
     private volatile BindingCodecContext codecContext;
 
+    public BindingNormalizedNodeCodecRegistry() {
+        this.generator = null;
+    }
+
+    public BindingNormalizedNodeCodecRegistry(final BindingRuntimeContext codecContext) {
+        this();
+        onBindingRuntimeContextUpdated(codecContext);
+    }
+
+    @Deprecated
     public BindingNormalizedNodeCodecRegistry(final DataObjectSerializerGenerator generator) {
         this.generator = requireNonNull(generator);
-        this.serializers = CacheBuilder.newBuilder().weakKeys().build(new GeneratorLoader());
     }
 
     @Override
     public DataObjectSerializer getSerializer(final Class<? extends DataObject> type) {
-        return serializers.getUnchecked(type);
+        return codecContext().getSerializer(type);
     }
 
     public BindingCodecTree getCodecContext() {
@@ -88,8 +97,28 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
     }
 
     public void onBindingRuntimeContextUpdated(final BindingRuntimeContext context) {
-        codecContext = new BindingCodecContext(context, this);
-        generator.onBindingRuntimeContextUpdated(context);
+        // BindingCodecContext is a costly resource. Let us not ditch it unless we have to
+        final BindingCodecContext current = codecContext;
+        if (current != null && context.equals(current.getRuntimeContext())) {
+            LOG.debug("Skipping update of runtime context {}", context);
+            return;
+        }
+
+        final BindingCodecContext updated = new BindingCodecContext(context, this);
+        if (UPDATER.compareAndSet(this, current, updated)) {
+            if (generator != null) {
+                generator.onBindingRuntimeContextUpdated(context);
+            }
+        } else {
+            LOG.warn("Concurrent update of runtime context (expected={} current={}) detected at ", current,
+                codecContext, new Throwable());
+        }
+    }
+
+    final @NonNull BindingCodecContext codecContext() {
+        final BindingCodecContext local = codecContext;
+        checkState(local != null, "No context available yet");
+        return local;
     }
 
     @Override
@@ -328,33 +357,6 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR
         }
     }
 
-    private final class GeneratorLoader extends CacheLoader<Class<? extends DataContainer>, DataObjectSerializer> {
-        @Override
-        public DataObjectSerializer load(final Class<? extends DataContainer> key) {
-            final DataObjectSerializerImplementation prototype = generator.getSerializer(key);
-            return new DataObjectSerializerProxy(prototype);
-        }
-    }
-
-    private final class DataObjectSerializerProxy
-            implements DataObjectSerializer, Delegator<DataObjectSerializerImplementation> {
-        private final DataObjectSerializerImplementation delegate;
-
-        DataObjectSerializerProxy(final DataObjectSerializerImplementation delegate) {
-            this.delegate = delegate;
-        }
-
-        @Override
-        public DataObjectSerializerImplementation getDelegate() {
-            return delegate;
-        }
-
-        @Override
-        public void serialize(final DataObject obj, final BindingStreamEventWriter stream) throws IOException {
-            delegate.serialize(BindingNormalizedNodeCodecRegistry.this, obj, stream);
-        }
-    }
-
     @NonNullByDefault
     private abstract static class AbstractLazyActionContainerNode<T extends DataObject>
             extends AbstractBindingLazyContainerNode<T, BindingNormalizedNodeCodecRegistry> {
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamer.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamer.java
new file mode 100644 (file)
index 0000000..f48ade1
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.codec.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.opendaylight.mdsal.binding.dom.codec.util.AugmentationReader;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.BindingSerializer;
+import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.DataObjectSerializer;
+import org.opendaylight.yangtools.yang.binding.DataObjectSerializerImplementation;
+import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Base superclass for all concrete streamers, that is objects which are able to turn a concrete DataObject into a
+ * stream of events.
+ *
+ * @param <T> DataObject type
+ */
+@Beta
+public abstract class DataObjectStreamer<T extends DataObject> implements DataObjectSerializerImplementation {
+    private static final Logger LOG = LoggerFactory.getLogger(DataObjectStreamer.class);
+
+    protected DataObjectStreamer() {
+
+    }
+
+    protected static final void streamAnyxml(final BindingStreamEventWriter writer, final String localName,
+            final Object value) throws IOException {
+        if (value != null) {
+            writer.anyxmlNode(localName, value);
+        }
+    }
+
+    protected static final void streamAugmentations(final DataObjectSerializerRegistry registry,
+            final BindingStreamEventWriter writer, final Augmentable<?> obj) throws IOException {
+        final Map<Class<? extends Augmentation<?>>, Augmentation<?>> augmentations;
+        if (registry instanceof AugmentationReader) {
+            augmentations = ((AugmentationReader) registry).getAugmentations(obj);
+        } else if (Proxy.isProxyClass(obj.getClass())) {
+            augmentations = getFromProxy(obj);
+        } else {
+            augmentations = BindingReflections.getAugmentations(obj);
+        }
+        for (final Entry<Class<? extends Augmentation<?>>, Augmentation<?>> aug : augmentations.entrySet()) {
+            emitAugmentation(aug.getKey(), aug.getValue(), writer, registry);
+        }
+    }
+
+    private static Map<Class<? extends Augmentation<?>>, Augmentation<?>> getFromProxy(final Augmentable<?> obj) {
+        final InvocationHandler proxy = Proxy.getInvocationHandler(obj);
+        if (proxy instanceof AugmentationReader) {
+            return ((AugmentationReader) proxy).getAugmentations(obj);
+        }
+        return ImmutableClassToInstanceMap.of();
+    }
+
+    protected static final <C extends DataContainer> void streamChoice(final Class<C> choiceClass,
+            final DataObjectSerializerRegistry registry, final BindingStreamEventWriter writer, final C value)
+                    throws IOException {
+        if (value != null) {
+            final Class<? extends DataContainer> caseClass = value.implementedInterface();
+            writer.startChoiceNode(choiceClass, BindingStreamEventWriter.UNKNOWN_SIZE);
+            final DataObjectSerializer caseStreamer = registry.getSerializer(caseClass.asSubclass(DataObject.class));
+            if (caseStreamer != null && tryCache(writer, (DataObject) value)) {
+                caseStreamer.serialize((DataObject) value, writer);
+            } else {
+                LOG.warn("No serializer for case {} is available in registry {}", caseClass, registry);
+            }
+
+            writer.endNode();
+        }
+    }
+
+    protected static final <C extends DataObject> void streamContainer(final DataObjectStreamer<C> childStreamer,
+            final DataObjectSerializerRegistry registry, final BindingStreamEventWriter writer, final C value)
+                    throws IOException {
+        if (value != null && tryCache(writer, value)) {
+            childStreamer.serialize(registry, value, writer);
+        }
+    }
+
+    protected static final void streamLeaf(final BindingStreamEventWriter writer, final String localName,
+            final Object value) throws IOException {
+        if (value != null) {
+            writer.leafNode(localName, value);
+        }
+    }
+
+    protected static final void streamLeafList(final BindingStreamEventWriter writer, final String localName,
+            final List<?> value) throws IOException {
+        if (value != null) {
+            writer.startLeafSet(localName, value.size());
+            commonStreamLeafset(writer, value);
+        }
+    }
+
+    protected static final void streamOrderedLeafList(final BindingStreamEventWriter writer,
+            final String localName, final List<?> value) throws IOException {
+        if (value != null) {
+            writer.startOrderedLeafSet(localName, value.size());
+            commonStreamLeafset(writer, value);
+        }
+    }
+
+    protected static final <E extends DataObject> void streamList(final Class<E> childClass,
+            final DataObjectStreamer<E> childStreamer, final DataObjectSerializerRegistry registry,
+            final BindingStreamEventWriter writer, final List<? extends E> value) throws IOException {
+        if (value != null) {
+            writer.startUnkeyedList(childClass, value.size());
+            commonStreamList(registry, writer, childStreamer, value);
+        }
+    }
+
+    protected static final <E extends DataObject & Identifiable<?>> void streamMap(final Class<E> childClass,
+            final DataObjectStreamer<E> childStreamer, final DataObjectSerializerRegistry registry,
+            final BindingStreamEventWriter writer, final List<? extends E> value) throws IOException {
+        if (value != null) {
+            writer.startMapNode(childClass, value.size());
+            commonStreamList(registry, writer, childStreamer, value);
+        }
+    }
+
+    protected static final <E extends DataObject & Identifiable<?>> void streamOrderedMap(final Class<E> childClass,
+            final DataObjectStreamer<E> childStreamer, final DataObjectSerializerRegistry registry,
+            final BindingStreamEventWriter writer, final List<? extends E> value) throws IOException {
+        if (value != null) {
+            writer.startOrderedMapNode(childClass, value.size());
+            commonStreamList(registry, writer, childStreamer, value);
+        }
+    }
+
+    private static <E extends DataObject> void commonStreamList(final DataObjectSerializerRegistry registry,
+            final BindingStreamEventWriter writer, final DataObjectStreamer<E> childStreamer,
+            final Collection<? extends E> value) throws IOException {
+
+        for (E entry : value) {
+            if (tryCache(writer, entry)) {
+                childStreamer.serialize(registry, entry, writer);
+            }
+        }
+        writer.endNode();
+    }
+
+
+    private static void commonStreamLeafset(final BindingStreamEventWriter writer, final List<?> value)
+            throws IOException {
+        for (Object entry : value) {
+            writer.leafSetEntryNode(entry);
+        }
+        writer.endNode();
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static void emitAugmentation(final Class type, final Augmentation<?> value,
+            final BindingStreamEventWriter writer, final DataObjectSerializerRegistry registry) throws IOException {
+        /*
+         * Binding Specification allowed to insert augmentation with null for
+         * value, which effectively could be used to remove augmentation
+         * from builder / DTO.
+         */
+        if (value != null) {
+            checkArgument(value instanceof DataObject);
+            @SuppressWarnings("unchecked")
+            final DataObjectSerializer serializer = registry.getSerializer(type);
+            if (serializer != null) {
+                serializer.serialize((DataObject) value, writer);
+            } else {
+                LOG.warn("DataObjectSerializer is not present for {} in registry {}", type, registry);
+            }
+        }
+    }
+
+    private static boolean tryCache(final BindingStreamEventWriter writer, final DataObject value) {
+        return writer instanceof BindingSerializer ? ((BindingSerializer) writer).serialize(value) == null : true;
+    }
+}
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerBridge.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerBridge.java
new file mode 100644 (file)
index 0000000..e75ee77
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.codec.impl;
+
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Bridge for initializing {@link DataObjectStreamer} instance constants during class loading time. This class is public
+ * only due to implementation restrictions and can change at any time.
+ */
+// FIXME: this bridge is only necessary to work around Javassist compiler resolution of dependencies. If we switch to
+//         a more bytecode-oriented framework, this bridge becomes superfluous.
+//
+@Beta
+public final class DataObjectStreamerBridge {
+    private static final ThreadLocal<DataObjectStreamerCustomizer> CURRENT_CUSTOMIZER = new ThreadLocal<>();
+
+    private DataObjectStreamerBridge() {
+
+    }
+
+    public static @NonNull DataObjectStreamer<?> resolve(final @NonNull String methodName) {
+        return verifyNotNull(CURRENT_CUSTOMIZER.get(), "No customizer attached").resolve(methodName);
+    }
+
+    static @Nullable DataObjectStreamerCustomizer setup(final @NonNull DataObjectStreamerCustomizer next) {
+        final DataObjectStreamerCustomizer prev = CURRENT_CUSTOMIZER.get();
+        CURRENT_CUSTOMIZER.set(verifyNotNull(next));
+        return prev;
+    }
+
+    static void tearDown(final @Nullable DataObjectStreamerCustomizer prev) {
+        if (prev == null) {
+            CURRENT_CUSTOMIZER.remove();
+        } else {
+            CURRENT_CUSTOMIZER.set(prev);
+        }
+    }
+}
diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerCustomizer.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerCustomizer.java
new file mode 100644 (file)
index 0000000..f958c15
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.mdsal.binding.dom.codec.impl;
+
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import javassist.CannotCompileException;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.Modifier;
+import javassist.NotFoundException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.Customizer;
+import org.opendaylight.mdsal.binding.dom.codec.loader.StaticClassPool;
+import org.opendaylight.mdsal.binding.dom.codec.util.BindingSchemaMapping;
+import org.opendaylight.mdsal.binding.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding.model.api.MethodSignature;
+import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
+import org.opendaylight.mdsal.binding.model.api.Type;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+public final class DataObjectStreamerCustomizer implements Customizer {
+    static final CtClass CT_DOS = StaticClassPool.findClass(DataObjectStreamer.class);
+    static final String INSTANCE_FIELD = "INSTANCE";
+
+    private static final Logger LOG = LoggerFactory.getLogger(DataObjectStreamerCustomizer.class);
+    private static final String UNKNOWN_SIZE = BindingStreamEventWriter.class.getName() + ".UNKNOWN_SIZE";
+
+    private static final CtClass CT_VOID = StaticClassPool.findClass(void.class);
+    private static final CtClass[] SERIALIZE_ARGS = new CtClass[] {
+        StaticClassPool.findClass(DataObjectSerializerRegistry.class),
+        StaticClassPool.findClass(DataObject.class),
+        StaticClassPool.findClass(BindingStreamEventWriter.class)
+    };
+
+    private final Map<String, Class<? extends DataObject>> constants = new HashMap<>();
+    private final ImmutableMap<String, Type> props;
+    private final CodecContextFactory registry;
+    private final DataNodeContainer schema;
+    private final String startEvent;
+    private final Class<?> type;
+
+    DataObjectStreamerCustomizer(final CodecContextFactory registry, final GeneratedType genType,
+            final DataNodeContainer schema, final Class<?> type, final String startEvent) {
+        this.registry = requireNonNull(registry);
+        this.schema = requireNonNull(schema);
+        this.type = requireNonNull(type);
+        this.startEvent = requireNonNull(startEvent);
+        props = collectAllProperties(genType);
+    }
+
+    public static DataObjectStreamerCustomizer create(final CodecContextFactory registry, final Class<?> type) {
+        final Entry<GeneratedType, WithStatus> typeAndSchema = registry.getRuntimeContext().getTypeWithSchema(type);
+        final WithStatus schema = typeAndSchema.getValue();
+
+        final String startEvent;
+        if (schema instanceof ContainerSchemaNode || schema instanceof NotificationDefinition) {
+            startEvent = "startContainerNode(" + type.getName() + ".class," + UNKNOWN_SIZE;
+        } else if (schema instanceof ListSchemaNode) {
+            final ListSchemaNode casted = (ListSchemaNode) schema;
+            if (!casted.getKeyDefinition().isEmpty()) {
+                startEvent = "startMapEntryNode(obj." + BindingMapping.IDENTIFIABLE_KEY_NAME + "(), " + UNKNOWN_SIZE;
+            } else {
+                startEvent = "startUnkeyedListItem(" + UNKNOWN_SIZE;
+            }
+        } else if (schema instanceof AugmentationSchemaNode) {
+            startEvent = "startAugmentationNode(" + type.getName() + ".class";
+        } else if (schema instanceof CaseSchemaNode) {
+            startEvent = "startCase(" + type.getName() + ".class, " + UNKNOWN_SIZE;
+        } else {
+            throw new UnsupportedOperationException("Schema type " + schema.getClass() + " is not supported");
+        }
+
+        return new DataObjectStreamerCustomizer(registry, typeAndSchema.getKey(), (DataNodeContainer) schema, type,
+            startEvent);
+    }
+
+    @Override
+    public List<Class<?>> customize(final CodecClassLoader loader, final CtClass bindingClass, final CtClass generated)
+            throws CannotCompileException, NotFoundException, IOException {
+        LOG.trace("Definining streamer {}", generated.getName());
+
+        final CtField instanceField = new CtField(generated, INSTANCE_FIELD, generated);
+        instanceField.setModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
+        generated.addField(instanceField, "new " + generated.getName() + "()");
+
+        // This results in a body
+        final String objType = bindingClass.getName();
+        final StringBuilder sb = new StringBuilder()
+                .append("{\n")
+                .append("final ").append(objType).append(" obj = (").append(objType).append(") $2;\n")
+                .append("$3.").append(startEvent).append(");\n");
+
+        final List<Class<?>> dependencies = emitChildren(sb, loader, generated);
+        if (Augmentable.class.isAssignableFrom(type)) {
+            sb.append("streamAugmentations($1, $3, obj);\n");
+        }
+
+        sb.append("$3.endNode();\n")
+            .append('}');
+
+        final CtMethod serialize = new CtMethod(CT_VOID, "serialize", SERIALIZE_ARGS, generated);
+        serialize.setModifiers(Modifier.PUBLIC);
+        serialize.setBody(sb.toString());
+        generated.addMethod(serialize);
+
+        generated.setModifiers(Modifier.PUBLIC | Modifier.FINAL);
+        LOG.trace("Definition of {} done", generated.getName());
+
+        return dependencies;
+    }
+
+    @Override
+    public Class<?> customizeLoading(final @NonNull Supplier<Class<?>> loader) {
+        if (constants.isEmpty()) {
+            return loader.get();
+        }
+
+        final DataObjectStreamerCustomizer prev = DataObjectStreamerBridge.setup(this);
+        try {
+            final Class<?> result = loader.get();
+
+            /*
+             * This a bit of magic to support DataObjectStreamer constants. These constants need to be resolved while
+             * we have the information needed to find them -- that information is being held in this instance and we
+             * leak it to a thread-local variable held by DataObjectStreamerBridge.
+             *
+             * By default the JVM will defer class initialization to first use, which unfortunately is too late for
+             * us, and hence we need to force class to initialize.
+             */
+            try {
+                Class.forName(result.getName(), true, result.getClassLoader());
+            } catch (ClassNotFoundException e) {
+                throw new LinkageError("Failed to find newly-defined " + result, e);
+            }
+
+            return result;
+        } finally {
+            DataObjectStreamerBridge.tearDown(prev);
+        }
+    }
+
+    @NonNull DataObjectStreamer<?> resolve(final @NonNull String methodName) {
+        final Class<? extends DataObject> target = verifyNotNull(constants.get(methodName), "Cannot resolve type of %s",
+            methodName);
+        return verifyNotNull(registry.getDataObjectSerializer(target), "Cannot find serializer for %s", target);
+    }
+
+    private List<Class<?>> emitChildren(final StringBuilder sb, final CodecClassLoader loader,
+            final CtClass generated) throws CannotCompileException {
+        final List<Class<?>> dependencies = new ArrayList<>();
+
+        for (final DataSchemaNode schemaChild : schema.getChildNodes()) {
+            if (!schemaChild.isAugmenting()) {
+                final String getterName = BindingSchemaMapping.getGetterMethodName(schemaChild);
+                final Method getter;
+                try {
+                    getter = type.getMethod(getterName);
+                } catch (NoSuchMethodException e) {
+                    throw new IllegalStateException("Failed to find getter " + getterName, e);
+                }
+
+                final Class<?> dependency = emitChild(sb, loader, generated, getterName, getter.getReturnType(),
+                    schemaChild);
+                if (dependency != null) {
+                    LOG.trace("Require dependency {}", dependency);
+                    dependencies.add(dependency);
+                }
+            }
+        }
+
+        return dependencies;
+    }
+
+    private @Nullable Class<?> emitChild(final StringBuilder sb, final CodecClassLoader loader, final CtClass generated,
+            final String getterName, final Class<?> returnType, final DataSchemaNode child)
+                    throws CannotCompileException {
+        if (child instanceof LeafSchemaNode) {
+            sb.append("streamLeaf($3, \"").append(child.getQName().getLocalName()).append("\", obj.")
+            .append(getterName).append("());\n");
+            return null;
+        }
+        if (child instanceof ContainerSchemaNode) {
+            final Class<? extends DataObject> itemClass = returnType.asSubclass(DataObject.class);
+            final String constField = declareDependency(generated, getterName, itemClass);
+
+            sb.append("streamContainer(").append(constField).append(", $1, $3, obj.").append(getterName)
+            .append("());\n");
+            return registry.getDataObjectSerializer(itemClass).getClass();
+        }
+        if (child instanceof ListSchemaNode) {
+            final Type childType = props.get(getterName);
+            verify(childType instanceof ParameterizedType, "Unexpected type %s for %s", childType, getterName);
+            final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
+            final Class<?> valueClass;
+            try {
+                valueClass = loader.loadClass(valueType.getFullyQualifiedName());
+            } catch (ClassNotFoundException e) {
+                throw new LinkageError("Failed to load " + valueType, e);
+            }
+
+            verify(DataObject.class.isAssignableFrom(valueClass), "Value type %s of %s is not a DataObject", valueClass,
+                returnType);
+            final Class<? extends DataObject> itemClass = valueClass.asSubclass(DataObject.class);
+            final ListSchemaNode casted = (ListSchemaNode) child;
+
+            sb.append("stream");
+            if (casted.getKeyDefinition().isEmpty()) {
+                sb.append("List");
+            } else {
+                if (casted.isUserOrdered()) {
+                    sb.append("Ordered");
+                }
+                sb.append("Map");
+            }
+
+            final String constField = declareDependency(generated, getterName, itemClass);
+            sb.append('(').append(valueClass.getName()).append(".class, ").append(constField).append(", $1, $3, obj.")
+            .append(getterName).append("());\n");
+            return registry.getDataObjectSerializer(itemClass).getClass();
+        }
+        if (child instanceof AnyXmlSchemaNode) {
+            sb.append("streamAnyxml($3, \"").append(child.getQName().getLocalName()).append("\", obj.")
+            .append(getterName).append("());\n");
+            return null;
+        }
+        if (child instanceof LeafListSchemaNode) {
+            sb.append("stream");
+            if (((LeafListSchemaNode) child).isUserOrdered()) {
+                sb.append("Ordered");
+            }
+            sb.append("LeafList($3, \"").append(child.getQName().getLocalName()).append("\", obj.")
+            .append(getterName).append("());\n");
+            return null;
+        }
+        if (child instanceof ChoiceSchemaNode) {
+            sb.append("streamChoice(").append(returnType.getName()).append(".class, $1, $3, obj.").append(getterName)
+            .append("());\n");
+            return null;
+        }
+
+        LOG.debug("Ignoring {} due to unhandled schema {}", getterName, child);
+        return null;
+    }
+
+    /*
+     * Javassist not quite helpful in our environment. We really want to output plain bytecode so that it links
+     * using normal ClassLoader mechanics (supported via CodecClassLoader), but Javassist's compiler really gets in
+     * the way of keeping things simple by requiring CtClass references to dependencies at the the time we set the
+     * implementation body. In order to side-step those requirements, we rely on defining references to our dependencies
+     * as constants and fill them up via customizeLoading().
+     *
+     * This method defines the constants for later use. Should we migrate to a more byte-code oriented framework
+     * (like ByteBuddy), we will pay some cost in assembling the method bodies, we can ditch the constants, as we
+     * provide INSTANCE_FIELD which can readily be reused and CodecClassLoader will resolve the dependencies without
+     * any problems.
+     */
+    private String declareDependency(final CtClass generated, final String getterName,
+            final Class<? extends DataObject> bindingClass) throws CannotCompileException {
+        final String fieldName = getterName + "_STREAMER";
+
+        final CtField instanceField = new CtField(CT_DOS, fieldName, generated);
+        instanceField.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);
+        generated.addField(instanceField,
+            DataObjectStreamerBridge.class.getName() + ".resolve(\"" + getterName + "\")");
+
+        verify(constants.put(getterName, bindingClass) == null, "Duplicate dependency for %s", getterName);
+        return fieldName;
+    }
+
+    private static ImmutableMap<String, Type> collectAllProperties(final GeneratedType type) {
+        final Map<String, Type> props = new HashMap<>();
+        collectAllProperties(type, props);
+        return ImmutableMap.copyOf(props);
+    }
+
+    private static void collectAllProperties(final GeneratedType type, final Map<String, Type> hashMap) {
+        for (final MethodSignature definition : type.getMethodDefinitions()) {
+            hashMap.put(definition.getName(), definition.getReturnType());
+        }
+        for (final Type parent : type.getImplements()) {
+            if (parent instanceof GeneratedType) {
+                collectAllProperties((GeneratedType) parent, hashMap);
+            }
+        }
+    }
+}
index 5944d7951e26468fa110efa6b81be3746a04ab4a..95f308fe61854ed31ba64166e7a4e545e99c9c18 100644 (file)
@@ -83,6 +83,8 @@ abstract class NodeCodecContext implements BindingCodecTreeNode {
          */
         @NonNull CodecClassLoader getLoader();
 
+        @NonNull DataObjectStreamer<?> getDataObjectSerializer(Class<?> type);
+
         DataObjectSerializer getEventStreamSerializer(Class<?> type);
     }
 
index 766523b8e29fee19898cf9d729a2f5fc4c5745ff..6176aa06d9838865d8796adb4ad28442ae5bfdef 100644 (file)
@@ -7,34 +7,26 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.test;
 
-import javassist.ClassPool;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
-import org.opendaylight.mdsal.binding.dom.codec.gen.impl.StreamWriterGenerator;
 import org.opendaylight.mdsal.binding.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
-import org.opendaylight.mdsal.binding.generator.util.JavassistUtils;
 
 public abstract class AbstractBindingCodecTest extends AbstractBindingRuntimeTest {
-    private static JavassistUtils UTILS;
-
     protected BindingNormalizedNodeCodecRegistry registry;
 
     @BeforeClass
     public static void beforeClass() {
         AbstractBindingRuntimeTest.beforeClass();
-        UTILS = JavassistUtils.forClassPool(ClassPool.getDefault());
     }
 
     @AfterClass
     public static void afterClass() {
-        UTILS = null;
         AbstractBindingRuntimeTest.afterClass();
     }
 
     @Before
     public void before() {
-        this.registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(UTILS));
-        this.registry.onBindingRuntimeContextUpdated(getRuntimeContext());
+        this.registry = new BindingNormalizedNodeCodecRegistry(getRuntimeContext());
     }
 }
index 61528083578d736b0c70f3cb464703454be095d8..297d59ae7cf2ff613cf20cb75e05582b5a14cacd 100644 (file)
@@ -13,16 +13,13 @@ import static org.junit.Assert.assertNotNull;
 import java.util.HashSet;
 import java.util.Map.Entry;
 import java.util.Set;
-import javassist.ClassPool;
 import org.junit.Before;
 import org.junit.Test;
-import org.opendaylight.mdsal.binding.dom.codec.gen.impl.StreamWriterGenerator;
 import org.opendaylight.mdsal.binding.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
 import org.opendaylight.mdsal.binding.dom.codec.impl.MissingClassInLoadingStrategyException;
 import org.opendaylight.mdsal.binding.generator.impl.GeneratedClassLoadingStrategy;
 import org.opendaylight.mdsal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
-import org.opendaylight.mdsal.binding.generator.util.JavassistUtils;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeLeafOnlyAugment;
@@ -57,9 +54,7 @@ public class AugmentationClassDiscoveredAfterCodecTest {
         mockedContext = new ClassExcludingClassLoadingStrategy(ctx);
         schemaContext = ctx.tryToCreateSchemaContext().get();
         runtimeContext = BindingRuntimeContext.create(mockedContext, schemaContext);
-        final JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault());
-        registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils));
-        registry.onBindingRuntimeContextUpdated(runtimeContext);
+        registry = new BindingNormalizedNodeCodecRegistry(runtimeContext);
     }
 
     private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
index eaa748b8e56b3cf511b40b34f2dac1a84b388ede..f1be7157d49c15160fd25627f1b7a0765db57642 100644 (file)
@@ -7,16 +7,13 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.test;
 
-import javassist.ClassPool;
 import org.junit.Test;
-import org.opendaylight.mdsal.binding.dom.codec.gen.impl.StreamWriterGenerator;
 import org.opendaylight.mdsal.binding.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
 import org.opendaylight.mdsal.binding.dom.codec.impl.IncorrectNestingException;
 import org.opendaylight.mdsal.binding.dom.codec.impl.MissingSchemaException;
 import org.opendaylight.mdsal.binding.dom.codec.impl.MissingSchemaForClassException;
 import org.opendaylight.mdsal.binding.generator.impl.ModuleInfoBackedContext;
 import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
-import org.opendaylight.mdsal.binding.generator.util.JavassistUtils;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeLeafOnlyAugment;
@@ -94,10 +91,7 @@ public class ExceptionReportingTest {
         }
         final SchemaContext schema = ctx.tryToCreateSchemaContext().get();
         final BindingRuntimeContext runtimeCtx = BindingRuntimeContext.create(ctx, schema);
-        final JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault());
-        final BindingNormalizedNodeCodecRegistry registry = new BindingNormalizedNodeCodecRegistry(
-            StreamWriterGenerator.create(utils));
-        registry.onBindingRuntimeContextUpdated(runtimeCtx);
+        final BindingNormalizedNodeCodecRegistry registry = new BindingNormalizedNodeCodecRegistry(runtimeCtx);
         return registry;
     }
 }