<artifactId>mdsal-binding-runtime-spi</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-params</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
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",
+ return CodecPackage.CODEC.generateClass(loader, bindingInterface,
new Reusable<>(BB_CDO, simpleProperties, daoProperties, keyMethod));
}
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",
+ return CodecPackage.CODEC.generateClass(loader, bindingInterface,
new Reusable<>(BB_ACDO, simpleProperties, daoProperties, keyMethod));
}
--- /dev/null
+/*
+ * 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
+ */
+package org.opendaylight.mdsal.binding.dom.codec.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader.ClassGenerator;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
+
+/**
+ * Centralized registry of Java package names used by classes generated by codec components.
+ */
+enum CodecPackage {
+ /**
+ * Package holding {@link CodecDataObject}s, {@link CodecOpaqueObject}s and similar.
+ */
+ CODEC("org.opendaylight.yang.rt.v1.obj"),
+ /**
+ * Package holding {@link DataObjectStreamer}s.
+ */
+ STREAMER("org.opendaylight.yang.rt.v1.stream"),
+ /**
+ * Package holding @link EventInstantAware} specializations of {@code notification} objects.
+ */
+ EVENT_AWARE("org.opendaylight.yang.rt.v1.eia");
+
+ private static final int PACKAGE_PREFIX_LENGTH = BindingMapping.PACKAGE_PREFIX.length();
+
+ private String packagePrefix;
+
+ CodecPackage(final String packagePrefix) {
+ this.packagePrefix = requireNonNull(packagePrefix);
+ }
+
+ <T> @NonNull Class<T> generateClass(final BindingClassLoader loader, final Class<?> bindingInterface,
+ final ClassGenerator<T> generator) {
+ return loader.generateClass(bindingInterface, createFQCN(bindingInterface), generator);
+ }
+
+ @NonNull Class<?> getGeneratedClass(final BindingClassLoader loader, final Class<?> bindingInterface) {
+ return loader.getGeneratedClass(bindingInterface, createFQCN(bindingInterface));
+ }
+
+ private @NonNull String createFQCN(final Class<?> bindingInterface) {
+ final var ifName = bindingInterface.getName();
+ checkArgument(ifName.startsWith(BindingMapping.PACKAGE_PREFIX), "Unrecognized interface %s", bindingInterface);
+ return packagePrefix + ifName.substring(PACKAGE_PREFIX_LENGTH);
+ }
+}
throw new UnsupportedOperationException("Schema type " + schema.getClass() + " is not supported");
}
- return loader.generateClass(type, "streamer",
+ return CodecPackage.STREAMER.generateClass(loader, type,
// FIXME: cast to GeneratedType: we really should adjust getTypeWithSchema()
new DataObjectStreamerGenerator<>(registry, (GeneratedType) typeAndSchema.javaType(),
(DataNodeContainer) schema, type, startEvent));
super(DataContainerCodecPrototype.from(key, schema, factory));
final Class<D> bindingClass = getBindingClass();
- final Class<?> awareClass = factory().getLoader().generateClass(bindingClass, "eventInstantAware",
+ final Class<?> awareClass = CodecPackage.EVENT_AWARE.generateClass(factory().getLoader(), bindingClass,
(loader, fqcn, bindingInterface) -> {
- final Class<?> codecImpl = loader.getGeneratedClass(bindingClass, "codecImpl");
+ final Class<?> codecImpl = CodecPackage.CODEC.getGeneratedClass(loader, bindingClass);
return GeneratorResult.of(new ByteBuddy()
.subclass(codecImpl, ConstructorStrategy.Default.NO_CONSTRUCTORS)
}
private static MethodHandle createImpl(final BindingClassLoader rootLoader, final Class<?> bindingClass) {
- final Class<?> proxyClass = rootLoader.generateClass(bindingClass, "codecImpl",
+ final Class<?> proxyClass = CodecPackage.CODEC.generateClass(rootLoader, bindingClass,
(loader, fqcn, bindingInterface) -> GeneratorResult.of(TEMPLATE
.name(fqcn)
.implement(bindingInterface)
--- /dev/null
+/*
+ * Copyright (c) 2022 PANTHEON.tech s.r.o. 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 org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.stream.Stream;
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.implementation.FixedValue;
+import net.bytebuddy.matcher.ElementMatchers;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader;
+import org.opendaylight.mdsal.binding.loader.BindingClassLoader.GeneratorResult;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.yang.union.test.rev220428.Top;
+
+public class CodecClassLoaderTest {
+
+ private final BindingClassLoader codecClassLoader = BindingClassLoader.create(CodecClassLoaderTest.class, null);
+
+ @ParameterizedTest(name = "Generate class within namespace: {0}")
+ @MethodSource("generateClassWithinNamespaceArgs")
+ void generateClassWithinNamespace(final CodecPackage pkg, final String expectedClassName) {
+ final Class<?> generated = pkg.generateClass(codecClassLoader, Top.class,
+ (loader, fqcn, bindingInterface) -> GeneratorResult.of(new ByteBuddy()
+ .subclass(Object.class)
+ .name(fqcn)
+ .method(ElementMatchers.isToString())
+ .intercept(FixedValue.value("test"))
+ .make()));
+ assertNotNull(generated);
+ assertEquals(expectedClassName, generated.getName());
+
+ final Class<?> stored = pkg.getGeneratedClass(codecClassLoader, Top.class);
+ assertEquals(generated, stored);
+ }
+
+ private static Stream<Arguments> generateClassWithinNamespaceArgs() {
+ final String common = "urn.opendaylight.yang.union.test.rev220428.Top";
+ return Stream.of(
+ Arguments.of(CodecPackage.CODEC, "org.opendaylight.yang.rt.v1.obj." + common),
+ Arguments.of(CodecPackage.STREAMER, "org.opendaylight.yang.rt.v1.stream." + common),
+ Arguments.of(CodecPackage.EVENT_AWARE, "org.opendaylight.yang.rt.v1.eia." + common)
+ );
+ }
+}
}
/**
- * The name of the target class is formed through concatenation of the name of a {@code bindingInterface} and
- * specified {@code suffix}.
+ * Generate a class which is related to specified compile-type-generated interface.
*
* @param <T> Type of generated class
* @param bindingInterface Binding compile-time-generated interface
- * @param suffix Suffix to use
+ * @param fqcn Fully-Qualified Class Name of the generated class
* @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,
+ public final <T> @NonNull Class<T> generateClass(final Class<?> bindingInterface, final String fqcn,
final ClassGenerator<T> generator) {
- return findClassLoader(requireNonNull(bindingInterface)).doGenerateClass(bindingInterface, suffix, generator);
+ return findClassLoader(requireNonNull(bindingInterface)).doGenerateClass(bindingInterface, fqcn, generator);
}
- public final @NonNull Class<?> getGeneratedClass(final Class<?> bindingInterface, final String suffix) {
+ public final @NonNull Class<?> getGeneratedClass(final Class<?> bindingInterface, final String fqcn) {
final var loader = findClassLoader(requireNonNull(bindingInterface));
- final var fqcn = generatedClassName(bindingInterface, suffix);
-
final Class<?> ret;
synchronized (loader.getClassLoadingLock(fqcn)) {
ret = loader.findLoadedClass(fqcn);
}
- checkArgument(ret != null, "Failed to find generated class %s for %s of %s", fqcn, suffix, bindingInterface);
+ checkArgument(ret != null, "Failed to find generated class %s for %s", fqcn, bindingInterface);
return ret;
}
*/
abstract @NonNull BindingClassLoader findClassLoader(@NonNull Class<?> bindingClass);
- private <T> Class<T> doGenerateClass(final Class<?> bindingInterface, final String suffix,
+ private <T> @NonNull Class<T> doGenerateClass(final Class<?> bindingInterface, final String fqcn,
final ClassGenerator<T> generator) {
- final var fqcn = generatedClassName(bindingInterface, suffix);
-
synchronized (getClassLoadingLock(fqcn)) {
// Attempt to find a loaded class
final var existing = findLoadedClass(fqcn);
}
}
}
-
- private static String generatedClassName(final Class<?> bindingInterface, final String suffix) {
- return bindingInterface.getName() + "$$$" + suffix;
- }
}