3388a9a2f0a0e5f7dfaa81ea2ceaadb9a7488f83
[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
47     private static final String EVENT_INSTANT_NAME;
48     private static final Generic EVENT_INSTANT_RETTYPE;
49
50     static {
51         final MethodDescription eventInstance = EVENT_INSTANT_AWARE.getDeclaredMethods().getOnly();
52         EVENT_INSTANT_NAME = eventInstance.getName();
53         EVENT_INSTANT_RETTYPE = eventInstance.getReturnType();
54     }
55
56     private static final Generic BB_DOCC = TypeDefinition.Sort.describe(DataObjectCodecContext.class);
57     private static final Generic BB_NNC = TypeDefinition.Sort.describe(NormalizedNodeContainer.class);
58     private static final Generic BB_I = TypeDefinition.Sort.describe(Instant.class);
59
60     private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, DataObjectCodecContext.class,
61         NormalizedNodeContainer.class, Instant.class);
62     private static final MethodType NOTIFICATION_TYPE = MethodType.methodType(Notification.class,
63         NotificationCodecContext.class, ContainerNode.class, Instant.class);
64     private static final String INSTANT_FIELD = "instant";
65
66     private final MethodHandle eventProxy;
67
68     NotificationCodecContext(final Class<?> key, final NotificationDefinition schema,
69             final CodecContextFactory factory) {
70         super(DataContainerCodecPrototype.from(key, schema, factory));
71         final Class<D> bindingClass = getBindingClass();
72
73         final Class<?> awareClass = factory().getLoader().generateClass(bindingClass, "eventInstantAware",
74             (loader, fqcn, bindingInterface) -> {
75                 final Class<?> codecImpl = loader.getGeneratedClass(bindingClass, "codecImpl");
76
77                 return GeneratorResult.of(new ByteBuddy()
78                     .subclass(codecImpl, ConstructorStrategy.Default.NO_CONSTRUCTORS)
79                     .implement(EVENT_INSTANT_AWARE)
80                     .name(fqcn)
81                     .defineField(INSTANT_FIELD, BB_I, Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC)
82                     .defineConstructor(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC)
83                         .withParameters(BB_DOCC, BB_NNC, BB_I)
84                         .intercept(ConstructorImplementation.INSTANCE)
85                     .defineMethod(EVENT_INSTANT_NAME, EVENT_INSTANT_RETTYPE, Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC)
86                         .intercept(EventInstantImplementation.INSTANCE)
87                     .make());
88             });
89
90         final MethodHandle ctor;
91         try {
92             ctor = MethodHandles.publicLookup().findConstructor(awareClass, CONSTRUCTOR_TYPE);
93         } catch (IllegalAccessException | NoSuchMethodException e) {
94             throw new LinkageError("Failed to acquire constructor", e);
95         }
96         eventProxy = ctor.asType(NOTIFICATION_TYPE);
97     }
98
99     @Override
100     public D deserialize(final NormalizedNode<?, ?> data) {
101         checkState(data instanceof ContainerNode);
102         return createBindingProxy((ContainerNode) data);
103     }
104
105     @SuppressWarnings("checkstyle:illegalCatch")
106     Notification deserialize(final @NonNull ContainerNode data, final @NonNull Instant eventInstant) {
107         try {
108             return (Notification) eventProxy.invokeExact(this, data, eventInstant);
109         } catch (final Throwable e) {
110             Throwables.throwIfUnchecked(e);
111             throw new IllegalStateException(e);
112         }
113     }
114
115     @Override
116     protected Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
117         return deserialize(normalizedNode);
118     }
119
120     private enum ConstructorImplementation implements Implementation {
121         INSTANCE;
122
123         private static final StackManipulation INSTANT_ARG = MethodVariableAccess.REFERENCE.loadFrom(3);
124         private static final StackManipulation LOAD_CTOR_ARGS;
125
126         static {
127             try {
128                 LOAD_CTOR_ARGS = MethodVariableAccess.allArgumentsOf(new MethodDescription.ForLoadedConstructor(
129                     AugmentableCodecDataObject.class.getDeclaredConstructor(DataObjectCodecContext.class,
130                         NormalizedNodeContainer.class)));
131             } catch (NoSuchMethodException e) {
132                 throw new ExceptionInInitializerError(e);
133             }
134         }
135
136         @Override
137         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
138             return instrumentedType;
139         }
140
141         @Override
142         public ByteCodeAppender appender(final Target implementationTarget) {
143             final TypeDescription instrumentedType = implementationTarget.getInstrumentedType();
144             final InGenericShape superCtor = instrumentedType.getSuperClass().getDeclaredMethods()
145                     .filter(ElementMatchers.isConstructor()).getOnly();
146
147             return new ByteCodeAppender.Simple(
148                 ByteBuddyUtils.THIS,
149                 LOAD_CTOR_ARGS,
150                 MethodInvocation.invoke(superCtor),
151                 ByteBuddyUtils.THIS,
152                 INSTANT_ARG,
153                 ByteBuddyUtils.putField(instrumentedType, INSTANT_FIELD),
154                 MethodReturn.VOID);
155         }
156     }
157
158     private enum EventInstantImplementation implements Implementation {
159         INSTANCE;
160
161         @Override
162         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
163             return instrumentedType;
164         }
165
166         @Override
167         public ByteCodeAppender appender(final Target implementationTarget) {
168             return new ByteCodeAppender.Simple(
169               ByteBuddyUtils.THIS,
170               ByteBuddyUtils.getField(implementationTarget.getInstrumentedType(), INSTANT_FIELD),
171               MethodReturn.REFERENCE);
172         }
173     }
174 }