2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.spec.reflect;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.cache.CacheBuilder;
13 import com.google.common.cache.CacheLoader;
14 import com.google.common.cache.LoadingCache;
15 import java.lang.invoke.MethodHandle;
16 import java.lang.invoke.MethodHandles;
17 import java.lang.invoke.MethodHandles.Lookup;
18 import java.lang.invoke.MethodType;
19 import java.lang.reflect.Field;
20 import java.security.AccessController;
21 import java.security.PrivilegedActionException;
22 import java.security.PrivilegedExceptionAction;
23 import java.util.Collections;
25 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
26 import org.opendaylight.yangtools.yang.binding.Augmentation;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
30 abstract class AugmentationFieldGetter {
32 private static final Logger LOG = LoggerFactory.getLogger(AugmentationFieldGetter.class);
34 private static final AugmentationFieldGetter DUMMY = new AugmentationFieldGetter() {
36 Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
37 return Collections.emptyMap();
41 private static final LoadingCache<Class<?>, AugmentationFieldGetter> AUGMENTATION_GETTERS =
42 CacheBuilder.newBuilder().weakKeys().build(new AugmentationGetterLoader());
45 * Retrieves augmentations from supplied object.
47 * @param input Input Data object, from which augmentations should be extracted
48 * @return Map of Augmentation class to augmentation
50 abstract Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(Object input);
52 static AugmentationFieldGetter getGetter(final Class<? extends Object> clz) {
53 return AUGMENTATION_GETTERS.getUnchecked(clz);
56 private static final class AugmentationGetterLoader extends CacheLoader<Class<?>, AugmentationFieldGetter> {
57 private static final MethodType GETTER_TYPE = MethodType.methodType(Map.class, Object.class);
58 private static final Lookup LOOKUP = MethodHandles.lookup();
61 public AugmentationFieldGetter load(final Class<?> key) throws IllegalAccessException {
64 field = AccessController.doPrivileged((PrivilegedExceptionAction<Field>) () -> {
65 final Field f = key.getDeclaredField(BindingMapping.AUGMENTATION_FIELD);
66 f.setAccessible(true);
69 } catch (PrivilegedActionException e) {
70 LOG.warn("Failed to acquire augmentation field {}, ignoring augmentations in class {}",
71 BindingMapping.AUGMENTATION_FIELD, key, e);
74 if (!Map.class.isAssignableFrom(field.getType())) {
75 LOG.warn("Class {} field {} is not a Map, ignoring augmentations", key,
76 BindingMapping.AUGMENTATION_FIELD);
80 return new ReflectionAugmentationFieldGetter(LOOKUP.unreflectGetter(field).asType(GETTER_TYPE));
84 private static final class ReflectionAugmentationFieldGetter extends AugmentationFieldGetter {
85 private final MethodHandle fieldGetter;
87 ReflectionAugmentationFieldGetter(final MethodHandle mh) {
88 this.fieldGetter = requireNonNull(mh);
92 @SuppressWarnings("checkstyle:illegalCatch")
93 Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Object input) {
95 return (Map<Class<? extends Augmentation<?>>, Augmentation<?>>) fieldGetter.invokeExact(input);
96 } catch (Throwable e) {
97 throw new IllegalStateException("Failed to access augmentation field on " + input, e);