6553f707824baf39ec9930ec67fc7757b5dab20d
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / NotificationCodecContext.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
9
10 import static com.google.common.base.Preconditions.checkState;
11
12 import com.google.common.base.Throwables;
13 import java.lang.invoke.MethodHandle;
14 import java.lang.invoke.MethodHandles;
15 import java.lang.invoke.MethodType;
16 import java.time.Instant;
17 import net.bytebuddy.ByteBuddy;
18 import net.bytebuddy.description.method.MethodDescription;
19 import net.bytebuddy.description.method.MethodDescription.InGenericShape;
20 import net.bytebuddy.description.type.TypeDefinition;
21 import net.bytebuddy.description.type.TypeDescription;
22 import net.bytebuddy.description.type.TypeDescription.Generic;
23 import net.bytebuddy.dynamic.scaffold.InstrumentedType;
24 import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
25 import net.bytebuddy.implementation.Implementation;
26 import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
27 import net.bytebuddy.implementation.bytecode.StackManipulation;
28 import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
29 import net.bytebuddy.implementation.bytecode.member.MethodReturn;
30 import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
31 import net.bytebuddy.jar.asm.Opcodes;
32 import net.bytebuddy.matcher.ElementMatchers;
33 import org.eclipse.jdt.annotation.NonNull;
34 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.GeneratorResult;
35 import org.opendaylight.yangtools.yang.binding.DataObject;
36 import org.opendaylight.yangtools.yang.binding.EventInstantAware;
37 import org.opendaylight.yangtools.yang.binding.Notification;
38 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
41 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
42
43 final class NotificationCodecContext<D extends DataObject & Notification>
44         extends DataObjectCodecContext<D, NotificationDefinition> {
45     private static final Generic EVENT_INSTANT_AWARE = TypeDefinition.Sort.describe(EventInstantAware.class);
46     private static final MethodDescription EVENT_INSTANT = EVENT_INSTANT_AWARE.getDeclaredMethods().getOnly();
47     private static final Generic BB_DOCC = TypeDefinition.Sort.describe(DataObjectCodecContext.class);
48     private static final Generic BB_NNC = TypeDefinition.Sort.describe(NormalizedNodeContainer.class);
49     private static final Generic BB_I = TypeDefinition.Sort.describe(Instant.class);
50
51     private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, DataObjectCodecContext.class,
52         NormalizedNodeContainer.class, Instant.class);
53     private static final MethodType NOTIFICATION_TYPE = MethodType.methodType(Notification.class,
54         NotificationCodecContext.class, ContainerNode.class, Instant.class);
55     private static final String INSTANT_FIELD = "instant";
56
57     private final MethodHandle eventProxy;
58
59     NotificationCodecContext(final Class<?> key, final NotificationDefinition schema,
60             final CodecContextFactory factory) {
61         super(DataContainerCodecPrototype.from(key, schema, factory));
62         final Class<D> bindingClass = getBindingClass();
63
64         final Class<?> awareClass = factory().getLoader().generateClass(bindingClass, "eventInstantAware",
65             (loader, fqcn, bindingInterface) -> {
66                 final Class<?> codecImpl = loader.getGeneratedClass(bindingClass, "codecImpl");
67
68                 return GeneratorResult.of(new ByteBuddy()
69                     .subclass(codecImpl, ConstructorStrategy.Default.NO_CONSTRUCTORS)
70                     .implement(EVENT_INSTANT_AWARE)
71                     .name(fqcn)
72                     .defineField(INSTANT_FIELD, BB_I, Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC)
73                     .defineConstructor(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC)
74                         .withParameters(BB_DOCC, BB_NNC, BB_I)
75                         .intercept(ConstructorImplementation.INSTANCE)
76                     .define(EVENT_INSTANT).intercept(EventInstantImplementation.INSTANCE)
77                     .make());
78             });
79
80         final MethodHandle ctor;
81         try {
82             ctor = MethodHandles.publicLookup().findConstructor(awareClass, CONSTRUCTOR_TYPE);
83         } catch (IllegalAccessException | NoSuchMethodException e) {
84             throw new LinkageError("Failed to acquire constructor", e);
85         }
86         eventProxy = ctor.asType(NOTIFICATION_TYPE);
87     }
88
89     @Override
90     public D deserialize(final NormalizedNode<?, ?> data) {
91         checkState(data instanceof ContainerNode);
92         return createBindingProxy((ContainerNode) data);
93     }
94
95     @SuppressWarnings("checkstyle:illegalCatch")
96     Notification deserialize(final @NonNull ContainerNode data, final @NonNull Instant eventInstant) {
97         try {
98             return (Notification) eventProxy.invokeExact(this, data, eventInstant);
99         } catch (final Throwable e) {
100             Throwables.throwIfUnchecked(e);
101             throw new IllegalStateException(e);
102         }
103     }
104
105     @Override
106     protected Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
107         return deserialize(normalizedNode);
108     }
109
110     private enum ConstructorImplementation implements Implementation {
111         INSTANCE;
112
113         private static final StackManipulation INSTANT_ARG = MethodVariableAccess.REFERENCE.loadFrom(3);
114         private static final StackManipulation LOAD_CTOR_ARGS;
115
116         static {
117             try {
118                 LOAD_CTOR_ARGS = MethodVariableAccess.allArgumentsOf(new MethodDescription.ForLoadedConstructor(
119                     AugmentableCodecDataObject.class.getDeclaredConstructor(DataObjectCodecContext.class,
120                         NormalizedNodeContainer.class)));
121             } catch (NoSuchMethodException e) {
122                 throw new ExceptionInInitializerError(e);
123             }
124         }
125
126         @Override
127         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
128             return instrumentedType;
129         }
130
131         @Override
132         public ByteCodeAppender appender(final Target implementationTarget) {
133             final TypeDescription instrumentedType = implementationTarget.getInstrumentedType();
134             final InGenericShape superCtor = instrumentedType.getSuperClass().getDeclaredMethods()
135                     .filter(ElementMatchers.isConstructor()).getOnly();
136
137             return new ByteCodeAppender.Simple(
138                 ByteBuddyUtils.THIS,
139                 LOAD_CTOR_ARGS,
140                 MethodInvocation.invoke(superCtor),
141                 ByteBuddyUtils.THIS,
142                 INSTANT_ARG,
143                 ByteBuddyUtils.putField(instrumentedType, INSTANT_FIELD),
144                 MethodReturn.VOID);
145         }
146     }
147
148     private enum EventInstantImplementation implements Implementation {
149         INSTANCE;
150
151         @Override
152         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
153             return instrumentedType;
154         }
155
156         @Override
157         public ByteCodeAppender appender(final Target implementationTarget) {
158             return new ByteCodeAppender.Simple(
159               ByteBuddyUtils.THIS,
160               ByteBuddyUtils.getField(implementationTarget.getInstrumentedType(), INSTANT_FIELD),
161               MethodReturn.REFERENCE);
162         }
163     }
164 }