Binding v2 runtime context
[mdsal.git] / binding2 / mdsal-binding2-spec / src / main / java / org / opendaylight / mdsal / binding / javav2 / spec / util / AugmentationFieldGetter.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. 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
9 package org.opendaylight.mdsal.binding.javav2.spec.util;
10
11 import com.google.common.annotations.Beta;
12 import com.google.common.base.Preconditions;
13 import com.google.common.cache.CacheBuilder;
14 import com.google.common.cache.CacheLoader;
15 import com.google.common.cache.LoadingCache;
16 import java.lang.invoke.MethodHandle;
17 import java.lang.invoke.MethodHandles;
18 import java.lang.invoke.MethodHandles.Lookup;
19 import java.lang.invoke.MethodType;
20 import java.lang.reflect.Field;
21 import java.util.Collections;
22 import java.util.Map;
23 import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
24 import org.opendaylight.mdsal.binding.javav2.spec.structural.AugmentationHolder;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 @Beta
29 abstract class AugmentationFieldGetter {
30
31     private static final Logger LOG = LoggerFactory.getLogger(AugmentationFieldGetter.class);
32
33     private static final String AUGMENTATION_FIELD = "augmentation";
34
35     private static final AugmentationFieldGetter DUMMY = new AugmentationFieldGetter() {
36         @Override
37         protected Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
38             return Collections.emptyMap();
39         }
40     };
41
42     private static final AugmentationFieldGetter AUGMENTATION_HOLDER_GETTER = new AugmentationFieldGetter() {
43
44         @Override
45         @SuppressWarnings({ "unchecked", "rawtypes" })
46         protected Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
47             return (Map) ((AugmentationHolder<?>) input).augmentations();
48         }
49     };
50
51     private static final LoadingCache<Class<?>, AugmentationFieldGetter> AUGMENTATION_GETTERS =
52             CacheBuilder.newBuilder().weakKeys().build(new AugmentationGetterLoader());
53
54     /**
55      * Retrieves augmentations from supplied object.
56      *
57      * @param input
58      *            - input Data object, from which augmentations should be
59      *            extracted
60      * @return Map of Augmentation class to augmentation
61      */
62     protected abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input);
63
64     public static AugmentationFieldGetter getGetter(final Class<? extends Object> clz) {
65         if (AugmentationHolder.class.isAssignableFrom(clz)) {
66             return AUGMENTATION_HOLDER_GETTER;
67         }
68         return AUGMENTATION_GETTERS.getUnchecked(clz);
69     }
70
71     private static final class AugmentationGetterLoader extends CacheLoader<Class<?>, AugmentationFieldGetter> {
72         private static final MethodType GETTER_TYPE = MethodType.methodType(Map.class, Object.class);
73         private static final Lookup LOOKUP = MethodHandles.lookup();
74
75         @Override
76         public AugmentationFieldGetter load(final Class<?> key) throws IllegalAccessException {
77             final Field field;
78             try {
79                 field = key.getDeclaredField(AUGMENTATION_FIELD);
80                 field.setAccessible(true);
81             } catch (NoSuchFieldException | SecurityException e) {
82                 LOG.warn("Failed to acquire augmentation field {}, ignoring augmentations in class {}",
83                         AUGMENTATION_FIELD, key, e);
84                 return DUMMY;
85             }
86             if (!Map.class.isAssignableFrom(field.getType())) {
87                 LOG.warn("Class {} field {} is not a Map, ignoring augmentations", key,
88                         AUGMENTATION_FIELD);
89                 return DUMMY;
90             }
91
92             return new ReflectionAugmentationFieldGetter(LOOKUP.unreflectGetter(field).asType(GETTER_TYPE));
93         }
94     }
95
96     private static final class ReflectionAugmentationFieldGetter extends AugmentationFieldGetter {
97         private final MethodHandle fieldGetter;
98
99         ReflectionAugmentationFieldGetter(final MethodHandle mh) {
100             this.fieldGetter = Preconditions.checkNotNull(mh);
101         }
102
103         @Override
104         protected Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
105             try {
106                 return (Map<Class<? extends Augmentation<?>>, Augmentation<?>>) this.fieldGetter.invokeExact(input);
107             } catch (final Throwable e) {
108                 throw new IllegalStateException("Failed to access augmentation field on " + input, e);
109             }
110         }
111     }
112 }