org.mule.runtime.module.extension.internal.util.ReflectionCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mule-module-extensions-support Show documentation
Show all versions of mule-module-extensions-support Show documentation
Default implementation of the Mule Extension API.
The newest version!
/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.module.extension.internal.util;
import static java.lang.String.format;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static org.mule.runtime.module.extension.internal.util.IntrospectionUtils.getAnnotatedFields;
import org.mule.runtime.core.api.util.ClassUtils;
import org.mule.runtime.extension.api.exception.IllegalModelDefinitionException;
import org.mule.runtime.module.extension.api.loader.java.type.FieldElement;
import org.mule.runtime.module.extension.internal.loader.ParameterGroupDescriptor;
import org.reflections.ReflectionUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Caches results of frequently done reflection lookups.
*
* @since 4.1
*/
public class ReflectionCache {
private final ConcurrentMap, List> fieldElements = new ConcurrentHashMap<>();
private final ConcurrentMap, ConcurrentMap, Optional>> fieldSetterForAnnotatedField =
new ConcurrentHashMap<>(3, 0.9f);
private final ConcurrentMap, List> fieldsByClass = new ConcurrentHashMap<>();
private final ConcurrentMap, Boolean> hasDefaultConstructorsByClass = new ConcurrentHashMap<>();
public List fieldElementsFor(ParameterGroupDescriptor groupDescriptor) {
Class clazz = groupDescriptor.getType().getDeclaringClass().get();
List elements = fieldElements.get(clazz);
// This pre-check is made in order to avoid the synchronized block in the implementation of ConcurrentHashMap
// (https://bugs.openjdk.java.net/browse/JDK-8161372)
if (elements == null) {
elements = fieldElements.computeIfAbsent(clazz, cls -> groupDescriptor.getType().getFields());
}
return elements;
}
public Optional getFieldSetterForAnnotatedField(Object target, Class annotationClass) {
ConcurrentMap, Optional> cache = fieldSetterForAnnotatedField.get(annotationClass);
// This pre-check is made in order to avoid the synchronized block in the implementation of ConcurrentHashMap
// (https://bugs.openjdk.java.net/browse/JDK-8161372)
if (cache == null) {
cache = fieldSetterForAnnotatedField.computeIfAbsent(annotationClass, k -> new ConcurrentHashMap<>());
}
final Class type = target.getClass();
Optional setter = cache.get(type);
// This pre-check is made in order to avoid the synchronized block in the implementation of ConcurrentHashMap
// (https://bugs.openjdk.java.net/browse/JDK-8161372)
if (setter == null) {
setter = cache.computeIfAbsent(type, t -> {
List fields = getAnnotatedFields(t, annotationClass);
if (fields.isEmpty()) {
return empty();
} else if (fields.size() > 1) {
throw new IllegalModelDefinitionException(format(
"Class '%s' has %d fields annotated with @%s. Only one field may carry that annotation",
t.getName(), fields.size(), annotationClass));
}
return of(new FieldSetter<>(fields.get(0)));
});
}
return setter;
}
public List getFields(Class clazz) {
List fields = fieldsByClass.get(clazz);
// This pre-check is made in order to avoid the synchronized block in the implementation of ConcurrentHashMap
// (https://bugs.openjdk.java.net/browse/JDK-8161372)
if (fields == null) {
fields = fieldsByClass.computeIfAbsent(clazz, cls -> {
List f = new ArrayList<>();
for (Field field : clazz.getDeclaredFields()) {
f.add(field);
}
for (Class type : ReflectionUtils.getAllSuperTypes(clazz)) {
for (Field field : type.getDeclaredFields()) {
f.add(field);
}
}
return f;
});
}
return fields;
}
public boolean hasDefaultConstructor(Class clazz) {
Boolean value = hasDefaultConstructorsByClass.get(clazz);
// This pre-check is made in order to avoid the synchronized block in the implementation of ConcurrentHashMap
// (https://bugs.openjdk.java.net/browse/JDK-8161372)
if (value == null) {
value = hasDefaultConstructorsByClass.computeIfAbsent(clazz, cls -> ClassUtils.getConstructor(cls, new Class[] {}) != null);
}
return value;
}
}