Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.datakernel.eventloop.util.ReflectionUtils Maven / Gradle / Ivy
Go to download
Efficient non-blocking network and file I/O, for building Node.js-like client/server applications with
high performance requirements. It is similar to Event Loop in Node.js.
Although Eventloop runs in a single thread, multiple event loops can be
launched at the same time allowing for efficient CPU usage.
/*
* Copyright (C) 2015 SoftIndex LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.datakernel.eventloop.util;
import io.datakernel.common.exception.UncheckedException;
import io.datakernel.common.ref.RefBoolean;
import io.datakernel.eventloop.jmx.JmxRefreshableStats;
import io.datakernel.eventloop.jmx.JmxStats;
import io.datakernel.eventloop.jmx.JmxStatsWithReset;
import io.datakernel.eventloop.jmx.JmxStatsWithSmoothingWindow;
import io.datakernel.jmx.api.JmxAttribute;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.management.MXBean;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.time.Duration;
import java.util.*;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static io.datakernel.common.Preconditions.checkArgument;
import static io.datakernel.common.collection.CollectionUtils.first;
import static java.util.stream.Collectors.toSet;
public final class ReflectionUtils {
public static boolean isPrimitiveType(Class> cls) {
return cls == boolean.class
|| cls == byte.class
|| cls == char.class
|| cls == short.class
|| cls == int.class
|| cls == long.class
|| cls == float.class
|| cls == double.class;
}
public static boolean isBoxedPrimitiveType(Class> cls) {
return cls == Boolean.class
|| cls == Byte.class
|| cls == Character.class
|| cls == Short.class
|| cls == Integer.class
|| cls == Long.class
|| cls == Float.class
|| cls == Double.class;
}
public static boolean isPrimitiveTypeOrBox(Class> cls) {
return isPrimitiveType(cls) || isBoxedPrimitiveType(cls);
}
public static boolean isSimpleType(Class> cls) {
return isPrimitiveTypeOrBox(cls) || cls == String.class;
}
public static boolean isThrowable(Class> cls) {
return Throwable.class.isAssignableFrom(cls);
}
public static boolean isJmxStats(Class> cls) {
return JmxStats.class.isAssignableFrom(cls);
}
public static boolean isJmxRefreshableStats(Class> cls) {
return JmxRefreshableStats.class.isAssignableFrom(cls);
}
public static boolean isGetter(Method method) {
return Modifier.isPublic(method.getModifiers())
&& method.getName().length() > 2
&& (method.getName().startsWith("get") && method.getReturnType() != void.class
|| method.getName().startsWith("is") && (method.getReturnType() == boolean.class || method.getReturnType() == Boolean.class));
}
public static boolean isSetter(Method method) {
return Modifier.isPublic(method.getModifiers())
&& method.getName().length() > 3
&& method.getName().startsWith("set")
&& method.getReturnType() == void.class
&& method.getParameterCount() == 1;
}
public static String extractFieldNameFromGetter(Method getter) {
if (getter.getName().startsWith("get")) {
if (getter.getName().length() == 3) {
return "";
}
String getterName = getter.getName();
String firstLetter = getterName.substring(3, 4);
String restOfName = getterName.substring(4);
return firstLetter.toLowerCase() + restOfName;
}
if (getter.getName().startsWith("is") && getter.getName().length() > 2) {
String getterName = getter.getName();
String firstLetter = getterName.substring(2, 3);
String restOfName = getterName.substring(3);
return firstLetter.toLowerCase() + restOfName;
}
throw new IllegalArgumentException("Given method is not a getter");
}
public static String extractFieldNameFromSetter(Method setter) {
String name = setter.getName();
checkArgument(name.startsWith("set") && name.length() > 3, "Given method is not a setter");
String firstLetter = name.substring(3, 4);
String restOfName = name.substring(4);
return firstLetter.toLowerCase() + restOfName;
}
@SuppressWarnings("unchecked")
@Nullable
private static Supplier getConstructorOrFactory(Class cls, String... factoryMethodNames) {
for (String methodName : factoryMethodNames) {
Method method;
try {
method = cls.getDeclaredMethod(methodName);
} catch (NoSuchMethodException e) {
continue;
}
if ((method.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) == 0 || method.getReturnType() != cls) {
continue;
}
return () -> {
try {
return (T) method.invoke(null);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new UncheckedException(e);
}
};
}
return Arrays.stream(cls.getConstructors())
.filter(c -> c.getParameterTypes().length == 0)
.findAny()
.>map(c -> () -> {
try {
return (T) c.newInstance();
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
throw new UncheckedException(e);
}
})
.orElse(null);
}
public static boolean canBeCreated(Class> cls, String... factoryMethodNames) {
return getConstructorOrFactory(cls, factoryMethodNames) != null;
}
@Nullable
public static T tryToCreateInstanceWithFactoryMethods(Class cls, String... factoryMethodNames) {
try {
Supplier supplier = getConstructorOrFactory(cls, factoryMethodNames);
return supplier != null ? supplier.get() : null;
} catch (UncheckedException u) {
return null;
}
}
private static void visitFields(Object instance, Predicate action) {
if (instance == null) {
return;
}
for (Method method : instance.getClass().getMethods()) {
if (method.getParameters().length != 0 || !Modifier.isPublic(method.getModifiers())) {
continue;
}
Class> returnType = method.getReturnType();
if (returnType == void.class || isSimpleType(returnType)) {
continue;
}
if (Arrays.stream(method.getAnnotations()).noneMatch(a -> a.annotationType() == JmxAttribute.class)) {
continue;
}
Object fieldValue;
try {
fieldValue = method.invoke(instance);
} catch (IllegalAccessException | InvocationTargetException e) {
continue;
}
if (fieldValue == null) {
continue;
}
if (action.test(fieldValue)) {
continue;
}
if (Map.class.isAssignableFrom(returnType)) {
for (Object item : ((Map, ?>) fieldValue).values()) {
visitFields(item, action);
}
} else if (Collection.class.isAssignableFrom(returnType)) {
for (Object item : (Collection>) fieldValue) {
visitFields(item, action);
}
} else {
visitFields(fieldValue, action);
}
}
}
public static List> getAllInterfaces(Class> cls) {
Set> interfacesFound = new LinkedHashSet<>();
getAllInterfaces(cls, interfacesFound);
return new ArrayList<>(interfacesFound);
}
private static void getAllInterfaces(Class> cls, Set> found) {
while (cls != null) {
if (cls.isInterface()) {
found.add(cls);
}
for (Class> interfaceCls : cls.getInterfaces()) {
if (found.add(interfaceCls)) {
getAllInterfaces(interfaceCls, found);
}
}
cls = cls.getSuperclass();
}
}
private static boolean isBeanInterface(Class> cls) {
String name = cls.getName();
return name.endsWith("MBean") || name.endsWith("MXBean") || cls.isAnnotationPresent(MXBean.class);
}
public static boolean isBean(Class> cls) {
return getAllInterfaces(cls).stream().anyMatch(ReflectionUtils::isBeanInterface);
}
public static Map getJmxAttributes(@Nullable Object instance) {
Map result = new LinkedHashMap<>();
getJmxAttributes(instance, "", result);
return result;
}
private static boolean getJmxAttributes(@Nullable Object instance, String rootName, Map attrs) {
if (instance == null) {
return false;
}
Set attributeNames = getAllInterfaces(instance.getClass()).stream()
.filter(ReflectionUtils::isBeanInterface)
.flatMap(i -> Arrays.stream(i.getMethods())
.filter(ReflectionUtils::isGetter)
.map(Method::getName))
.collect(toSet());
RefBoolean changed = new RefBoolean(false);
Arrays.stream(instance.getClass().getMethods())
.filter(method -> isGetter(method) && (attributeNames.contains(method.getName())
|| Arrays.stream(method.getAnnotations()).anyMatch(a -> a.annotationType() == JmxAttribute.class)))
.sorted(Comparator.comparing(Method::getName))
.forEach(method -> {
Object fieldValue;
method.setAccessible(true); // anonymous classes do not work without this, wow
try {
fieldValue = method.invoke(instance);
} catch (IllegalAccessException | InvocationTargetException e) {
return;
}
changed.set(true);
JmxAttribute annotation = method.getAnnotation(JmxAttribute.class);
String name = annotation == null || annotation.name().equals(JmxAttribute.USE_GETTER_NAME) ?
extractFieldNameFromGetter(method) :
annotation.name();
name = rootName.isEmpty() ? name : rootName + (name.isEmpty() ? "" : "_" + name);
if (fieldValue != instance && !attrs.containsKey(name) && !getJmxAttributes(fieldValue, name, attrs)) {
attrs.put(name, fieldValue);
}
});
return changed.get();
}
public static void resetStats(Object instance) {
visitFields(instance, item -> {
if (item instanceof JmxStatsWithReset) {
((JmxStatsWithReset) item).resetStats();
return true;
}
return false;
});
}
public static void setSmoothingWindow(Object instance, Duration smoothingWindowSeconds) {
visitFields(instance, item -> {
if (item instanceof JmxStatsWithSmoothingWindow) {
((JmxStatsWithSmoothingWindow) item).setSmoothingWindow(smoothingWindowSeconds);
return true;
}
return false;
});
}
@Nullable
public static Duration getSmoothingWindow(Object instance) {
Set result = new HashSet<>();
visitFields(instance, item -> {
if (item instanceof JmxStatsWithSmoothingWindow) {
Duration smoothingWindow = ((JmxStatsWithSmoothingWindow) item).getSmoothingWindow();
result.add(smoothingWindow);
return true;
}
return false;
});
if (result.size() == 1) {
return first(result);
}
return null;
}
public static String getAnnotationString(@NotNull Class extends Annotation> annotationType, @Nullable Annotation annotation) throws ReflectiveOperationException {
if (annotation != null) {
return getAnnotationString(annotation);
}
return annotationType.getSimpleName();
}
/**
* Builds string representation of annotation with its elements.
* The string looks differently depending on the number of elements, that an annotation has.
* If annotation has no elements, string looks like this : "AnnotationName"
* If annotation has a single element with the name "value", string looks like this : "AnnotationName(someValue)"
* If annotation has one or more custom elements, string looks like this : "(key1=value1,key2=value2)"
*/
public static String getAnnotationString(Annotation annotation) throws ReflectiveOperationException {
Class extends Annotation> annotationType = annotation.annotationType();
StringBuilder annotationString = new StringBuilder();
Method[] annotationElements = filterNonEmptyElements(annotation);
if (annotationElements.length == 0) {
// annotation without elements
annotationString.append(annotationType.getSimpleName());
return annotationString.toString();
}
if (annotationElements.length == 1 && annotationElements[0].getName().equals("value")) {
// annotation with single element which has name "value"
annotationString.append(annotationType.getSimpleName());
Object value = fetchAnnotationElementValue(annotation, annotationElements[0]);
annotationString.append('(').append(value.toString()).append(')');
return annotationString.toString();
}
// annotation with one or more custom elements
annotationString.append('(');
for (Method annotationParameter : annotationElements) {
Object value = fetchAnnotationElementValue(annotation, annotationParameter);
String nameKey = annotationParameter.getName();
String nameValue = value.toString();
annotationString.append(nameKey).append('=').append(nameValue).append(',');
}
assert annotationString.substring(annotationString.length() - 1).equals(",");
annotationString = new StringBuilder(annotationString.substring(0, annotationString.length() - 1));
annotationString.append(')');
return annotationString.toString();
}
public static Method[] filterNonEmptyElements(Annotation annotation) throws ReflectiveOperationException {
List filtered = new ArrayList<>();
for (Method method : annotation.annotationType().getDeclaredMethods()) {
Object elementValue = fetchAnnotationElementValue(annotation, method);
if (elementValue instanceof String) {
String stringValue = (String) elementValue;
if (stringValue.length() == 0) {
// skip this element, because it is empty string
continue;
}
}
filtered.add(method);
}
return filtered.toArray(new Method[0]);
}
/**
* Returns values if it is not null, otherwise throws exception
*/
public static Object fetchAnnotationElementValue(Annotation annotation, Method element) throws ReflectiveOperationException {
Object value = element.invoke(annotation);
if (value == null) {
String errorMsg = "@" + annotation.annotationType().getName() + "." +
element.getName() + "() returned null";
throw new NullPointerException(errorMsg);
}
return value;
}
public static boolean isPrivateApiAvailable() {
try {
Class.forName("java.lang.Module");
return false;
} catch (ClassNotFoundException e) {
return true;
}
}
}