Split out mdsal-binding-loader 37/103337/6
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 24 Nov 2022 00:50:45 +0000 (01:50 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 24 Nov 2022 09:45:07 +0000 (10:45 +0100)
The class loader based on ByteBuddy is more generally useful, split it
out into a separate artifact for reuse.

Also rename CodecClassLoader to BindingClassLoader, as that is more
appropriate. Modernize the code a bit by using local variable type
inference. Furthermore drop dependency on Guava's Supplier, as we can
achieve the same with java.util.function.Supplier.

JIRA: MDSAL-793
Change-Id: I3630e452ed631c11cd0ea62b1d790bec360baf4a
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
18 files changed:
artifacts/pom.xml
binding/mdsal-binding-dom-codec/pom.xml
binding/mdsal-binding-dom-codec/src/main/java/module-info.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/ClassGeneratorBridge.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CodecDataObjectGenerator.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectStreamerGenerator.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NodeCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/NotificationCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/OpaqueNodeCodecContext.java
binding/mdsal-binding-loader/pom.xml [new file with mode: 0644]
binding/mdsal-binding-loader/src/main/java/module-info.java [new file with mode: 0644]
binding/mdsal-binding-loader/src/main/java/org/opendaylight/mdsal/binding/loader/BindingClassLoader.java [moved from binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/loader/CodecClassLoader.java with 70% similarity]
binding/mdsal-binding-loader/src/main/java/org/opendaylight/mdsal/binding/loader/LeafBindingClassLoader.java [moved from binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/loader/LeafCodecClassLoader.java with 61% similarity]
binding/mdsal-binding-loader/src/main/java/org/opendaylight/mdsal/binding/loader/RootBindingClassLoader.java [moved from binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/loader/RootCodecClassLoader.java with 71% similarity]
binding/mdsal-binding-loader/src/main/java/org/opendaylight/mdsal/binding/loader/package-info.java [moved from binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/loader/package-info.java with 54% similarity]
binding/pom.xml
docs/pom.xml

index cbb62f5435352f2e0b0c7d183b0e4d81b1deb69e..882c34e3f4c9fcff1238eb27da88d8fe17c4c373 100644 (file)
             </dependency>
 
             <!-- Binding MD-SAL & Java Binding -->
+            <dependency>
+                <groupId>org.opendaylight.mdsal</groupId>
+                <artifactId>mdsal-binding-loader</artifactId>
+                <version>11.0.0-SNAPSHOT</version>
+            </dependency>
             <dependency>
                 <groupId>org.opendaylight.mdsal</groupId>
                 <artifactId>mdsal-binding-model-api</artifactId>
index e8144bb23bb3287810c9bd858739ab05eb6ff701..6e16f67d2070da741841e143c32b35187b9a2f59 100644 (file)
             <groupId>org.opendaylight.yangtools</groupId>
             <artifactId>yang-model-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-loader</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-binding-model-api</artifactId>
index 6376925a41cc5eb67b0d8be20e3fc8abf0f40337..a27a9178836562e79eaaf573a159cf84025c0bd4 100644 (file)
@@ -26,6 +26,7 @@ module org.opendaylight.mdsal.binding.dom.codec.impl {
     requires transitive org.opendaylight.mdsal.binding.dom.codec.spi;
     requires com.google.common;
     requires net.bytebuddy;
+    requires org.opendaylight.mdsal.binding.loader;
     requires org.opendaylight.mdsal.binding.model.api;
     requires org.opendaylight.mdsal.binding.spec.util;
     requires org.opendaylight.yangtools.concepts;
index 3810f5f04c9f03c7060b9aa1cab92868ec35b88a..307e6e1cd4351cd7fcb1d05653345adcf570d8e5 100644 (file)
@@ -12,11 +12,13 @@ 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.base.Strings;
 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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
@@ -43,10 +45,10 @@ import org.opendaylight.mdsal.binding.dom.codec.api.BindingInstanceIdentifierCod
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeWriterFactory;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingStreamEventWriter;
 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader;
 import org.opendaylight.mdsal.binding.dom.codec.spi.AbstractBindingNormalizedNodeSerializer;
 import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecServices;
 import org.opendaylight.mdsal.binding.dom.codec.spi.BindingSchemaMapping;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.runtime.api.ListRuntimeType;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
@@ -118,6 +120,12 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
     }
 
     private static final Logger LOG = LoggerFactory.getLogger(BindingCodecContext.class);
+    private static final File BYTECODE_DIRECTORY;
+
+    static {
+        final String dir = System.getProperty("org.opendaylight.mdsal.binding.dom.codec.loader.bytecodeDumpDirectory");
+        BYTECODE_DIRECTORY = Strings.isNullOrEmpty(dir) ? null : new File(dir);
+    }
 
     private final LoadingCache<Class<?>, DataObjectStreamer<?>> streamers = CacheBuilder.newBuilder().build(
         new CacheLoader<Class<?>, DataObjectStreamer<?>>() {
@@ -137,7 +145,8 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
             }
         });
 
