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.
/*
* Copyright 2017 Red Hat, Inc, and individual contributors.
*
* 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.smallrye.faulttolerance.internal;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
final class SecurityActions {
private SecurityActions() {
}
static void setAccessible(final AccessibleObject accessibleObject) {
if (System.getSecurityManager() == null) {
accessibleObject.setAccessible(true);
}
AccessController.doPrivileged((PrivilegedAction) () -> {
accessibleObject.setAccessible(true);
return accessibleObject;
});
}
/**
* Finds a fallback method for given guarded method. If the guarded method is present on given {@code beanClass}
* and is actually declared by given {@code declaringClass} and has given {@code parameterTypes} and {@code returnType},
* then a fallback method of given {@code name}, with parameter types and return type matching the parameter types
* and return type of the guarded method, is searched for on the {@code beanClass} and its superclasses and
* superinterfaces, according to the specification rules. Returns {@code null} if no matching fallback method exists.
*
* @param beanClass the class of the bean that has the guarded method
* @param declaringClass the class that actually declares the guarded method (can be a supertype of bean class)
* @param name name of the fallback method
* @param parameterTypes parameter types of the guarded method
* @param returnType return type of the guarded method
* @return the fallback method or {@code null} if none exists
*/
static Method findFallbackMethod(Class> beanClass, Class> declaringClass,
String name, Type[] parameterTypes, Type returnType) throws PrivilegedActionException {
Set result;
if (System.getSecurityManager() == null) {
result = doFindFallbackMethod(beanClass, declaringClass, name, parameterTypes, returnType, false);
} else {
result = AccessController.doPrivileged((PrivilegedExceptionAction>) () -> {
return doFindFallbackMethod(beanClass, declaringClass, name, parameterTypes, returnType, false);
});
}
return result.isEmpty() ? null : result.iterator().next();
}
/**
* Finds a set of fallback methods with exception parameter for given guarded method. If the guarded method
* is present on given {@code beanClass} and is actually declared by given {@code declaringClass} and has given
* {@code parameterTypes} and {@code returnType}, then fallback methods of given {@code name}, with parameter types
* and return type matching the parameter types and return type of the guarded method, and with one additional
* parameter assignable to {@code Throwable} at the end of parameter list, is searched for on the {@code beanClass}
* and its superclasses and superinterfaces, according to the specification rules. Returns {@code null} if no
* matching fallback method exists.
*
* @param beanClass the class of the bean that has the guarded method
* @param declaringClass the class that actually declares the guarded method (can be a supertype of bean class)
* @param name name of the fallback method
* @param parameterTypes parameter types of the guarded method
* @param returnType return type of the guarded method
* @return the fallback method or {@code null} if none exists
*/
static Set findFallbackMethodsWithExceptionParammeter(Class> beanClass, Class> declaringClass,
String name, Type[] parameterTypes, Type returnType) throws PrivilegedActionException {
if (System.getSecurityManager() == null) {
return doFindFallbackMethod(beanClass, declaringClass, name, parameterTypes, returnType, true);
}
return AccessController.doPrivileged((PrivilegedExceptionAction>) () -> {
return doFindFallbackMethod(beanClass, declaringClass, name, parameterTypes, returnType, true);
});
}
private static Set doFindFallbackMethod(Class> beanClass, Class> declaringClass, String name,
Type[] expectedParameterTypes, Type expectedReturnType, boolean expectedExceptionParameter) {
Set result = new HashSet<>();
TypeMapping expectedMapping = TypeMapping.createFor(beanClass, declaringClass);
TypeMapping actualMapping = new TypeMapping();
// if we find a matching method on the bean class or one of its superclasses or superinterfaces,
// then we have to check that the method is either identical to or an override of a method that:
// - is declared on a class which is a supertype of the declaring class, or
// - is declared on an interface which implemented by the declaring class
//
// this is to satisfy the specification, which says: fallback method must be on the same class, a superclass
// or an implemented interface of the class which declares the annotated method
//
// we fake this by checking that the matching method has the same name as one of the method declared on
// the declaring class or any of its superclasses or any of its implemented interfaces (this is actually
// quite precise, the only false positive would occur in presence of overloads)
Set possibleFallbackMethodNames = findPossibleFallbackMethodNames(declaringClass);
Class> clazz = beanClass;
while (true) {
Set methods = getMethodsFromClass(clazz, name, expectedParameterTypes, expectedReturnType,
expectedExceptionParameter, declaringClass, actualMapping, expectedMapping);
for (Method method : methods) {
if (possibleFallbackMethodNames.contains(method.getName())) {
result.add(method);
if (!expectedExceptionParameter) {
return result;
}
}
}
if (clazz.getSuperclass() == null) {
break;
}
actualMapping = actualMapping.getSuperclassMapping(clazz);
clazz = clazz.getSuperclass();
}
for (Class> iface : beanClass.getInterfaces()) {
Set methods = getMethodsFromClass(iface, name, expectedParameterTypes, expectedReturnType,
expectedExceptionParameter, declaringClass, actualMapping, expectedMapping);
for (Method method : methods) {
if (possibleFallbackMethodNames.contains(method.getName())) {
result.add(method);
if (!expectedExceptionParameter) {
return result;
}
}
}
}
return result;
}
private static Set findPossibleFallbackMethodNames(Class> declaringClass) {
Set result = new HashSet<>();
Class> clazz = declaringClass;
while (clazz != null) {
for (Method m : clazz.getDeclaredMethods()) {
result.add(m.getName());
}
clazz = clazz.getSuperclass();
}
for (Class> iface : declaringClass.getInterfaces()) {
for (Method m : iface.getMethods()) {
result.add(m.getName());
}
}
return result;
}
/**
* Returns all methods that:
*
*
are declared directly on given {@code classToSearch},
*
have given {@code name},
*
have matching {@code parameterTypes},
*
have matching {@code returnType},
*
have an additional {@code exceptionParameter} if required,
*
are accessible from given {@code guardedMethodDeclaringClass}.
*
*/
private static Set getMethodsFromClass(Class> classToSearch, String name, Type[] parameterTypes,
Type returnType, boolean exceptionParameter, Class> guardedMethodDeclaringClass,
TypeMapping actualMapping, TypeMapping expectedMapping) {
Set set = new HashSet<>();
for (Method method : classToSearch.getDeclaredMethods()) {
if (method.getName().equals(name)
&& isAccessibleFrom(method, guardedMethodDeclaringClass)
&& signaturesMatch(method, parameterTypes, returnType, exceptionParameter,
actualMapping, expectedMapping)) {
set.add(method);
}
}
return set;
}
private static boolean isAccessibleFrom(Method method, Class> guardedMethodDeclaringClass) {
if (Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers())) {
return true;
}
if (Modifier.isPrivate(method.getModifiers())) {
return method.getDeclaringClass() == guardedMethodDeclaringClass;
}
// not public, not protected and not private => default
// accessible only if in the same package
return method.getDeclaringClass().getPackage() == guardedMethodDeclaringClass.getPackage();
}
private static boolean signaturesMatch(Method method, Type[] expectedParameterTypes, Type expectedReturnType,
boolean expectedExceptionParameter, TypeMapping actualMapping, TypeMapping expectedMapping) {
int expectedParameters = expectedParameterTypes.length;
if (expectedExceptionParameter) {
// need to figure this out _before_ expanding the `expectedParameterTypes` array
boolean kotlinSuspendingFunction = KotlinSupport.isSuspendingFunction(expectedParameterTypes);
// adjust `expectedParameterTypes` so that there's one more element on the position
// where the exception parameter should be, and the value on that position is `null`
expectedParameterTypes = Arrays.copyOfRange(expectedParameterTypes, 0, expectedParameters + 1);
if (kotlinSuspendingFunction) {
expectedParameterTypes[expectedParameters] = expectedParameterTypes[expectedParameters - 1];
expectedParameterTypes[expectedParameters - 1] = null;
}
expectedParameters++;
}
Type[] methodParams = method.getGenericParameterTypes();
if (expectedParameters != methodParams.length) {
return false;
}
for (int i = 0; i < expectedParameters; i++) {
Type methodParam = methodParams[i];
Type expectedParamType = expectedParameterTypes[i];
if (expectedParamType != null) {
if (!typeMatches(methodParam, expectedParamType, actualMapping, expectedMapping)) {
return false;
}
} else { // exception parameter
boolean isThrowable = methodParam instanceof Class
&& Throwable.class.isAssignableFrom((Class>) methodParam);
if (!isThrowable) {
return false;
}
}
}
if (!typeMatches(method.getGenericReturnType(), expectedReturnType, actualMapping, expectedMapping)) {
return false;
}
return true;
}
private static boolean typeMatches(Type actualType, Type expectedType,
TypeMapping actualMapping, TypeMapping expectedMapping) {
actualType = actualMapping.map(actualType);
expectedType = expectedMapping.map(expectedType);
if (actualType instanceof Class) {
return expectedType == actualType;
} else if (isArray(actualType) && isArray(expectedType)) {
return typeMatches(getArrayComponentType(actualType), getArrayComponentType(expectedType),
actualMapping, expectedMapping);
} else if (actualType instanceof ParameterizedType && expectedType instanceof ParameterizedType) {
return parameterizedTypeMatches((ParameterizedType) actualType, (ParameterizedType) expectedType,
actualMapping, expectedMapping);
} else if (actualType instanceof WildcardType && expectedType instanceof WildcardType) {
return wildcardTypeMatches((WildcardType) actualType, (WildcardType) expectedType, actualMapping,
expectedMapping);
} else {
return false;
}
}
private static boolean wildcardTypeMatches(WildcardType actualType, WildcardType expectedType,
TypeMapping actualMapping, TypeMapping expectedMapping) {
boolean lowerBoundsMatch = typeArrayMatches(actualType.getLowerBounds(), expectedType.getLowerBounds(),
actualMapping, expectedMapping);
boolean upperBoundsMatch = typeArrayMatches(actualType.getUpperBounds(), expectedType.getUpperBounds(),
actualMapping, expectedMapping);
return lowerBoundsMatch && upperBoundsMatch;
}
private static boolean parameterizedTypeMatches(ParameterizedType actualType, ParameterizedType expectedType,
TypeMapping actualMapping, TypeMapping expectedMapping) {
boolean genericClassMatch = typeMatches(actualType.getRawType(), expectedType.getRawType(),
actualMapping, expectedMapping);
boolean typeArgumentsMatch = typeArrayMatches(actualType.getActualTypeArguments(),
expectedType.getActualTypeArguments(), actualMapping, expectedMapping);
return genericClassMatch && typeArgumentsMatch;
}
private static boolean typeArrayMatches(Type[] actualTypes, Type[] expectedTypes,
TypeMapping actualMapping, TypeMapping expectedMapping) {
if (actualTypes.length != expectedTypes.length) {
return false;
}
for (int i = 0; i < actualTypes.length; i++) {
if (!typeMatches(actualTypes[i], expectedTypes[i], actualMapping, expectedMapping)) {
return false;
}
}
return true;
}
/**
* Accepts only array {@code Class}es and {@code GenericArrayType}s.
* In other words, {@code isArray(type)} must be {@code true}.
*/
private static Type getArrayComponentType(Type type) {
if (type instanceof Class) {
return ((Class>) type).getComponentType();
} else if (type instanceof GenericArrayType) {
return ((GenericArrayType) type).getGenericComponentType();
} else {
throw new IllegalArgumentException("Not an array: " + type);
}
}
private static boolean isArray(Type parameterType) {
if (parameterType instanceof Class) {
return ((Class>) parameterType).isArray();
} else {
return parameterType instanceof GenericArrayType;
}
}
private static class TypeMapping {
private final Map map;
private TypeMapping() {
this.map = Collections.emptyMap();
}
private TypeMapping(Map map) {
this.map = map;
}
/**
* Bean class can be a subclass of the class that declares the guarded method.
* This method returns a mapping of the type parameters of the method's declaring class
* to the type arguments provided on the bean class or any class between it and the declaring class.
*
* @param beanClass class of the bean which has the guarded method
* @param declaringClass class that actually declares the guarded method
* @return type mapping
*/
private static TypeMapping createFor(Class> beanClass, Class> declaringClass) {
TypeMapping result = new TypeMapping();
if (beanClass == declaringClass) {
return result;
}
Class> current = beanClass;
while (current != declaringClass && current != null) {
if (current.getSuperclass() == null) {
break;
}
result = result.getSuperclassMapping(current);
current = current.getSuperclass();
}
return result;
}
private Type map(Type type) {
Type result = map.get(type);
return result != null ? result : type;
}
private TypeMapping getSuperclassMapping(Class> current) {
return new TypeMapping(mappingForSuperclass(current, this.map));
}
private static Map mappingForSuperclass(Class> clazz, Map previousMapping) {
Class> superclass = clazz.getSuperclass();
TypeVariable>[] typeParameters = superclass.getTypeParameters();
Type genericSuperclass = clazz.getGenericSuperclass();
Type[] typeArguments = (genericSuperclass instanceof ParameterizedType)
? ((ParameterizedType) genericSuperclass).getActualTypeArguments()
: new Type[0];
Map result = new HashMap<>();
for (int i = 0; i < typeArguments.length; i++) {
Type typeArgument = typeArguments[i];
if (typeArgument instanceof Class) {
result.put(typeParameters[i], typeArgument);
} else {
Type type = previousMapping.get(typeArgument);
result.put(typeParameters[i], type != null ? type : typeArgument);
}
}
return result;
}
}
}