import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
+import com.google.common.annotations.Beta;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
-import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import org.opendaylight.mdsal.binding.generator.api.ClassLoadingStrategy;
import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
import org.opendaylight.mdsal.binding.model.api.Type;
-import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.util.ClassLoaderUtils;
import org.opendaylight.yangtools.yang.binding.Augmentable;
import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.AugmentationHolder;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeContainer & WithStatus>
+/**
+ * This class is an implementation detail. It is public only due to technical reasons and may change at any time.
+ */
+@Beta
+public abstract class DataObjectCodecContext<D extends DataObject, T extends DataNodeContainer & WithStatus>
extends DataContainerCodecContext<D, T> {
private static final class Augmentations implements Immutable {
final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYang;
}
private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class);
- private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, InvocationHandler.class);
- private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class, InvocationHandler.class);
+ private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, NormalizedNodeContainer.class);
+ private static final MethodType AUGMENTABLE_CONSTRUCTOR_TYPE = MethodType.methodType(void.class,
+ DataObjectCodecContext.class, NormalizedNodeContainer.class);
+ private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class,
+ NormalizedNodeContainer.class);
+ private static final MethodType AUGMENTABLE_DATAOBJECT_TYPE = MethodType.methodType(DataObject.class,
+ DataObjectCodecContext.class, NormalizedNodeContainer.class);
private static final Comparator<Method> METHOD_BY_ALPHABET = Comparator.comparing(Method::getName);
private static final Augmentations EMPTY_AUGMENTATIONS = new Augmentations(ImmutableMap.of(), ImmutableMap.of());
- private static final Method[] EMPTY_METHODS = new Method[0];
private final ImmutableMap<String, ValueNodeCodecContext> leafChild;
private final ImmutableMap<YangInstanceIdentifier.PathArgument, NodeContextSupplier> byYang;
- private final ImmutableMap<String, NodeContextSupplier> byMethod;
- private final ImmutableMap<String, String> nonnullToGetter;
private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byStreamClass;
private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byBindingArgClass;
private final ImmutableMap<AugmentationIdentifier, Type> possibleAugmentations;
private final MethodHandle proxyConstructor;
- private final Method[] propertyMethods;
+ // FIXME: the presence of these two volatile fields may be preventing us from being able to improve
+ // DataContainerCodecPrototype.get() publication.
@SuppressWarnings("rawtypes")
private static final AtomicReferenceFieldUpdater<DataObjectCodecContext, Augmentations>
AUGMENTATIONS_UPDATER = AtomicReferenceFieldUpdater.newUpdater(DataObjectCodecContext.class,
private volatile ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> mismatchedAugmented = ImmutableMap.of();
DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype) {
+ this(prototype, null);
+ }
+
+ DataObjectCodecContext(final DataContainerCodecPrototype<T> prototype,
+ final Entry<Method, IdentifiableItemCodec> keyMethod) {
super(prototype);
final Class<D> bindingClass = getBindingClass();
}
}
- final int methodCount = tmpMethodToSupplier.size();
- final Builder<String, NodeContextSupplier> byMethodBuilder = ImmutableMap.builderWithExpectedSize(methodCount);
- this.propertyMethods = methodCount == 0 ? EMPTY_METHODS : new Method[methodCount];
-
- int offset = 0;
- for (Entry<Method, NodeContextSupplier> entry : tmpMethodToSupplier.entrySet()) {
- final Method method = entry.getKey();
- propertyMethods[offset++] = method;
- byMethodBuilder.put(method.getName(), entry.getValue());
- }
-
// Make sure properties are alpha-sorted
- Arrays.sort(propertyMethods, METHOD_BY_ALPHABET);
+ final Method[] properties = tmpMethodToSupplier.keySet().toArray(new Method[0]);
+ Arrays.sort(properties, METHOD_BY_ALPHABET);
+ final Builder<Method, NodeContextSupplier> propBuilder = ImmutableMap.builderWithExpectedSize(
+ properties.length);
+ for (Method prop : properties) {
+ propBuilder.put(prop, verifyNotNull(tmpMethodToSupplier.get(prop)));
+ }
- this.byMethod = byMethodBuilder.build();
this.byYang = ImmutableMap.copyOf(byYangBuilder);
this.byStreamClass = ImmutableMap.copyOf(byStreamClassBuilder);
byBindingArgClassBuilder.putAll(byStreamClass);
this.byBindingArgClass = ImmutableMap.copyOf(byBindingArgClassBuilder);
- final Map<Class<?>, Method> clsToNonnull = BindingReflections.getChildrenClassToNonnullMethod(bindingClass);
- final Map<String, String> nonnullToGetterBuilder = new HashMap<>();
- for (final Entry<Class<?>, Method> entry : clsToNonnull.entrySet()) {
- final Method method = entry.getValue();
- if (!method.isDefault()) {
- LOG.warn("Ignoring non-default method {} in {}", method, bindingClass);
- continue;
- }
-
- // Derive getter name from the nonnull method and verify we have the corresponding getter. Note that
- // the intern() call is important, as it makes sure we use the same instance to bridge to byMethod map.
- final String methodName = method.getName();
- final String getterName = BindingMapping.getGetterMethodForNonnull(methodName).intern();
- verify(byMethod.containsKey(getterName), "Cannot find getter %s for %s", getterName, methodName);
- nonnullToGetterBuilder.put(methodName, getterName);
- }
- nonnullToGetter = ImmutableMap.copyOf(nonnullToGetterBuilder);
-
+ final Class<? extends CodecDataObject<?>> generatedClass;
+ final MethodType ctorType;
if (Augmentable.class.isAssignableFrom(bindingClass)) {
this.possibleAugmentations = factory().getRuntimeContext().getAvailableAugmentationTypes(getSchema());
+ generatedClass = CodecDataObjectGenerator.generateAugmentable(prototype.getFactory().getLoader(),
+ bindingClass, propBuilder.build(), keyMethod);
+ ctorType = AUGMENTABLE_CONSTRUCTOR_TYPE;
} else {
this.possibleAugmentations = ImmutableMap.of();
+ generatedClass = CodecDataObjectGenerator.generate(prototype.getFactory().getLoader(), bindingClass,
+ propBuilder.build(), keyMethod);
+ ctorType = CONSTRUCTOR_TYPE;
}
reloadAllAugmentations();
- final Class<?> proxyClass = Proxy.getProxyClass(bindingClass.getClassLoader(), bindingClass,
- AugmentationHolder.class);
+ final MethodHandle ctor;
try {
- proxyConstructor = MethodHandles.publicLookup().findConstructor(proxyClass, CONSTRUCTOR_TYPE)
- .asType(DATAOBJECT_TYPE);
+ ctor = MethodHandles.publicLookup().findConstructor(generatedClass, ctorType);
} catch (NoSuchMethodException | IllegalAccessException e) {
- throw new IllegalStateException("Failed to find contructor for class " + proxyClass, e);
+ throw new LinkageError("Failed to find contructor for class " + generatedClass, e);
+ }
+
+ if (Augmentable.class.isAssignableFrom(bindingClass)) {
+ proxyConstructor = ctor.asType(AUGMENTABLE_DATAOBJECT_TYPE).bindTo(this);
+ } else {
+ proxyConstructor = ctor.asType(DATAOBJECT_TYPE);
}
}
if (ctxProto == null && Augmentation.class.isAssignableFrom(argType)) {
ctxProto = augmentationByClass(argType);
}
- final DataContainerCodecContext<?, ?> context =
- childNonNull(ctxProto, argType, "Class %s is not valid child of %s", argType, getBindingClass()).get();
+ final DataContainerCodecContext<?, ?> context = childNonNull(ctxProto, argType,
+ "Class %s is not valid child of %s", argType, getBindingClass()).get();
if (context instanceof ChoiceNodeCodecContext) {
final ChoiceNodeCodecContext<?> choice = (ChoiceNodeCodecContext<?>) context;
choice.addYangPathArgument(arg, builder);
return DataContainerCodecPrototype.from(augClass, augSchema.getKey(), augSchema.getValue(), factory());
}
- // Unlike BindingMapping.getGetterMethodForNonnull() this returns an interned String
- @NonNull String getterNameForNonnullName(final String nonnullMethod) {
- return verifyNotNull(nonnullToGetter.get(nonnullMethod), "Failed to look up getter method for %s",
- nonnullMethod);
- }
-
- @SuppressWarnings("rawtypes")
- @Nullable Object getBindingChildValue(final String method, final NormalizedNodeContainer domData) {
- final NodeCodecContext childContext = verifyNotNull(byMethod.get(method),
- "Cannot find data handler for method %s", method).get();
-
- @SuppressWarnings("unchecked")
- final Optional<NormalizedNode<?, ?>> domChild = domData.getChild(childContext.getDomPathArgument());
-
- // We do not want to use Optional.map() here because we do not want to invoke defaultObject() when we have
- // normal value because defaultObject() may end up throwing an exception intentionally.
- return domChild.isPresent() ? childContext.deserializeObject(domChild.get()) : childContext.defaultObject();
- }
-
@SuppressWarnings("checkstyle:illegalCatch")
protected final D createBindingProxy(final NormalizedNodeContainer<?, ?, ?> node) {
try {
- return (D) proxyConstructor.invokeExact((InvocationHandler)new LazyDataObject<>(this, node));
+ return (D) proxyConstructor.invokeExact(node);
} catch (final Throwable e) {
Throwables.throwIfUnchecked(e);
throw new IllegalStateException(e);
return map;
}
- final Method[] propertyMethods() {
- return propertyMethods;
- }
-
@Override
public InstanceIdentifier.PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
checkArgument(getDomPathArgument().equals(arg));