/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.everrest.core.impl.resource;
import com.google.common.base.MoreObjects;
import org.everrest.core.BaseObjectModel;
import org.everrest.core.Parameter;
import org.everrest.core.impl.header.MediaTypeHelper;
import org.everrest.core.impl.method.MethodParameter;
import org.everrest.core.resource.ResourceDescriptor;
import org.everrest.core.resource.ResourceMethodDescriptor;
import org.everrest.core.resource.SubResourceLocatorDescriptor;
import org.everrest.core.resource.SubResourceMethodDescriptor;
import org.everrest.core.uri.UriPattern;
import org.everrest.core.util.ResourceMethodComparator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.Encoded;
import javax.ws.rs.FormParam;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import static javax.ws.rs.core.MediaType.WILDCARD_TYPE;
import static org.everrest.core.impl.header.MediaTypeHelper.WADL_TYPE;
import static org.everrest.core.impl.method.ParameterHelper.RESOURCE_METHOD_PARAMETER_ANNOTATIONS;
/**
* @author andrew00x
*/
public class AbstractResourceDescriptor extends BaseObjectModel implements ResourceDescriptor {
/** Logger. */
private static final Logger LOG = LoggerFactory.getLogger(AbstractResourceDescriptor.class);
/** PathValue. */
private final PathValue path;
/** UriPattern. */
private final UriPattern uriPattern;
/** Sub-resource methods. Sub-resource method has path annotation. */
private final TreeMap<UriPattern, Map<String, List<SubResourceMethodDescriptor>>> subResourceMethods;
/** Sub-resource locators. Sub-resource locator has path annotation. */
private final TreeMap<UriPattern, SubResourceLocatorDescriptor> subResourceLocators;
/** Resource methods. Resource method has not own path annotation. */
private final MultivaluedMap<String, ResourceMethodDescriptor> resourceMethods;
private final ResourceMethodComparator resourceMethodComparator = new ResourceMethodComparator();
/**
* Constructs new instance of AbstractResourceDescriptor.
*
* @param resourceClass
* resource class
*/
public AbstractResourceDescriptor(Class<?> resourceClass) {
this(PathValue.getPath(resourceClass.getAnnotation(Path.class)), resourceClass);
}
public AbstractResourceDescriptor(String path, Class<?> resourceClass) {
super(resourceClass);
if (path == null) {
this.path = null;
this.uriPattern = null;
} else {
this.path = new PathValue(path);
this.uriPattern = new UriPattern(path);
}
this.resourceMethods = new MultivaluedHashMap<>();
this.subResourceMethods = new TreeMap<>(UriPattern.URIPATTERN_COMPARATOR);
this.subResourceLocators = new TreeMap<>(UriPattern.URIPATTERN_COMPARATOR);
processMethods();
}
/**
* Constructs new instance of AbstractResourceDescriptor.
*
* @param resource
* resource
*/
public AbstractResourceDescriptor(Object resource) {
this(resource.getClass());
}
public AbstractResourceDescriptor(String path, Object resource) {
this(path, resource.getClass());
}
@Override
public PathValue getPathValue() {
return path;
}
@Override
public Map<String, List<ResourceMethodDescriptor>> getResourceMethods() {
return resourceMethods;
}
@Override
public Map<UriPattern, SubResourceLocatorDescriptor> getSubResourceLocators() {
return subResourceLocators;
}
@Override
public Map<UriPattern, Map<String, List<SubResourceMethodDescriptor>>> getSubResourceMethods() {
return subResourceMethods;
}
@Override
public UriPattern getUriPattern() {
return uriPattern;
}
@Override
public boolean isRootResource() {
return path != null;
}
/**
* Process method of resource and separate them to three types Resource Methods, Sub-Resource Methods and
* Sub-Resource Locators.
*/
private void processMethods() {
Class<?> resourceClass = getObjectClass();
for (Method method : getAllMethods(resourceClass)) {
Path subPath = getMethodAnnotation(method, resourceClass, Path.class, false);
HttpMethod httpMethod = getMethodAnnotation(method, resourceClass, HttpMethod.class, true);
if (subPath != null || httpMethod != null) {
if (Modifier.isPublic(method.getModifiers())) {
List<Parameter> methodParameters = createMethodParameters(resourceClass, method);
Annotation securityAnnotation = getSecurityAnnotation(method, resourceClass);
Annotation[] additionalAnnotations = securityAnnotation != null ? new Annotation[]{securityAnnotation} : new Annotation[0];
if (httpMethod != null) {
Produces producesAnnotation = getMethodAnnotation(method, resourceClass, Produces.class, false);
if (producesAnnotation == null) {
producesAnnotation = getClassAnnotation(resourceClass, Produces.class);
}
List<MediaType> produces = MediaTypeHelper.createProducesList(producesAnnotation);
Consumes consumesAnnotation = getMethodAnnotation(method, resourceClass, Consumes.class, false);
if (consumesAnnotation == null) {
consumesAnnotation = getClassAnnotation(resourceClass, Consumes.class);
}
List<MediaType> consumes = MediaTypeHelper.createConsumesList(consumesAnnotation);
if (subPath == null) {
addResourceMethod(method, httpMethod, methodParameters, additionalAnnotations, produces, consumes);
} else {
addSubResourceMethod(method, subPath, httpMethod, methodParameters, additionalAnnotations, produces, consumes);
}
} else {
addSubResourceLocator(method, subPath, methodParameters, additionalAnnotations);
}
} else {
LOG.warn("Non-public method {} in {} annotated with @Path of HTTP method annotation, it's ignored", method.getName(), clazz.getName());
}
}
}
if (resourceMethods.size() + subResourceMethods.size() + subResourceLocators.size() == 0) {
LOG.warn("Not found any resource methods, sub-resource methods or sub-resource locators in {}", resourceClass.getName());
}
// End method processing. Start HEAD and OPTIONS resolving, see JAX-RS (JSR-311) specification section 3.3.5
resolveHeadRequest();
resolveOptionsRequest();
sortResourceMethods();
sortSubResourceMethods();
}
private List<Method> getAllMethods(Class<?> resourceClass) {
List<Method> methods = new ArrayList<>();
Collections.addAll(methods, resourceClass.getDeclaredMethods());
List<Method> inheritedMethods = new ArrayList<>();
Class<?> superclass = resourceClass.getSuperclass();
while (superclass != null && superclass != Object.class) {
Collections.addAll(inheritedMethods, superclass.getDeclaredMethods());
superclass = superclass.getSuperclass();
}
for (Method method : methods) {
for (Iterator<Method> iterator = inheritedMethods.iterator(); iterator.hasNext(); ) {
Method inheritedMethod = iterator.next();
if (Objects.equals(method.getName(), inheritedMethod.getName())
&& method.getReturnType() == inheritedMethod.getReturnType() &&
Arrays.equals(method.getParameterTypes(), inheritedMethod.getParameterTypes())) {
iterator.remove();
}
}
}
methods.addAll(inheritedMethods);
return methods;
}
private void addResourceMethod(Method method, HttpMethod httpMethod, List<Parameter> params, Annotation[] additional, List<MediaType> produces, List<MediaType> consumes) {
ResourceMethodDescriptor resourceMethod = new ResourceMethodDescriptorImpl(method, httpMethod.value(), params, this, consumes, produces, additional);
validateResourceMethod(resourceMethod);
ResourceMethodDescriptor existedResourceMethod = findMethodResourceMediaType(getResourceMethods(httpMethod.value()), resourceMethod.consumes(), resourceMethod.produces());
if (existedResourceMethod != null) {
throw new RuntimeException(String.format("Two resource method %s and %s with the same HTTP method, consumes and produces found", resourceMethod, existedResourceMethod));
}
resourceMethods.add(httpMethod.value(), resourceMethod);
}
private void addSubResourceMethod(Method method, Path subPath, HttpMethod httpMethod, List<Parameter> params, Annotation[] additional, List<MediaType> produces, List<MediaType> consumes) {
SubResourceMethodDescriptor subResourceMethod = new SubResourceMethodDescriptorImpl(new PathValue(subPath.value()), method, httpMethod.value(), params, this, consumes, produces, additional);
validateResourceMethod(subResourceMethod);
Map<String, List<SubResourceMethodDescriptor>> subResourceMethods = getSubResourceMethods(subResourceMethod.getUriPattern());
SubResourceMethodDescriptor existedSubResourceMethod = (SubResourceMethodDescriptor)findMethodResourceMediaType(subResourceMethods.get(httpMethod.value()), subResourceMethod.consumes(), subResourceMethod.produces());
if (existedSubResourceMethod != null) {
throw new RuntimeException(String.format("Two sub-resource method %s and %s with the same HTTP method, path, consumes and produces found", subResourceMethod, existedSubResourceMethod));
}
List<SubResourceMethodDescriptor> methodList = subResourceMethods.get(httpMethod.value());
if (methodList == null) {
methodList = new ArrayList<>();
subResourceMethods.put(httpMethod.value(), methodList);
}
methodList.add(subResourceMethod);
}
private void validateResourceMethod(ResourceMethodDescriptor resourceMethod) {
List<Parameter> methodParameters = resourceMethod.getMethodParameters();
int numberOfEntityParameters = (int)methodParameters.stream().filter(parameter -> parameter.getAnnotation() == null).count();
if (numberOfEntityParameters > 1) {
throw new RuntimeException(String.format("Method %s has %d parameters that are not annotated with JAX-RS parameter annotations, but must not have more than one",
resourceMethod.getMethod().getName(), numberOfEntityParameters));
}
boolean isAnyParameterAnnotatedWithFormParam = methodParameters.stream().anyMatch(parameter -> parameter.getAnnotation() != null &&
parameter.getAnnotation().annotationType() == FormParam.class);
if (isAnyParameterAnnotatedWithFormParam && numberOfEntityParameters == 1) {
boolean entityParameterIsMultivaluedMap = false;
Parameter entityParameter = methodParameters.stream().filter(parameter -> parameter.getAnnotation() == null).findFirst().get();
if (entityParameter.getParameterClass() == MultivaluedMap.class && entityParameter.getGenericType() instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType)entityParameter.getGenericType()).getActualTypeArguments();
if (actualTypeArguments.length == 2 && String.class == actualTypeArguments[0] && String.class == actualTypeArguments[1]) {
entityParameterIsMultivaluedMap = true;
}
}
if (!entityParameterIsMultivaluedMap) {
throw new RuntimeException("At least one method's parameter is annotated with FormParam, entity parameter might not be other than MultivaluedMap<String, String>");
}
}
}
private void addSubResourceLocator(Method method, Path subPath, List<Parameter> params, Annotation[] additional) {
SubResourceLocatorDescriptor resourceLocator = new SubResourceLocatorDescriptorImpl(new PathValue(subPath.value()), method, params, this, additional);
validateSubResourceLocator(resourceLocator);
if (subResourceLocators.containsKey(resourceLocator.getUriPattern())) {
throw new RuntimeException(String.format("Two sub-resource locators %s and %s with the same path found", resourceLocator, subResourceLocators.get(resourceLocator.getUriPattern())));
}
subResourceLocators.put(resourceLocator.getUriPattern(), resourceLocator);
}
private void validateSubResourceLocator(SubResourceLocatorDescriptor resourceLocator) {
List<Parameter> methodParameters = resourceLocator.getMethodParameters();
boolean hasEntityParameter = methodParameters.stream().anyMatch(parameter -> parameter.getAnnotation() == null);
if (hasEntityParameter) {
throw new RuntimeException(String.format("Method %s is resource locator, it must not have not JAX-RS annotated (entity) parameters",
resourceLocator.getMethod().getName()));
}
}
private void sortResourceMethods() {
for (List<ResourceMethodDescriptor> resourceMethods : this.resourceMethods.values()) {
Collections.sort(resourceMethods, resourceMethodComparator);
}
}
private List<ResourceMethodDescriptor> getResourceMethods(String httpMethod) {
List<ResourceMethodDescriptor> methodDescriptors = resourceMethods.get(httpMethod);
if (methodDescriptors == null) {
methodDescriptors = new ArrayList<>();
resourceMethods.put(httpMethod, methodDescriptors);
}
return methodDescriptors;
}
private Map<String, List<SubResourceMethodDescriptor>> getSubResourceMethods(UriPattern subResourceUriPattern) {
Map<String, List<SubResourceMethodDescriptor>> map = subResourceMethods.get(subResourceUriPattern);
if (map == null) {
map = new MultivaluedHashMap<>();
subResourceMethods.put(subResourceUriPattern, map);
}
return map;
}
private void sortSubResourceMethods() {
for (Map<String, List<SubResourceMethodDescriptor>> subResourceMethods : this.subResourceMethods.values()) {
for (List<SubResourceMethodDescriptor> resourceMethods : subResourceMethods.values()) {
Collections.sort(resourceMethods, resourceMethodComparator);
}
}
}
/**
* Create list of {@link Parameter} .
*
* @param resourceClass
* class
* @param method
* See {@link java.lang.reflect.Method}
* @return list of {@link Parameter}
*/
private List<Parameter> createMethodParameters(Class<?> resourceClass, Method method) {
Class<?>[] parameterClasses = method.getParameterTypes();
if (parameterClasses.length > 0) {
Type[] parameterGenTypes = method.getGenericParameterTypes();
Annotation[][] annotations = method.getParameterAnnotations();
List<Parameter> methodParameters = new ArrayList<>(parameterClasses.length);
boolean classEncoded = getClassAnnotation(resourceClass, Encoded.class) != null;
boolean methodEncoded = getMethodAnnotation(method, resourceClass, Encoded.class, false) != null;
for (int i = 0; i < parameterClasses.length; i++) {
String defaultValue = null;
Annotation parameterAnnotation = null;
boolean encoded = false;
for (int j = 0; j < annotations[i].length; j++) {
Annotation annotation = annotations[i][j];
Class<?> annotationType = annotation.annotationType();
if (RESOURCE_METHOD_PARAMETER_ANNOTATIONS.contains(annotationType.getName())) {
if (parameterAnnotation != null) {
String msg = String.format(
"JAX-RS annotations on one of method parameters of resource %s, method %s are equivocality. Annotations: %s and %s can't be applied to one parameter",
toString(), method.getName(), parameterAnnotation, annotation);
throw new RuntimeException(msg);
}
parameterAnnotation = annotation;
} else if (annotationType == Encoded.class) {
encoded = true;
} else if (annotationType == DefaultValue.class) {
defaultValue = ((DefaultValue)annotation).value();
}
}
Parameter methodParameter = new MethodParameter(
parameterAnnotation,
annotations[i],
parameterClasses[i],
parameterGenTypes[i],
defaultValue,
encoded || methodEncoded || classEncoded);
methodParameters.add(methodParameter);
}
return methodParameters;
}
return Collections.emptyList();
}
/**
* According to JSR-311:
* <p>
* On receipt of a HEAD request an implementation MUST either: 1. Call method annotated with request method
* designation for HEAD or, if none present, 2. Call method annotated with a request method designation GET and
* discard any returned entity.
* </p>
*/
private void resolveHeadRequest() {
List<ResourceMethodDescriptor> getResources = resourceMethods.get(HttpMethod.GET);
if (getResources != null && getResources.size() > 0) {
List<ResourceMethodDescriptor> headResources = getResourceMethods(HttpMethod.HEAD);
for (ResourceMethodDescriptor resourceMethod : getResources) {
if (findMethodResourceMediaType(headResources, resourceMethod.consumes(), resourceMethod.produces()) == null) {
headResources.add(
new ResourceMethodDescriptorImpl(resourceMethod.getMethod(), HttpMethod.HEAD,
resourceMethod.getMethodParameters(), this, resourceMethod.consumes(),
resourceMethod.produces(),
resourceMethod.getAnnotations()));
}
}
}
for (Map<String, List<SubResourceMethodDescriptor>> allSubResourceMethods : subResourceMethods.values()) {
List<SubResourceMethodDescriptor> getSubResources = allSubResourceMethods.get(HttpMethod.GET);
if (getSubResources != null && getSubResources.size() > 0) {
List<SubResourceMethodDescriptor> headSubResources = allSubResourceMethods.get(HttpMethod.HEAD);
if (headSubResources == null) {
headSubResources = new ArrayList<>();
allSubResourceMethods.put(HttpMethod.HEAD, headSubResources);
}
for (SubResourceMethodDescriptor subResourceMethod : getSubResources) {
if (findMethodResourceMediaType(headSubResources, subResourceMethod.consumes(), subResourceMethod.produces()) == null) {
headSubResources.add(
new SubResourceMethodDescriptorImpl(subResourceMethod.getPathValue(), subResourceMethod.getMethod(),
HttpMethod.HEAD, subResourceMethod.getMethodParameters(), this,
subResourceMethod.consumes(),
subResourceMethod.produces(), subResourceMethod.getAnnotations()));
}
}
}
}
}
/**
* According to JSR-311:
* <p>
* On receipt of a OPTIONS request an implementation MUST either: 1. Call method annotated with request method
* designation for OPTIONS or, if none present, 2. Generate an automatic response using the metadata provided by the
* JAX-RS annotations on the matching class and its methods.
* </p>
*/
private void resolveOptionsRequest() {
List<ResourceMethodDescriptor> optionResources = getResourceMethods(HttpMethod.OPTIONS);
if (optionResources.isEmpty()) {
List<Parameter> methodParameters = Collections.emptyList();
List<MediaType> consumes = Collections.singletonList(WILDCARD_TYPE);
List<MediaType> produces = Collections.singletonList(WADL_TYPE);
optionResources.add(new OptionsRequestResourceMethodDescriptorImpl("OPTIONS", methodParameters, this, consumes, produces, new Annotation[0]));
}
}
/**
* Get all method with at least one annotation which has annotation <i>annotation</i>. It is useful for annotation
* {@link javax.ws.rs.GET}, etc. All HTTP method annotations has annotation {@link javax.ws.rs.HttpMethod}.
*
* @param <T>
* annotation type
* @param method
* method
* @param annotationClass
* annotation class
* @return list of annotation
*/
private <T extends Annotation> T getMetaAnnotation(Method method, Class<T> annotationClass) {
for (Annotation annotation : method.getAnnotations()) {
T result;
if ((result = annotation.annotationType().getAnnotation(annotationClass)) != null) {
return result;
}
}
return null;
}
/**
* Tries to get JAX-RS annotation on method from the resource class's superclasses or implemented interfaces.
*
* @param <T>
* annotation type
* @param method
* method for discovering
* @param resourceClass
* class that contains discovered method
* @param annotationClass
* annotation type what we are looking for
* @param metaAnnotation
* false if annotation should be on method and true in method should contain annotations that
* has supplied annotation
* @return annotation from class or its ancestor or null if nothing found
*/
private <T extends Annotation> T getMethodAnnotation(Method method,
Class<?> resourceClass,
Class<T> annotationClass,
boolean metaAnnotation) {
T annotation = metaAnnotation ? getMetaAnnotation(method, annotationClass) : method.getAnnotation(annotationClass);
if (annotation == null) {
Method myMethod;
Class<?> myClass = resourceClass;
while (annotation == null && myClass != null && myClass != Object.class) {
for (Class<?> anInterface : myClass.getInterfaces()) {
try {
myMethod = anInterface.getDeclaredMethod(method.getName(), method.getParameterTypes());
T newAnnotation = metaAnnotation ? getMetaAnnotation(myMethod, annotationClass) : myMethod.getAnnotation(annotationClass);
if (annotation == null) {
annotation = newAnnotation;
} else {
throw new RuntimeException(String.format("Conflicts of JAX-RS annotations on method %s of resource %s. " +
"Method is declared in more than one interface and different interfaces contains JAX-RS annotations.",
myMethod.getName(), resourceClass.getName()));
}
} catch (NoSuchMethodException ignored) {
}
}
if (annotation == null) {
myClass = myClass.getSuperclass();
if (myClass != null && myClass != Object.class) {
try {
myMethod = myClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
annotation = metaAnnotation ? getMetaAnnotation(myMethod, annotationClass) : myMethod.getAnnotation(annotationClass);
} catch (NoSuchMethodException ignored) {
}
}
}
}
}
return annotation;
}
/** Tries to get JAX-RS annotation on class, superclasses or implemented interfaces. */
private <T extends Annotation> T getClassAnnotation(Class<?> resourceClass, Class<T> annotationClass) {
T annotation = resourceClass.getAnnotation(annotationClass);
if (annotation == null) {
Class<?> myClass = resourceClass;
while (annotation == null && myClass != null && myClass != Object.class) {
for (Class<?> anInterface : myClass.getInterfaces()) {
T newAnnotation = anInterface.getAnnotation(annotationClass);
if (annotation == null) {
annotation = newAnnotation;
} else {
throw new RuntimeException(String.format("Conflict of JAX-RS annotation on class %s. " +
"Class implements more that one interface and few interfaces have JAX-RS annotations.", resourceClass.getName()));
}
}
if (annotation == null) {
myClass = myClass.getSuperclass();
if (myClass != null && myClass != Object.class) {
annotation = myClass.getAnnotation(annotationClass);
}
}
}
}
return annotation;
}
/**
* Check is collection of {@link org.everrest.core.resource.ResourceMethodDescriptor} already contains ResourceMethodDescriptor with
* the same media types.
*
* @param resourceMethods
* {@link java.util.Set} of {@link org.everrest.core.resource.ResourceMethodDescriptor}
* @param consumes
* resource method consumed media type
* @param produces
* resource method produced media type
* @return ResourceMethodDescriptor or null if nothing found
*/
private <T extends ResourceMethodDescriptor> ResourceMethodDescriptor findMethodResourceMediaType(List<T> resourceMethods,
List<MediaType> consumes,
List<MediaType> produces) {
if (resourceMethods == null || resourceMethods.isEmpty()) {
return null;
}
ResourceMethodDescriptor matched = null;
for (Iterator<T> iterator = resourceMethods.iterator(); matched == null && iterator.hasNext(); ) {
T method = iterator.next();
if (method.consumes().size() != consumes.size() || method.produces().size() != produces.size()) {
continue;
}
if (method.consumes().containsAll(consumes) && method.produces().containsAll(produces)) {
matched = method;
}
}
return matched;
}
/**
* Get security annotation (DenyAll, RolesAllowed, PermitAll) from <code>method</code> or class
* <code>clazz</class> which contains method.
* Supper class or implemented interfaces will be also checked. Annotation
* on method has the advantage on annotation on class or interface.
*
* @param method
* method to be checked for security annotation
* @param clazz
* class which contains <code>method</code>
* @return one of security annotation or <code>null</code> is no such annotation found
* @see javax.annotation.security.DenyAll
* @see javax.annotation.security.RolesAllowed
* @see javax.annotation.security.PermitAll
*/
@SuppressWarnings("unchecked")
private <T extends Annotation> T getSecurityAnnotation(Method method, Class<?> clazz) {
Class<T>[] securityAnnotationClassesClasses = new Class[]{DenyAll.class, RolesAllowed.class, PermitAll.class};
T annotation = getAnnotation(method, securityAnnotationClassesClasses);
if (annotation == null) {
annotation = getAnnotation(clazz, securityAnnotationClassesClasses);
if (annotation == null) {
Method myMethod;
Class<?> myClass = clazz;
while (annotation == null && myClass != null && myClass != Object.class) {
Class<?>[] interfaces = myClass.getInterfaces();
for (int i = 0; annotation == null && i < interfaces.length; i++) {
try {
myMethod = interfaces[i].getDeclaredMethod(method.getName(), method.getParameterTypes());
annotation = getAnnotation(myMethod, securityAnnotationClassesClasses);
} catch (NoSuchMethodException ignored) {
}
if (annotation == null) {
annotation = getAnnotation(interfaces[i], securityAnnotationClassesClasses);
}
}
if (annotation == null) {
myClass = myClass.getSuperclass();
if (myClass != null && myClass != Object.class) {
try {
myMethod = myClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
annotation = getAnnotation(myMethod, securityAnnotationClassesClasses);
} catch (NoSuchMethodException ignored) {
}
if (annotation == null) {
annotation = getAnnotation(myClass, securityAnnotationClassesClasses);
}
}
}
}
}
}
return annotation;
}
private <T extends Annotation> T getAnnotation(Class<?> clazz, Class<T>[] annotationClasses) {
T annotation = null;
for (int i = 0; annotation == null && i < annotationClasses.length; i++) {
annotation = clazz.getAnnotation(annotationClasses[i]);
}
return annotation;
}
private <T extends Annotation> T getAnnotation(Method method, Class<T>[] annotationClasses) {
T annotation = null;
for (int i = 0; annotation == null && i < annotationClasses.length; i++) {
annotation = method.getAnnotation(annotationClasses[i]);
}
return annotation;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("path", path)
.add("isRootResource", isRootResource())
.add("class", clazz)
.omitNullValues()
.toString();
}
}