Retain protype in DataContainerCodecContext 48/109748/2
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 11 Jan 2024 18:42:26 +0000 (19:42 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 11 Jan 2024 23:10:48 +0000 (00:10 +0100)
Acknowledge that DataContainerCodecContext is always instantiated from a
prototype and retain it in the base class.

This ends up centralizing things enough to eliminate an ugly cast we are
using in AugmentationCodecContext.

JIRA: MDSAL-815
Change-Id: Ib604918c2d9e180fece0583331fc2486797cf546
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
13 files changed:
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractBindingNormalizedNodeCacheHolder.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractDataObjectCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AugmentationCodecContext.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/BindingToNormalizedStreamWriter.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeCodec.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeSerializer.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/ChoiceCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CommonDataObjectCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataContainerCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectNormalizedNodeCache.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java

index d07de864c57bc4295f07c29797e6dc8db2ffeb3a..e50b64402e3168b99fa9f8c750a8279c112543dd 100644 (file)
@@ -27,7 +27,7 @@ abstract class AbstractBindingNormalizedNodeCacheHolder {
             @Override
             public AbstractBindingNormalizedNodeCache load(final CodecContext key) {
                 // FIXME: Use a switch expression once we have https://openjdk.org/jeps/441
-                if (key instanceof DataContainerCodecContext<?, ?> dataContainer) {
+                if (key instanceof DataContainerCodecContext<?, ?, ?> dataContainer) {
                     return new DataObjectNormalizedNodeCache(AbstractBindingNormalizedNodeCacheHolder.this,
                         dataContainer);
                 }
index 0afa5e63e45a932d1ba14757198ffe58c21c7cda..d0e2f04db38233ca2e7b478cf789fffd46db72ab 100644 (file)
@@ -60,7 +60,7 @@ public abstract sealed class AbstractDataObjectCodecContext<D extends DataObject
     @Override
     public final WithStatus getSchema() {
         // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
-        return (WithStatus) type().statement();
+        return (WithStatus) prototype().runtimeType().statement();
     }
 
     @Override
@@ -80,7 +80,7 @@ public abstract sealed class AbstractDataObjectCodecContext<D extends DataObject
 
             final var caseType = arg.getCaseType();
             final var type = arg.getType();
-            final DataContainerCodecContext<?, ?> caze;
+            final DataContainerCodecContext<?, ?, ?> caze;
             if (caseType.isPresent()) {
                 // Non-ambiguous addressing this should not pose any problems
                 caze = choice.getStreamChild(caseType.orElseThrow());
index 9401a0e2f62fa06035d9a148b77099c4c79b63db..3d0e09f59dd1e540e456d7067f15627e5a29bf27 100644 (file)
@@ -73,7 +73,7 @@ final class AugmentationCodecContext<D extends DataObject & Augmentation<?>>
     @SuppressWarnings("checkstyle:illegalCatch")
     @Override
     public D filterFrom(final DataContainerNode parentData) {
-        for (var childArg : ((AugmentationCodecPrototype) prototype).getChildArgs()) {
+        for (var childArg : ((AugmentationCodecPrototype) prototype()).getChildArgs()) {
             if (parentData.childByArg(childArg) != null) {
                 try {
                     return (D) proxyConstructor.invokeExact(this, parentData);
index c3e327ec9a2d518aeaabde3bd378e6dd70a77aee..1547e7d0a9654c430d69b7f8c3abc5851ad3fd68 100644 (file)
@@ -151,10 +151,10 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
                 return new DataContainerSerializer(BindingCodecContext.this, streamers.get(key));
             }
         });
-    private final LoadingCache<Class<? extends DataObject>, DataContainerCodecContext<?, ?>> childrenByClass =
+    private final LoadingCache<Class<? extends DataObject>, DataContainerCodecContext<?, ?, ?>> childrenByClass =
         CacheBuilder.newBuilder().build(new CacheLoader<>() {
             @Override
-            public DataContainerCodecContext<?, ?> load(final Class<? extends DataObject> key) {
+            public DataContainerCodecContext<?, ?, ?> load(final Class<? extends DataObject> key) {
                 final var childSchema = context.getTypes().bindingChild(JavaTypeName.create(key));
                 if (childSchema instanceof ContainerLikeRuntimeType containerLike) {
                     if (childSchema instanceof ContainerRuntimeType container
@@ -177,10 +177,10 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
         });
 
     // FIXME: this could also be a leaf!
-    private final LoadingCache<QName, DataContainerCodecContext<?, ?>> childrenByDomArg =
+    private final LoadingCache<QName, DataContainerCodecContext<?, ?, ?>> childrenByDomArg =
         CacheBuilder.newBuilder().build(new CacheLoader<>() {
             @Override
-            public DataContainerCodecContext<?, ?> load(final QName qname) throws ClassNotFoundException {
+            public DataContainerCodecContext<?, ?, ?> load(final QName qname) throws ClassNotFoundException {
                 final var type = context.getTypes();
                 final var child = type.schemaTreeChild(qname);
                 if (child == null) {
@@ -426,12 +426,12 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
         return new BindingToNormalizedStreamWriter(getActionCodec(action).output(), domWriter);
     }
 
-    @NonNull DataContainerCodecContext<?,?> getCodecContextNode(final InstanceIdentifier<?> binding,
+    @NonNull DataContainerCodecContext<?, ?, ?> getCodecContextNode(final InstanceIdentifier<?> binding,
             final List<PathArgument> builder) {
         final var it = binding.getPathArguments().iterator();
         final var arg = it.next();
 
-        DataContainerCodecContext<?, ?> current;
+        DataContainerCodecContext<?, ?, ?> current;
         final var caseType = arg.getCaseType();
         if (caseType.isPresent()) {
             final @NonNull Class<? extends DataObject> type = caseType.orElseThrow();
index d4b8aedda2f50c80cf51e2c1694f300124939bbf..7ae433121825ed9b304cb44375bdcbd139a2c9d6 100644 (file)
@@ -37,7 +37,7 @@ final class BindingToNormalizedStreamWriter implements AnydataBindingStreamWrite
     private final @NonNull NormalizedNodeStreamWriter delegate;
     private final CodecContext rootContext;
 
-    BindingToNormalizedStreamWriter(final DataContainerCodecContext<?, ?> rootContext,
+    BindingToNormalizedStreamWriter(final DataContainerCodecContext<?, ?, ?> rootContext,
             final NormalizedNodeStreamWriter delegate) {
         this.rootContext = requireNonNull(rootContext);
         this.delegate = requireNonNull(delegate);
@@ -71,7 +71,7 @@ final class BindingToNormalizedStreamWriter implements AnydataBindingStreamWrite
         if (current == null) {
             // Entry of first node
             next = rootContext;
-        } else if (current instanceof DataContainerCodecContext<?, ?> currentContainer) {
+        } else if (current instanceof DataContainerCodecContext<?, ?, ?> currentContainer) {
             next = currentContainer.getStreamChild((Class) name);
         } else {
             throw new IllegalArgumentException("Could not start node " + name + " in non-container " + current);
index 4d8599d4c419c2293e7aca20a47ee88f74a10d2e..a1807fd658ea45ca1606960a633e6d4320bc8259 100644 (file)
@@ -17,7 +17,7 @@ import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 class CachingNormalizedNodeCodec<D extends DataObject,
-        C extends DataContainerCodecContext<D, ?> & BindingNormalizedNodeCodec<D>>
+        C extends DataContainerCodecContext<D, ?, ?> & BindingNormalizedNodeCodec<D>>
         extends AbstractBindingNormalizedNodeCacheHolder implements BindingNormalizedNodeCachingCodec<D> {
     private final @NonNull C context;
 
index b02b64879ad72aaad66dbed722c9e841c6e518a1..60cbccf998b45c4acee59fb22092cc680a97d09d 100644 (file)
@@ -39,7 +39,7 @@ final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEvent
     private final BindingToNormalizedStreamWriter delegate;
 
     private CachingNormalizedNodeSerializer(final AbstractBindingNormalizedNodeCacheHolder cacheHolder,
-            final DataContainerCodecContext<?, ?> subtreeRoot) {
+            final DataContainerCodecContext<?, ?, ?> subtreeRoot) {
         this.cacheHolder = requireNonNull(cacheHolder);
         delegate = new BindingToNormalizedStreamWriter(subtreeRoot, domWriter);
     }
@@ -53,7 +53,7 @@ final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEvent
      * @return Normalized Node representation of data.
      */
     static NormalizedNode serializeUsingStreamWriter(final AbstractBindingNormalizedNodeCacheHolder cacheHolder,
-            final DataContainerCodecContext<?, ?> subtreeRoot, final DataObject data) {
+            final DataContainerCodecContext<?, ?, ?> subtreeRoot, final DataObject data) {
         final var writer = new CachingNormalizedNodeSerializer(cacheHolder, subtreeRoot);
         try {
             subtreeRoot.eventStreamSerializer().serialize(data, writer);
@@ -120,7 +120,7 @@ final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEvent
     private AbstractBindingNormalizedNodeCache<DataObject, ?> getCacheSerializer(
             final Class<? extends DataObject> type) {
         if (cacheHolder.isCached(type)) {
-            final var currentCtx = (DataContainerCodecContext<?, ?>) delegate.current();
+            final var currentCtx = (DataContainerCodecContext<?, ?, ?>) delegate.current();
             if (type.equals(currentCtx.getBindingClass())) {
                 return cacheHolder.getCachingSerializer(currentCtx);
             }
index 810dfdca652c28b0204fad333834cc732e06985d..8e0adc74b7e6bb314a16b24288cb9330a61f0e78 100644 (file)
@@ -204,7 +204,7 @@ final class ChoiceCodecContext<D extends DataObject> extends CommonDataObjectCod
     @Override
     public WithStatus getSchema() {
         // FIXME: Bad cast, we should be returning an EffectiveStatement perhaps?
-        return (WithStatus) type().statement();
+        return (WithStatus) prototype().runtimeType().statement();
     }
 
     @Override
@@ -269,7 +269,7 @@ final class ChoiceCodecContext<D extends DataObject> extends CommonDataObjectCod
         return createCachingCodec(this, cacheSpecifier);
     }
 
-    DataContainerCodecContext<?, ?> getCaseByChildClass(final @NonNull Class<? extends DataObject> type) {
+    DataContainerCodecContext<?, ?, ?> getCaseByChildClass(final @NonNull Class<? extends DataObject> type) {
         var result = byCaseChildClass.get(type);
         if (result == null) {
             // We have not found an unambiguous result, try ambiguous ones
index cae761a9bb2db286a26641e80c73ac7e291fb065..f5416d24f54fbebefb5ef001f3cd5238d2c195f7 100644 (file)
@@ -7,9 +7,6 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
-import static java.util.Objects.requireNonNull;
-
-import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.dom.codec.api.CommonDataObjectCodecTreeNode;
 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -21,34 +18,22 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
  * Base implementation of {@link CommonDataObjectCodecTreeNode}.
  */
 abstract sealed class CommonDataObjectCodecContext<D extends DataObject, T extends CompositeRuntimeType>
-        extends DataContainerCodecContext<D, T> implements CommonDataObjectCodecTreeNode<D>
+        extends DataContainerCodecContext<D, T, CommonDataObjectCodecPrototype<T>>
+        implements CommonDataObjectCodecTreeNode<D>
         permits AbstractDataObjectCodecContext, ChoiceCodecContext {
-    final @NonNull CommonDataObjectCodecPrototype<T> prototype;
-
     CommonDataObjectCodecContext(final CommonDataObjectCodecPrototype<T> prototype) {
-        super(prototype.runtimeType());
-        this.prototype = requireNonNull(prototype);
+        super(prototype);
     }
 
     @SuppressWarnings("unchecked")
     @Override
     public final Class<D> getBindingClass() {
-        return Class.class.cast(prototype.javaClass());
-    }
-
-    @Override
-    protected final CodecContextFactory factory() {
-        return prototype.contextFactory();
-    }
-
-    @Override
-    protected final T type() {
-        return prototype.runtimeType();
+        return Class.class.cast(prototype().javaClass());
     }
 
     @Override
     protected NodeIdentifier getDomPathArgument() {
-        return prototype.getYangArg();
+        return prototype().getYangArg();
     }
 
     /**
@@ -59,6 +44,6 @@ abstract sealed class CommonDataObjectCodecContext<D extends DataObject, T exten
     }
 
     protected final PathArgument bindingArg() {
-        return prototype.getBindingArg();
+        return prototype().getBindingArg();
     }
 }
index d919a4ae9b07dc4ea1a04f02ae88ef6fca49e950..a1fd1bfc29658831fe40508ca921dbf54ba0b701 100644 (file)
@@ -62,7 +62,8 @@ import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-abstract sealed class DataContainerCodecContext<D extends DataContainer, T extends CompositeRuntimeType>
+abstract sealed class DataContainerCodecContext<D extends DataContainer, R extends CompositeRuntimeType,
+        P extends DataContainerPrototype<?, R>>
         extends CodecContext implements BindingDataContainerCodecTreeNode<D>
         permits CommonDataObjectCodecContext {
     private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecContext.class);
@@ -77,6 +78,7 @@ abstract sealed class DataContainerCodecContext<D extends DataContainer, T exten
         }
     }
 
+    private final @NonNull P prototype;
     private final @NonNull ChildAddressabilitySummary childAddressabilitySummary;
 
     // Accessed via a VarHandle
@@ -84,8 +86,13 @@ abstract sealed class DataContainerCodecContext<D extends DataContainer, T exten
     @SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "https://github.com/spotbugs/spotbugs/issues/2749")
     private volatile DataContainerSerializer eventStreamSerializer;
 
-    DataContainerCodecContext(final T type) {
-        childAddressabilitySummary = computeChildAddressabilitySummary(type.statement());
+    DataContainerCodecContext(final P prototype) {
+        this.prototype = requireNonNull(prototype);
+        childAddressabilitySummary = computeChildAddressabilitySummary(prototype.runtimeType().statement());
+    }
+
+    final @NonNull P prototype() {
+        return prototype;
     }
 
     @Override
@@ -93,10 +100,6 @@ abstract sealed class DataContainerCodecContext<D extends DataContainer, T exten
         return childAddressabilitySummary;
     }
 
-    protected abstract @NonNull CodecContextFactory factory();
-
-    protected abstract @NonNull T type();
-
     // Non-final for ChoiceCodecContext
     @Override
     public CodecContext yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg) {
@@ -160,7 +163,7 @@ abstract sealed class DataContainerCodecContext<D extends DataContainer, T exten
         return getClass().getSimpleName() + " [" + getBindingClass() + "]";
     }
 
-    static final <T extends DataObject, C extends DataContainerCodecContext<T, ?> & BindingNormalizedNodeCodec<T>>
+    static final <T extends DataObject, C extends DataContainerCodecContext<T, ?, ?> & BindingNormalizedNodeCodec<T>>
             @NonNull BindingNormalizedNodeCachingCodec<T> createCachingCodec(final C context,
                 final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
         return cacheSpecifier.isEmpty() ? new NonCachingCodec<>(context)
@@ -194,7 +197,7 @@ abstract sealed class DataContainerCodecContext<D extends DataContainer, T exten
     @CheckReturnValue
     private IllegalArgumentException childNullException(final QName child, final String message, final Object... args) {
         final var module = child.getModule();
-        if (!factory().getRuntimeContext().modelContext().findModule(module).isPresent()) {
+        if (!prototype().contextFactory().getRuntimeContext().modelContext().findModule(module).isPresent()) {
             return new MissingSchemaException("Module " + module + " is not present in current schema context.");
         }
         return new IncorrectNestingException(message, args);
@@ -203,7 +206,7 @@ abstract sealed class DataContainerCodecContext<D extends DataContainer, T exten
     @CheckReturnValue
     private @NonNull IllegalArgumentException childNullException(final Class<?> childClass, final String message,
             final Object... args) {
-        return childNullException(factory().getRuntimeContext(), childClass, message, args);
+        return childNullException(prototype().contextFactory().getRuntimeContext(), childClass, message, args);
     }
 
     @CheckReturnValue
@@ -236,7 +239,7 @@ abstract sealed class DataContainerCodecContext<D extends DataContainer, T exten
 
     // Split out to aid inlining
     private DataContainerSerializer loadEventStreamSerializer() {
-        final DataContainerSerializer loaded = factory().getEventStreamSerializer(getBindingClass());
+        final DataContainerSerializer loaded = prototype().contextFactory().getEventStreamSerializer(getBindingClass());
         final Object witness = EVENT_STREAM_SERIALIZER.compareAndExchangeRelease(this, null, loaded);
         return witness == null ? loaded : (DataContainerSerializer) witness;
     }
index a1a24dda503701429d434b22c82c21bd0184ce29..5ba4e8fd9609a5c6c6ab3c4be97993ce728eae47 100644 (file)
@@ -32,7 +32,6 @@ import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.AugmentableRuntimeType;
-import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
@@ -105,6 +104,7 @@ public abstract sealed class DataObjectCodecContext<D extends DataObject, T exte
 
         // Final bits: generate the appropriate class, As a side effect we identify what Augmentations are possible
         final List<AugmentRuntimeType> possibleAugmentations;
+        final var loader = prototype().contextFactory().getLoader();
         if (Augmentable.class.isAssignableFrom(bindingClass)) {
             // Verify we have the appropriate backing runtimeType
             final var runtimeType = prototype.runtimeType();
@@ -114,12 +114,12 @@ public abstract sealed class DataObjectCodecContext<D extends DataObject, T exte
             }
 
             possibleAugmentations = augmentableRuntimeType.augments();
-            generatedClass = CodecDataObjectGenerator.generateAugmentable(factory().getLoader(), bindingClass,
-                analysis.leafContexts, analysis.daoProperties, keyMethod);
+            generatedClass = CodecDataObjectGenerator.generateAugmentable(loader, bindingClass, analysis.leafContexts,
+                analysis.daoProperties, keyMethod);
         } else {
             possibleAugmentations = List.of();
-            generatedClass = CodecDataObjectGenerator.generate(factory().getLoader(), bindingClass,
-                analysis.leafContexts, analysis.daoProperties, keyMethod);
+            generatedClass = CodecDataObjectGenerator.generate(loader, bindingClass, analysis.leafContexts,
+                analysis.daoProperties, keyMethod);
         }
 
         // All done: acquire the constructor: it is supposed to be public
@@ -239,7 +239,7 @@ public abstract sealed class DataObjectCodecContext<D extends DataObject, T exte
     }
 
     private boolean belongsToRuntimeContext(final Class<?> cls) {
-        final BindingRuntimeContext ctx = factory().getRuntimeContext();
+        final var ctx = prototype().contextFactory().getRuntimeContext();
         final Class<?> loaded;
         try {
             loaded = ctx.loadClass(Type.of(cls));
@@ -262,7 +262,7 @@ public abstract sealed class DataObjectCodecContext<D extends DataObject, T exte
             return null;
         }
 
-        final var factory = factory();
+        final var factory = prototype().contextFactory();
         final GeneratedType javaType = augment.javaType();
         final Class<? extends Augmentation<?>> augClass;
         try {
index f52f40a85a93757b6794eeccedc4834bcac5281a..c5528471dcb85c3453b017b47a4ee34839f652ec 100644 (file)
@@ -16,11 +16,11 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
  * A cache of NormalizedNodes corresponding to a particular DataObject instantiation.
  */
 final class DataObjectNormalizedNodeCache
-        extends AbstractBindingNormalizedNodeCache<DataObject, DataContainerCodecContext<?, ?>> {
+        extends AbstractBindingNormalizedNodeCache<DataObject, DataContainerCodecContext<?, ?, ?>> {
     private final AbstractBindingNormalizedNodeCacheHolder cacheHolder;
 
     DataObjectNormalizedNodeCache(final AbstractBindingNormalizedNodeCacheHolder cacheHolder,
-            final DataContainerCodecContext<?, ?> rootContext) {
+            final DataContainerCodecContext<?, ?, ?> rootContext) {
         super(rootContext);
         this.cacheHolder = requireNonNull(cacheHolder, "cacheHolder");
     }
index 8c9ea4ac3cd0b43d065ce71543a6fb78293af6d4..324b01f01863a48e3ffeac72c32740de9ca5587f 100644 (file)
@@ -72,11 +72,11 @@ final class NotificationCodecContext<D extends DataObject & BaseNotification>
     NotificationCodecContext(final Class<?> notificationClass, final NotificationRuntimeType type,
             final CodecContextFactory factory) {
         super(new Prototype<>(notificationClass, type, factory));
-        final Class<D> bindingClass = getBindingClass();
+        final var bindingClass = getBindingClass();
 
-        final Class<?> awareClass = CodecPackage.EVENT_AWARE.generateClass(factory().getLoader(), bindingClass,
-            (loader, fqcn, bindingInterface) -> {
-                final Class<?> codecImpl = CodecPackage.CODEC.getGeneratedClass(loader, bindingClass);
+        final var eventAwareClass = CodecPackage.EVENT_AWARE.generateClass(prototype().contextFactory().getLoader(),
+            bindingClass, (loader, fqcn, bindingInterface) -> {
+                final var codecImpl = CodecPackage.CODEC.getGeneratedClass(loader, bindingClass);
 
                 return GeneratorResult.of(new ByteBuddy()
                     .subclass(codecImpl, ConstructorStrategy.Default.NO_CONSTRUCTORS)
@@ -93,7 +93,7 @@ final class NotificationCodecContext<D extends DataObject & BaseNotification>
 
         final MethodHandle ctor;
         try {
-            ctor = MethodHandles.publicLookup().findConstructor(awareClass, CONSTRUCTOR_TYPE);
+            ctor = MethodHandles.publicLookup().findConstructor(eventAwareClass, CONSTRUCTOR_TYPE);
         } catch (IllegalAccessException | NoSuchMethodException e) {
             throw new LinkageError("Failed to acquire constructor", e);
         }