-    private final @NonNull CodecClassLoader loader = CodecClassLoader.create();
+    private final @NonNull BindingClassLoader loader =
+        BindingClassLoader.create(BindingCodecContext.class, BYTECODE_DIRECTORY);
     private final @NonNull InstanceIdentifierCodec instanceIdentifierCodec;
     private final @NonNull IdentityCodec identityCodec;
     private final @NonNull BindingRuntimeContext context;
@@ -161,7 +170,7 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
     }
 
     @Override
-    public CodecClassLoader getLoader() {
+    public BindingClassLoader getLoader() {
         return loader;
     }
 
index 7b2c313f9e1cb67a2c9cd0214f0f71493b324bf8..e3b78561eb47c8769bb5a9f25d8b7df410563295 100644 (file)
@@ -10,10 +10,10 @@ package org.opendaylight.mdsal.binding.dom.codec.impl;
 import static com.google.common.base.Verify.verifyNotNull;
 
 import com.google.common.annotations.Beta;
-import com.google.common.base.Supplier;
+import java.util.function.Supplier;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader.ClassGenerator;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader.ClassGenerator;
 
 /**
  * Bridge for initializing generated instance constants during class loading time. This class is public only due to
@@ -24,9 +24,9 @@ public final class ClassGeneratorBridge {
     interface BridgeProvider<T> extends ClassGenerator<T> {
         @Override
         default Class<T> customizeLoading(final @NonNull Supplier<Class<T>> loader) {
-            final BridgeProvider<?> prev = ClassGeneratorBridge.setup(this);
+            final var prev = ClassGeneratorBridge.setup(this);
             try {
-                final Class<T> result = loader.get();
+                final var result = loader.get();
 
                 /*
                  * This a bit of magic to support NodeContextSupplier constants. These constants need to be resolved
@@ -62,7 +62,7 @@ public final class ClassGeneratorBridge {
     private static final ThreadLocal<BridgeProvider<?>> CURRENT_CUSTOMIZER = new ThreadLocal<>();
 
     private ClassGeneratorBridge() {
-
+        // Hidden on purpose
     }
 
     public static @NonNull NodeContextSupplier resolveNodeContextSupplier(final @NonNull String methodName) {
@@ -74,7 +74,7 @@ public final class ClassGeneratorBridge {
     }
 
     static @Nullable BridgeProvider<?> setup(final @NonNull BridgeProvider<?> next) {
-        final BridgeProvider<?> prev = CURRENT_CUSTOMIZER.get();
+        final var prev = CURRENT_CUSTOMIZER.get();
         CURRENT_CUSTOMIZER.set(verifyNotNull(next));
         return prev;
     }
index 40816f24cd8eb3562bc414e8d773dd982a60677b..62776c833c163554762bbc0229b6f8c88d0166c2 100644 (file)
@@ -43,9 +43,9 @@ import net.bytebuddy.jar.asm.Opcodes;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.dom.codec.impl.ClassGeneratorBridge.LocalNameProvider;
 import org.opendaylight.mdsal.binding.dom.codec.impl.ClassGeneratorBridge.NodeContextSupplierProvider;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader.ClassGenerator;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader.GeneratorResult;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader.ClassGenerator;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader.GeneratorResult;
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.slf4j.Logger;
@@ -130,7 +130,7 @@ import org.slf4j.LoggerFactory;
  *
  * <p>
  * We take a different approach here, which takes advantage of the fact we are in control of both code generation (here)
- * and class loading (in {@link CodecClassLoader}). The process is performed in four steps:
+ * and class loading (in {@link BindingClassLoader}). The process is performed in four steps:
  * <ul>
  * <li>During code generation, the context fields are pointed towards
  *     {@link ClassGeneratorBridge#resolveNodeContextSupplier(String)} and
@@ -260,7 +260,7 @@ abstract class CodecDataObjectGenerator<T extends CodecDataObject<?>> implements
         this.keyMethod = keyMethod;
     }
 
-    static <D extends DataObject, T extends CodecDataObject<T>> Class<T> generate(final CodecClassLoader loader,
+    static <D extends DataObject, T extends CodecDataObject<T>> Class<T> generate(final BindingClassLoader loader,
             final Class<D> bindingInterface, final ImmutableMap<Method, ValueNodeCodecContext> simpleProperties,
             final Map<Class<?>, PropertyInfo> daoProperties, final Method keyMethod) {
         return loader.generateClass(bindingInterface, "codecImpl",
@@ -268,7 +268,7 @@ abstract class CodecDataObjectGenerator<T extends CodecDataObject<?>> implements
     }
 
     static <D extends DataObject, T extends CodecDataObject<T>> Class<T> generateAugmentable(
-            final CodecClassLoader loader, final Class<D> bindingInterface,
+            final BindingClassLoader loader, final Class<D> bindingInterface,
             final ImmutableMap<Method, ValueNodeCodecContext> simpleProperties,
             final Map<Class<?>, PropertyInfo> daoProperties, final Method keyMethod) {
         return loader.generateClass(bindingInterface, "codecImpl",
@@ -276,7 +276,7 @@ abstract class CodecDataObjectGenerator<T extends CodecDataObject<?>> implements
     }
 
     @Override
-    public final GeneratorResult<T> generateClass(final CodecClassLoader loader, final String fqcn,
+    public final GeneratorResult<T> generateClass(final BindingClassLoader loader, final String fqcn,
             final Class<?> bindingInterface) {
         LOG.trace("Generating class {}", fqcn);
 
index b0da58398506299300e7e0c6db24b79a642e85f0..1161b5ca34aab36b47aa5aceea45d17d7b9a7996 100644 (file)
@@ -50,10 +50,10 @@ import net.bytebuddy.matcher.ElementMatchers;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingStreamEventWriter;
 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader.ClassGenerator;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader.GeneratorResult;
 import org.opendaylight.mdsal.binding.dom.codec.spi.BindingSchemaMapping;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader.ClassGenerator;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader.GeneratorResult;
 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.model.api.MethodSignature;
 import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
@@ -173,7 +173,7 @@ final class DataObjectStreamerGenerator<T extends DataObjectStreamer<?>> impleme
         this.startEvent = requireNonNull(startEvent);
     }
 
-    static Class<? extends DataObjectStreamer<?>> generateStreamer(final CodecClassLoader loader,
+    static Class<? extends DataObjectStreamer<?>> generateStreamer(final BindingClassLoader loader,
             final CodecContextFactory registry, final Class<?> type) {
 
         final var typeAndSchema = registry.getRuntimeContext().getTypeWithSchema(type);
@@ -203,7 +203,7 @@ final class DataObjectStreamerGenerator<T extends DataObjectStreamer<?>> impleme
     }
 
     @Override
-    public GeneratorResult<T> generateClass(final CodecClassLoader loader, final String fqcn,
+    public GeneratorResult<T> generateClass(final BindingClassLoader loader, final String fqcn,
             final Class<?> bindingInterface) {
         LOG.trace("Definining streamer {}", fqcn);
 
@@ -247,7 +247,7 @@ final class DataObjectStreamerGenerator<T extends DataObjectStreamer<?>> impleme
         return result;
     }
 
-    private ChildStream createStream(final CodecClassLoader loader, final ImmutableMap<String, Type> props,
+    private ChildStream createStream(final BindingClassLoader loader, final ImmutableMap<String, Type> props,
             final DataSchemaNode childSchema, final Method getter) {
         if (childSchema instanceof LeafSchemaNode) {
             return qnameChildStream(STREAM_LEAF, getter, childSchema);
@@ -379,7 +379,7 @@ final class DataObjectStreamerGenerator<T extends DataObjectStreamer<?>> impleme
         }
     }
 
-    private static Class<?> loadTypeClass(final CodecClassLoader loader, final Type type) {
+    private static Class<?> loadTypeClass(final BindingClassLoader loader, final Type type) {
         try {
             return loader.loadClass(type.getFullyQualifiedName());
         } catch (ClassNotFoundException e) {
index f4ac5d8c63d4fffcb7cabe30055fa6f658f553a1..de20d86e6179b67d2ea85f2bc54d6d11b5ebac21 100644 (file)
@@ -13,7 +13,7 @@ import java.util.List;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
 import org.opendaylight.mdsal.binding.runtime.api.ListRuntimeType;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -80,7 +80,7 @@ abstract class NodeCodecContext implements BindingCodecTreeNode {
          *
          * @return A codec loader instance
          */
-        @NonNull CodecClassLoader getLoader();
+        @NonNull BindingClassLoader getLoader();
 
         @NonNull DataObjectStreamer<?> getDataObjectSerializer(Class<?> type);
 
index 2319a14e8912aedd12a62db8bb5adbdde51103d3..af0071b99d378976f135766f634a67d00e32e0c8 100644 (file)
@@ -34,7 +34,7 @@ import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
 import net.bytebuddy.jar.asm.Opcodes;
 import net.bytebuddy.matcher.ElementMatchers;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader.GeneratorResult;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader.GeneratorResult;
 import org.opendaylight.mdsal.binding.runtime.api.NotificationRuntimeType;
 import org.opendaylight.yangtools.yang.binding.BaseNotification;
 import org.opendaylight.yangtools.yang.binding.DataObject;
index daf33df1201dc97a7439fb65620e44e52f3972cd..fd4ac8a7eac759a25736cc981552947b7adcd596 100644 (file)
@@ -21,8 +21,8 @@ import net.bytebuddy.dynamic.DynamicType.Builder;
 import net.bytebuddy.jar.asm.Opcodes;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingOpaqueObjectCodecTreeNode;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader;
-import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader.GeneratorResult;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader.GeneratorResult;
 import org.opendaylight.yangtools.yang.binding.OpaqueData;
 import org.opendaylight.yangtools.yang.binding.OpaqueObject;
 import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
@@ -38,7 +38,7 @@ abstract class OpaqueNodeCodecContext<T extends OpaqueObject<T>> extends ValueNo
         implements BindingOpaqueObjectCodecTreeNode<T> {
     static final class Anyxml<T extends OpaqueObject<T>> extends OpaqueNodeCodecContext<T> {
         Anyxml(final AnyxmlSchemaNode schema, final String getterName, final Class<T> bindingClass,
-                final CodecClassLoader loader) {
+                final BindingClassLoader loader) {
             super(schema, getterName, bindingClass, loader);
         }
 
@@ -60,7 +60,7 @@ abstract class OpaqueNodeCodecContext<T extends OpaqueObject<T>> extends ValueNo
 
     static final class Anydata<T extends OpaqueObject<T>> extends OpaqueNodeCodecContext<T> {
         Anydata(final AnydataSchemaNode schema, final String getterName, final Class<T> bindingClass,
-                final CodecClassLoader loader) {
+                final BindingClassLoader loader) {
             super(schema, getterName, bindingClass, loader);
         }
 
@@ -103,7 +103,7 @@ abstract class OpaqueNodeCodecContext<T extends OpaqueObject<T>> extends ValueNo
     private final @NonNull Class<T> bindingClass;
 
     OpaqueNodeCodecContext(final DataSchemaNode schema, final String getterName, final Class<T> bindingClass,
-            final CodecClassLoader loader) {
+            final BindingClassLoader loader) {
         super(schema, getterName, null);
         this.bindingClass = requireNonNull(bindingClass);
         proxyConstructor = createImpl(loader, bindingClass);
@@ -154,7 +154,7 @@ abstract class OpaqueNodeCodecContext<T extends OpaqueObject<T>> extends ValueNo
         }
     }
 
-    private static MethodHandle createImpl(final CodecClassLoader rootLoader, final Class<?> bindingClass) {
+    private static MethodHandle createImpl(final BindingClassLoader rootLoader, final Class<?> bindingClass) {
         final Class<?> proxyClass = rootLoader.generateClass(bindingClass, "codecImpl",
             (loader, fqcn, bindingInterface) -> GeneratorResult.of(TEMPLATE
                 .name(fqcn)
diff --git a/binding/mdsal-binding-loader/pom.xml b/binding/mdsal-binding-loader/pom.xml
new file mode 100644 (file)
index 0000000..1047acd
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2022 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.mdsal</groupId>
+        <artifactId>dom-parent</artifactId>
+        <version>11.0.0-SNAPSHOT</version>
+        <relativePath>../../dom/dom-parent</relativePath>
+    </parent>
+
+    <artifactId>mdsal-binding-loader</artifactId>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.bytebuddy</groupId>
+            <artifactId>byte-buddy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>yang-binding</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/binding/mdsal-binding-loader/src/main/java/module-info.java b/binding/mdsal-binding-loader/src/main/java/module-info.java
new file mode 100644 (file)
index 0000000..cf9efd1
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2022 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
+ */
+
+/**
+ * A ClassLoader for binding constructs. It allows loading of runtime-generated classes with a combined visibility with
+ * a set of parent class loaders.
+ */
+module org.opendaylight.mdsal.binding.loader {
+    exports org.opendaylight.mdsal.binding.loader;
+
+    requires transitive net.bytebuddy;
+
+    requires com.google.common;
+    requires org.opendaylight.yangtools.yang.binding;
+    requires org.slf4j;
+
+    // Annotations
+    requires static transitive org.eclipse.jdt.annotation;
+}
@@ -5,15 +5,12 @@
  * 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.loader;
+package org.opendaylight.mdsal.binding.loader;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Verify.verify;
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.annotations.Beta;
-import com.google.common.base.Strings;
-import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import java.io.File;
@@ -22,12 +19,12 @@ import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.Map.Entry;
 import java.util.Set;
-import net.bytebuddy.description.type.TypeDescription;
+import java.util.function.Supplier;
 import net.bytebuddy.dynamic.DynamicType.Unloaded;
 import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,11 +42,14 @@ import org.slf4j.LoggerFactory;
  *
  * <p>In single-classloader environments, obviously, the root loader can load all binding classes, and hence no leaf
  * loader is created.
- *
- * @author Robert Varga
  */
-@Beta
-public abstract class CodecClassLoader extends ClassLoader {
+public abstract sealed class BindingClassLoader extends ClassLoader
+        permits LeafBindingClassLoader, RootBindingClassLoader {
+    /**
+     * A class generator, generating a class of a particular type.
+     *
+     * @param <T> Type of generated class
+     */
     public interface ClassGenerator<T> {
         /**
          * Generate a class.
@@ -58,7 +58,7 @@ public abstract class CodecClassLoader extends ClassLoader {
          * @param bindingInterface Binding interface for which the class is being generated
          * @return A result.
          */
-        GeneratorResult<T> generateClass(CodecClassLoader loader, String fqcn, Class<?> bindingInterface);
+        GeneratorResult<T> generateClass(BindingClassLoader loader, String fqcn, Class<?> bindingInterface);
 
         /**
          * Run the specified loader in a customized environment. The environment customizations must be cleaned up by
@@ -72,6 +72,11 @@ public abstract class CodecClassLoader extends ClassLoader {
         }
     }
 
+    /**
+     * Result of class generation.
+     *
+     * @param <T> Type of generated class.
+     */
     public static final class GeneratorResult<T> {
         private final @NonNull ImmutableSet<Class<?>> dependecies;
         private final @NonNull Unloaded<T> result;
@@ -88,7 +93,7 @@ public abstract class CodecClassLoader extends ClassLoader {
         public static <T> @NonNull GeneratorResult<T> of(final Unloaded<T> result,
                 final Collection<Class<?>> dependencies) {
             return dependencies.isEmpty() ? of(result) : new GeneratorResult<>(result,
-                    ImmutableSet.copyOf(dependencies));
+                ImmutableSet.copyOf(dependencies));
         }
 
         @NonNull Unloaded<T> getResult() {
@@ -100,9 +105,9 @@ public abstract class CodecClassLoader extends ClassLoader {
         }
     }
 
-    private static final ClassLoadingStrategy<CodecClassLoader> STRATEGY = (classLoader, types) -> {
+    private static final ClassLoadingStrategy<BindingClassLoader> STRATEGY = (classLoader, types) -> {
         verify(types.size() == 1, "Unexpected multiple types", types);
-        final Entry<TypeDescription, byte[]> entry = types.entrySet().iterator().next();
+        final var entry = types.entrySet().iterator().next();
         return ImmutableMap.of(entry.getKey(), classLoader.loadClass(entry.getKey().getName(), entry.getValue()));
     };
 
@@ -110,45 +115,52 @@ public abstract class CodecClassLoader extends ClassLoader {
         verify(ClassLoader.registerAsParallelCapable());
     }
 
-    private static final Logger LOG = LoggerFactory.getLogger(CodecClassLoader.class);
-    private static final File BYTECODE_DIRECTORY;
+    private static final Logger LOG = LoggerFactory.getLogger(BindingClassLoader.class);
 
-    static {
-        final String dir = System.getProperty("org.opendaylight.mdsal.binding.dom.codec.loader.bytecodeDumpDirectory");
-        BYTECODE_DIRECTORY = Strings.isNullOrEmpty(dir) ? null : new File(dir);
-    }
+    private final @Nullable File dumpDir;
 
-    CodecClassLoader(final ClassLoader parentLoader) {
+    BindingClassLoader(final ClassLoader parentLoader, final @Nullable File dumpDir) {
         super(parentLoader);
+        this.dumpDir = dumpDir;
+    }
+
+    BindingClassLoader(final BindingClassLoader parentLoader) {
+        this(parentLoader, parentLoader.dumpDir);
     }
 
     /**
-     * Instantiate a new CodecClassLoader, which serves as the root of generated code loading.
+     * Instantiate a new BindingClassLoader, which serves as the root of generated code loading.
      *
-     * @return A new CodecClassLoader.
+     * @param rootClass Class from which to derive the class loader
+     * @param dumpDir Directory in which to dump loaded bytecode
+     * @return A new BindingClassLoader.
+     * @throws NullPointerException if {@code parentLoader} is {@code null}
      */
-    public static @NonNull CodecClassLoader create() {
-        return AccessController.doPrivileged((PrivilegedAction<CodecClassLoader>)() -> new RootCodecClassLoader());
+    public static @NonNull BindingClassLoader create(final Class<?> rootClass, final @Nullable File dumpDir) {
+        final var parentLoader = rootClass.getClassLoader();
+        return AccessController.doPrivileged(
+            (PrivilegedAction<BindingClassLoader>)() -> new RootBindingClassLoader(parentLoader, dumpDir));
     }
 
     /**
      * The name of the target class is formed through concatenation of the name of a {@code bindingInterface} and
      * specified {@code suffix}.
      *
+     * @param <T> Type of generated class
      * @param bindingInterface Binding compile-time-generated interface
      * @param suffix Suffix to use
      * @param generator Code generator to run
      * @return A generated class object
      * @throws NullPointerException if any argument is null
      */
-    public final <T> Class<T> generateClass(final Class<?> bindingInterface,
-            final String suffix, final ClassGenerator<T> generator)  {
+    public final <T> Class<T> generateClass(final Class<?> bindingInterface, final String suffix,
+            final ClassGenerator<T> generator)  {
         return findClassLoader(requireNonNull(bindingInterface)).doGenerateClass(bindingInterface, suffix, generator);
     }
 
     public final @NonNull Class<?> getGeneratedClass(final Class<?> bindingInterface, final String suffix) {
-        final CodecClassLoader loader = findClassLoader(requireNonNull(bindingInterface));
-        final String fqcn = generatedClassName(bindingInterface, suffix);
+        final var loader = findClassLoader(requireNonNull(bindingInterface));
+        final var fqcn = generatedClassName(bindingInterface, suffix);
 
         final Class<?> ret;
         synchronized (loader.getClassLoadingLock(fqcn)) {
@@ -167,7 +179,7 @@ public abstract class CodecClassLoader extends ClassLoader {
      * @param newLoaders Loaders to append
      * @throws NullPointerException if {@code loaders} is null
      */
-    abstract void appendLoaders(@NonNull Set<LeafCodecClassLoader> newLoaders);
+    abstract void appendLoaders(@NonNull Set<LeafBindingClassLoader> newLoaders);
 
     /**
      * Find the loader responsible for holding classes related to a binding class.
@@ -176,21 +188,21 @@ public abstract class CodecClassLoader extends ClassLoader {
      * @return a Loader instance
      * @throws NullPointerException if {@code bindingClass} is null
      */
-    abstract @NonNull CodecClassLoader findClassLoader(@NonNull Class<?> bindingClass);
+    abstract @NonNull BindingClassLoader findClassLoader(@NonNull Class<?> bindingClass);
 
     private <T> Class<T> doGenerateClass(final Class<?> bindingInterface, final String suffix,
             final ClassGenerator<T> generator)  {
-        final String fqcn = generatedClassName(bindingInterface, suffix);
+        final var fqcn = generatedClassName(bindingInterface, suffix);
 
         synchronized (getClassLoadingLock(fqcn)) {
             // Attempt to find a loaded class
-            final Class<?> existing = findLoadedClass(fqcn);
+            final var existing = findLoadedClass(fqcn);
             if (existing != null) {
                 return (Class<T>) existing;
             }
 
-            final GeneratorResult<T> result = generator.generateClass(this, fqcn, bindingInterface);
-            final Unloaded<T> unloaded = result.getResult();
+            final var result = generator.generateClass(this, fqcn, bindingInterface);
+            final var unloaded = result.getResult();
             verify(fqcn.equals(unloaded.getTypeDescription().getName()), "Unexpected class in %s", unloaded);
             verify(unloaded.getAuxiliaryTypes().isEmpty(), "Auxiliary types present in %s", unloaded);
             dumpBytecode(unloaded);
@@ -202,18 +214,18 @@ public abstract class CodecClassLoader extends ClassLoader {
 
     final Class<?> loadClass(final String fqcn, final byte[] byteCode) {
         synchronized (getClassLoadingLock(fqcn)) {
-            final Class<?> existing = findLoadedClass(fqcn);
+            final var existing = findLoadedClass(fqcn);
             verify(existing == null, "Attempted to load existing %s", existing);
             return defineClass(fqcn, byteCode, 0, byteCode.length);
         }
     }
 
     private void processDependencies(final Collection<Class<?>> deps) {
-        final Set<LeafCodecClassLoader> depLoaders = new HashSet<>();
-        for (Class<?> dep : deps) {
-            final ClassLoader depLoader = dep.getClassLoader();
-            verify(depLoader instanceof CodecClassLoader, "Dependency %s is not a generated class", dep);
-            if (this.equals(depLoader)) {
+        final var depLoaders = new HashSet<LeafBindingClassLoader>();
+        for (var dep : deps) {
+            final var depLoader = dep.getClassLoader();
+            verify(depLoader instanceof BindingClassLoader, "Dependency %s is not a generated class", dep);
+            if (equals(depLoader)) {
                 // Same loader, skip
                 continue;
             }
@@ -223,8 +235,8 @@ public abstract class CodecClassLoader extends ClassLoader {
             } catch (ClassNotFoundException e) {
                 LOG.debug("Cannot find {} in local loader, attempting to compensate", dep, e);
                 // Root loader is always visible from a leaf, hence the dependency can only be a leaf
-                verify(depLoader instanceof LeafCodecClassLoader, "Dependency loader %s is not a leaf", depLoader);
-                depLoaders.add((LeafCodecClassLoader) depLoader);
+                verify(depLoader instanceof LeafBindingClassLoader, "Dependency loader %s is not a leaf", depLoader);
+                depLoaders.add((LeafBindingClassLoader) depLoader);
             }
         }
 
@@ -233,10 +245,11 @@ public abstract class CodecClassLoader extends ClassLoader {
         }
     }
 
-    private static void dumpBytecode(final Unloaded<?> unloaded) {
-        if (BYTECODE_DIRECTORY != null) {
+    private void dumpBytecode(final Unloaded<?> unloaded) {
+        final var dir = dumpDir;
+        if (dir != null) {
             try {
-                unloaded.saveIn(BYTECODE_DIRECTORY);
+                unloaded.saveIn(dir);
             } catch (IOException | IllegalArgumentException e) {
                 LOG.info("Failed to save {}", unloaded.getTypeDescription().getName(), e);
             }
@@ -5,14 +5,13 @@
  * 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.loader;
+package org.opendaylight.mdsal.binding.loader;
 
 import static com.google.common.base.Verify.verify;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableSet;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import org.eclipse.jdt.annotation.NonNull;
@@ -20,22 +19,22 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 // A leaf class loader, binding together a root class loader and some other class loader
-final class LeafCodecClassLoader extends CodecClassLoader {
+final class LeafBindingClassLoader extends BindingClassLoader {
     static {
         verify(registerAsParallelCapable());
     }
 
-    private static final Logger LOG = LoggerFactory.getLogger(LeafCodecClassLoader.class);
+    private static final Logger LOG = LoggerFactory.getLogger(LeafBindingClassLoader.class);
 
-    private final @NonNull RootCodecClassLoader root;
+    private final @NonNull RootBindingClassLoader root;
     private final @NonNull ClassLoader target;
 
     @SuppressWarnings("rawtypes")
-    private static final AtomicReferenceFieldUpdater<LeafCodecClassLoader, ImmutableSet> DEPENDENCIES_UPDATER =
-            AtomicReferenceFieldUpdater.newUpdater(LeafCodecClassLoader.class, ImmutableSet.class, "dependencies");
-    private volatile ImmutableSet<LeafCodecClassLoader> dependencies = ImmutableSet.of();
+    private static final AtomicReferenceFieldUpdater<LeafBindingClassLoader, ImmutableSet> DEPENDENCIES_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(LeafBindingClassLoader.class, ImmutableSet.class, "dependencies");
+    private volatile ImmutableSet<LeafBindingClassLoader> dependencies = ImmutableSet.of();
 
-    LeafCodecClassLoader(final RootCodecClassLoader root, final ClassLoader target) {
+    LeafBindingClassLoader(final RootBindingClassLoader root, final ClassLoader target) {
         super(root);
         this.root = requireNonNull(root);
         this.target = requireNonNull(target);
@@ -47,10 +46,10 @@ final class LeafCodecClassLoader extends CodecClassLoader {
             return target.loadClass(name);
         } catch (ClassNotFoundException e) {
             LOG.trace("Class {} not found in target, looking through dependencies", name);
-            for (LeafCodecClassLoader loader : dependencies) {
+            for (LeafBindingClassLoader loader : dependencies) {
                 // Careful: a loading operation may be underway, make sure that process has completed
                 synchronized (loader.getClassLoadingLock(name)) {
-                    final Class<?> loaded = loader.findLoadedClass(name);
+                    final var loaded = loader.findLoadedClass(name);
                     if (loaded != null) {
                         LOG.trace("Class {} found in dependency {}", name, loader);
                         return loaded;
@@ -63,19 +62,18 @@ final class LeafCodecClassLoader extends CodecClassLoader {
     }
 
     @Override
-    CodecClassLoader findClassLoader(final Class<?> bindingClass) {
-        final ClassLoader bindingTarget = bindingClass.getClassLoader();
-        return target.equals(bindingTarget) ? this : root.findClassLoader(bindingClass);
+    BindingClassLoader findClassLoader(final Class<?> bindingClass) {
+        return target.equals(bindingClass.getClassLoader()) ? this : root.findClassLoader(bindingClass);
     }
 
     @Override
-    void appendLoaders(final Set<LeafCodecClassLoader> newLoaders) {
+    void appendLoaders(final Set<LeafBindingClassLoader> newLoaders) {
         while (true) {
-            final ImmutableSet<LeafCodecClassLoader> local = dependencies;
-            final List<LeafCodecClassLoader> builder = new ArrayList<>(local.size() + newLoaders.size());
+            final var local = dependencies;
+            final var builder = new ArrayList<LeafBindingClassLoader>(local.size() + newLoaders.size());
             builder.addAll(local);
             builder.addAll(newLoaders);
-            final ImmutableSet<LeafCodecClassLoader> updated = ImmutableSet.copyOf(builder);
+            final var updated = ImmutableSet.copyOf(builder);
             if (local.equals(updated) || DEPENDENCIES_UPDATER.compareAndSet(this, local, updated)) {
                 // No need for an update or the update was successful
                 return;
@@ -5,70 +5,68 @@
  * 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.loader;
+package org.opendaylight.mdsal.binding.loader;
 
 import static com.google.common.base.Verify.verify;
-import static com.google.common.base.Verify.verifyNotNull;
 
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMap.Builder;
+import java.io.File;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 // A root codec classloader, binding only whatever is available StaticClassPool
-final class RootCodecClassLoader extends CodecClassLoader {
-    private static final Logger LOG = LoggerFactory.getLogger(RootCodecClassLoader.class);
-    private static final ClassLoader LOADER = verifyNotNull(RootCodecClassLoader.class.getClassLoader());
+final class RootBindingClassLoader extends BindingClassLoader {
+    private static final Logger LOG = LoggerFactory.getLogger(RootBindingClassLoader.class);
 
     static {
         verify(registerAsParallelCapable());
     }
 
     @SuppressWarnings("rawtypes")
-    private static final AtomicReferenceFieldUpdater<RootCodecClassLoader, ImmutableMap> LOADERS_UPDATER =
-            AtomicReferenceFieldUpdater.newUpdater(RootCodecClassLoader.class, ImmutableMap.class, "loaders");
+    private static final AtomicReferenceFieldUpdater<RootBindingClassLoader, ImmutableMap> LOADERS_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(RootBindingClassLoader.class, ImmutableMap.class, "loaders");
 
-    private volatile ImmutableMap<ClassLoader, CodecClassLoader> loaders = ImmutableMap.of();
+    private volatile ImmutableMap<ClassLoader, BindingClassLoader> loaders = ImmutableMap.of();
 
-    RootCodecClassLoader() {
-        super(LOADER);
+    RootBindingClassLoader(final ClassLoader parentLoader, final @Nullable File dumpDir) {
+        super(parentLoader, dumpDir);
     }
 
     @Override
-    CodecClassLoader findClassLoader(final Class<?> bindingClass) {
-        final ClassLoader target = bindingClass.getClassLoader();
+    BindingClassLoader findClassLoader(final Class<?> bindingClass) {
+        final var target = bindingClass.getClassLoader();
         if (target == null) {
             // No class loader associated ... well, let's use root then
             return this;
         }
 
         // Cache for update
-        ImmutableMap<ClassLoader, CodecClassLoader> local = loaders;
-        final CodecClassLoader known = local.get(target);
+        var local = loaders;
+        final var known = local.get(target);
         if (known != null) {
             return known;
         }
 
         // Alright, we need to determine if the class is accessible through our hierarchy (in which case we use
         // ourselves) or we need to create a new Leaf.
-        final CodecClassLoader found;
+        final BindingClassLoader found;
         if (!isOurClass(bindingClass)) {
             verifyStaticLinkage(target);
             found = AccessController.doPrivileged(
-                (PrivilegedAction<CodecClassLoader>)() -> new LeafCodecClassLoader(this, target));
+                (PrivilegedAction<BindingClassLoader>)() -> new LeafBindingClassLoader(this, target));
         } else {
             found = this;
         }
 
         // Now make sure we cache this result
         while (true) {
-            final Builder<ClassLoader, CodecClassLoader> builder = ImmutableMap.builderWithExpectedSize(
-                local.size() + 1);
+            final var builder = ImmutableMap.<ClassLoader, BindingClassLoader>builderWithExpectedSize(local.size() + 1);
             builder.putAll(local);
             builder.put(target, found);
 
@@ -77,7 +75,7 @@ final class RootCodecClassLoader extends CodecClassLoader {
             }
 
             local = loaders;
-            final CodecClassLoader recheck = local.get(target);
+            final var recheck = local.get(target);
             if (recheck != null) {
                 return recheck;
             }
@@ -85,7 +83,7 @@ final class RootCodecClassLoader extends CodecClassLoader {
     }
 
     @Override
-    void appendLoaders(final Set<LeafCodecClassLoader> newLoaders) {
+    void appendLoaders(final Set<LeafBindingClassLoader> newLoaders) {
         // Root loader should never see the requirement for other loaders, as that would violate loop-free nature
         // of generated code: if a binding class is hosted in root loader, all its references must be visible from
         // the root loader and hence all the generated code ends up residing in the root loader, too.
similarity index 54%
rename from binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/loader/package-info.java
rename to binding/mdsal-binding-loader/src/main/java/org/opendaylight/mdsal/binding/loader/package-info.java
index ced5ac7ea9cb4d698be2f42c6e8ca766d9758931..12137d872922a17386514c447a34b789b2f0b8d6 100644 (file)
@@ -6,12 +6,12 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 /**
- * {@link java.lang.ClassLoader} support for Binding/DOM codec translation code generators. This package provides one
- * core class, {@link CodecClassLoader}, which allows lookup of compile-time-generated Binding classes for the purpose
- * of referencing them within code generators and which serves as the ClassLoader holding runtime-generated codecs.
+ * {@link java.lang.ClassLoader} support for Binding-related runtime-loaded code. This package provides one core class,
+ * {@link BindingClassLoader}, which allows lookup of compile-time-generated Binding classes for the purpose of
+ * referencing them within code generators and which serves as the ClassLoader holding runtime-generated codecs.
  *
  * <p>
  * While the interfaces and classes in this package may be publicly accessible, they are an implementation detail and
  * may change incompatibly at any time.
  */
-package org.opendaylight.mdsal.binding.dom.codec.impl.loader;
\ No newline at end of file
+package org.opendaylight.mdsal.binding.loader;
\ No newline at end of file
index 6eaff60ea99fdb8ce2ad90d780d6738086414fca..23aa86e48be07701261169b897ab3deb017626ec 100644 (file)
@@ -33,6 +33,7 @@
         <module>binding-parent</module>
         <!-- FIXME: rename to mdsal-binding-spec-api -->
         <module>yang-binding</module>
+        <module>mdsal-binding-loader</module>
         <module>mdsal-binding-spec-util</module>
 
         <!-- Binding Runtime Services, dealing with class/schema mapping -->
index 3798398ee63dc03bcabb1578a5e5aac954632202..a52b6b4814ae036ee7748ea72629c6832e5376b6 100644 (file)
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-dom-inmemory-datastore</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.mdsal</groupId>
+            <artifactId>mdsal-binding-loader</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.mdsal</groupId>
             <artifactId>mdsal-binding-model-api</artifactId>