import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
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.StaticClassPool;
import org.opendaylight.mdsal.binding.dom.codec.util.BindingSchemaMapping;
import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
import org.opendaylight.mdsal.binding.model.api.GeneratedType;
final class BindingCodecContext implements CodecContextFactory, BindingCodecTree, Immutable {
private static final Logger LOG = LoggerFactory.getLogger(BindingCodecContext.class);
+ private final CodecClassLoader loader = StaticClassPool.createLoader();
private final InstanceIdentifierCodec instanceIdentifierCodec;
private final IdentityCodec identityCodec;
private final BindingNormalizedNodeCodecRegistry registry;
final Class<?> valueType = method.getReturnType();
verify(OpaqueObject.class.isAssignableFrom(valueType), "Illegal value type %s", valueType);
valueNode = new OpaqueNodeCodecContext.AnyXml<>((AnyXmlSchemaNode) schema, method,
- valueType.asSubclass(OpaqueObject.class));
+ valueType.asSubclass(OpaqueObject.class), loader);
} else {
verify(schema == null, "Unhandled schema %s for method %s", schema, method);
// We do not have schema for leaf, so we will ignore it (e.g. getClass).
} else if (typeDef instanceof InstanceIdentifierTypeDefinition) {
return ValueTypeCodec.encapsulatedValueCodecFor(valueType, typeDef, instanceIdentifierCodec);
} else if (typeDef instanceof UnionTypeDefinition) {
- final Callable<UnionTypeCodec> loader = UnionTypeCodec.loader(valueType, (UnionTypeDefinition) typeDef,
+ final Callable<UnionTypeCodec> unionLoader = UnionTypeCodec.loader(valueType, (UnionTypeDefinition) typeDef,
this);
try {
- return loader.call();
+ return unionLoader.call();
} catch (final Exception e) {
throw new IllegalStateException("Unable to load codec for " + valueType, e);
}
--- /dev/null
+/*
+ * 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 java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.binding.AbstractOpaqueObject;
+import org.opendaylight.yangtools.yang.binding.OpaqueData;
+import org.opendaylight.yangtools.yang.binding.OpaqueObject;
+
+/**
+ * A base class for {@link OpaqueObject}s backed by {@link ForeignOpaqueData}. While this class is public, it not part
+ * of API surface and is an implementation detail. The only reason for it being public is that it needs to be accessible
+ * by code generated at runtime.
+ *
+ * @param <T> OpaqueObject type
+ */
+@Beta
+public abstract class CodecOpaqueObject<T extends OpaqueObject<T>> extends AbstractOpaqueObject<T> {
+ private final @NonNull OpaqueData<?> value;
+
+ // This constructor is public so Javassist generates a public constructor and we do not need to muck
+ public CodecOpaqueObject(final OpaqueData<?> value) {
+ this.value = requireNonNull(value);
+ }
+
+ @Override
+ public final OpaqueData<?> getValue() {
+ return value;
+ }
+}
+++ /dev/null
-/*
- * 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 java.util.Objects.requireNonNull;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
-import org.opendaylight.yangtools.yang.binding.AbstractOpaqueObject;
-import org.opendaylight.yangtools.yang.binding.OpaqueData;
-import org.opendaylight.yangtools.yang.binding.OpaqueObject;
-
-/**
- * Proxy support for an OpaqueObject.
- *
- * @param <T> OpaqueObject type
- */
-// FIXME: MDSAL-442: this class has no run-time dependencies on other generated types, hence is a prime candidate for
-// becoming a prototype instantiated in a separate ClassLoader (see MDSAL-401) and not being a proxy at all.
-final class ForeignOpaqueObject<T extends OpaqueObject<T>> extends AbstractOpaqueObject<T>
- implements InvocationHandler {
- private final @NonNull Class<T> implementedInterface;
- private final @NonNull ForeignOpaqueData<?> value;
-
- ForeignOpaqueObject(final Class<T> implementedInterface, final ForeignOpaqueData<?> value) {
- this.implementedInterface = requireNonNull(implementedInterface);
- this.value = requireNonNull(value);
- }
-
- @Override
- public Class<T> implementedInterface() {
- return implementedInterface;
- }
-
- @Override
- public OpaqueData<?> getValue() {
- return value;
- }
-
- @Override
- public Object invoke(final Object proxy, final Method method, final Object[] args) {
- switch (method.getName()) {
- case "equals":
- return equals(args[0]);
- case "hashCode":
- return hashCode();
- case "getValue":
- return getValue();
- case "toString":
- return toString();
- case BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME:
- return implementedInterface;
- default:
- throw new NoSuchMethodError("Unknown method " + method);
- }
- }
-}
import static java.util.Objects.requireNonNull;
import com.google.common.base.Throwables;
+import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
-import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
+import javassist.CannotCompileException;
+import javassist.CtClass;
+import javassist.Modifier;
+import javassist.NotFoundException;
import javax.xml.transform.dom.DOMSource;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingOpaqueObjectCodecTreeNode;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
+import org.opendaylight.mdsal.binding.dom.codec.loader.StaticClassPool;
import org.opendaylight.yangtools.concepts.Codec;
import org.opendaylight.yangtools.yang.binding.OpaqueData;
import org.opendaylight.yangtools.yang.binding.OpaqueObject;
abstract class OpaqueNodeCodecContext<T extends OpaqueObject<T>> extends ValueNodeCodecContext
implements BindingOpaqueObjectCodecTreeNode<T> {
static final class AnyXml<T extends OpaqueObject<T>> extends OpaqueNodeCodecContext<T> {
- AnyXml(final AnyXmlSchemaNode schema, final Method getter, final Class<T> bindingClass) {
- super(schema, getter, bindingClass);
+ AnyXml(final AnyXmlSchemaNode schema, final Method getter, final Class<T> bindingClass,
+ final CodecClassLoader loader) {
+ super(schema, getter, bindingClass, loader);
}
@Override
}
}
- private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, InvocationHandler.class);
- private static final MethodType OPAQUEOBJECT_TYPE = MethodType.methodType(OpaqueObject.class,
- ForeignOpaqueObject.class);
+ private static final CtClass SUPERCLASS = StaticClassPool.findClass(CodecOpaqueObject.class);
+ private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(OpaqueObject.class,
+ OpaqueData.class);
private final Codec<Object, Object> valueCodec = new Codec<Object, Object>() {
@Override
private final MethodHandle proxyConstructor;
private final @NonNull Class<T> bindingClass;
- OpaqueNodeCodecContext(final DataSchemaNode schema, final Method getter, final Class<T> bindingClass) {
+ OpaqueNodeCodecContext(final DataSchemaNode schema, final Method getter, final Class<T> bindingClass,
+ final CodecClassLoader loader) {
super(schema, getter, null);
this.bindingClass = requireNonNull(bindingClass);
-
- final Class<?> proxyClass = Proxy.getProxyClass(bindingClass.getClassLoader(), bindingClass);
- try {
- proxyConstructor = MethodHandles.publicLookup().findConstructor(proxyClass, CONSTRUCTOR_TYPE)
- .asType(OPAQUEOBJECT_TYPE);
- } catch (NoSuchMethodException | IllegalAccessException e) {
- throw new IllegalStateException("Failed to find contructor for class " + proxyClass, e);
- }
+ proxyConstructor = createImpl(loader, bindingClass);
}
@Override
// Streaming cannot support anything but DOMSource-based AnyxmlNodes.
verify(foreignData instanceof AnyXmlNode, "Variable node %s not supported yet", foreignData);
- final ForeignOpaqueData<?> opaqueData = new ForeignOpaqueData<>(foreignData);
- return bindingClass.cast(createBindingProxy(new ForeignOpaqueObject<>(bindingClass, opaqueData)));
+ return bindingClass.cast(createBindingProxy(new ForeignOpaqueData<>(foreignData)));
}
@Override
abstract @NonNull ForeignDataNode<?, ?> serializedData(OpaqueData<?> opaqueData);
@SuppressWarnings("checkstyle:illegalCatch")
- private OpaqueObject<?> createBindingProxy(final ForeignOpaqueObject<?> handler) {
+ private OpaqueObject<?> createBindingProxy(final OpaqueData<?> data) {
try {
- return (OpaqueObject<?>) proxyConstructor.invokeExact(handler);
+ return (OpaqueObject<?>) proxyConstructor.invokeExact(data);
} catch (final Throwable e) {
Throwables.throwIfUnchecked(e);
throw new IllegalStateException(e);
}
}
+
+ private static MethodHandle createImpl(final CodecClassLoader rootLoader, final Class<?> bindingClass) {
+ final Class<?> proxyClass;
+ try {
+ proxyClass = rootLoader.generateSubclass(SUPERCLASS, bindingClass, "codecImpl",
+ (pool, binding, generated) -> {
+ generated.addInterface(binding);
+ generated.setModifiers(Modifier.PUBLIC | Modifier.FINAL);
+ });
+ } catch (CannotCompileException | IOException | NotFoundException e) {
+ throw new LinkageError("Failed to instantiate prototype for " + bindingClass, e);
+ }
+
+ Constructor<?> ctor;
+ try {
+ ctor = proxyClass.getDeclaredConstructor(OpaqueData.class);
+ } catch (NoSuchMethodException e) {
+ throw new LinkageError("Failed to acquire constructor for prototype " + proxyClass, e);
+ }
+ try {
+ return MethodHandles.publicLookup().unreflectConstructor(ctor).asType(CONSTRUCTOR_TYPE);
+ } catch (IllegalAccessException e) {
+ throw new LinkageError("Failed to access constructor for prototype " + proxyClass, e);
+ }
+ }
}