Skip to content

Commit

Permalink
New AnnotationFinder to handle transitive annotations in FT (#4216)
Browse files Browse the repository at this point in the history
* New AnnotationFinder to handle transitive annotations in FT used by interceptor bindings. This allows users to define new annotations that are themselves annotated with FT annotations to be used. See new tests and Issue 4171.

Signed-off-by: Santiago Pericasgeertsen <[email protected]>

* Use AnnotationType and AnnotationMethod for annotation lookups instead of the corresponding Java classes.

Signed-off-by: Santiago Pericasgeertsen <[email protected]>

* Additional cleanup of unused code.

Signed-off-by: Santiago Pericasgeertsen <[email protected]>

* Removed BeanMethod pairing in extension.

Signed-off-by: Santiago Pericasgeertsen <[email protected]>

* Check if an annotation type is an interceptor binding using CDI's bean manager when available.

Signed-off-by: Santiago Pericasgeertsen <[email protected]>
  • Loading branch information
spericas authored May 18, 2022
1 parent 0c3530d commit ae7978e
Show file tree
Hide file tree
Showing 15 changed files with 493 additions and 199 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
*
* 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.helidon.microprofile.faulttolerance;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

import javax.enterprise.inject.spi.BeanManager;
import javax.interceptor.InterceptorBinding;

/**
* Searches for transitive annotations associated with interceptor bindings in
* a given Java package. Some of these operations can be expensive, so their
* results should be cached.
*
* For example, a new annotation {@code @TimedRetry} is itself annotated by
* {@code @Timeout} and {@code @Retry}, and both need to be found if a method
* uses {@code @TimedRetry} instead.
*/
public class AnnotationFinder {

/**
* Array of package prefixes we avoid traversing while computing
* a transitive closure for an annotation.
*/
private static final String[] SKIP_PACKAGE_PREFIXES = {
"java.",
"javax.",
"jakarta.",
"org.microprofile."
};

private final Package pkg;

private AnnotationFinder(Package pkg) {
this.pkg = pkg;
}

/**
* Create a find given a Java package.
*
* @param pkg a package
* @return the finder
*/
static AnnotationFinder create(Package pkg) {
Objects.requireNonNull(pkg);
return new AnnotationFinder(pkg);
}

Set<Annotation> findAnnotations(Set<Annotation> set, BeanManager bm) {
return findAnnotations(set, new HashSet<>(), new HashSet<>(), pkg, bm);
}

/**
* Collects a set of transitive annotations in a package. Follows any
* annotation that has not been already seen (to avoid infinite loops)
* and is of interest.
*
* @param set set of annotations to start with
* @param result set of annotations returned
* @param seen set of annotations already processed
* @param pkg the package
* @return the result set of annotations
*/
private Set<Annotation> findAnnotations(Set<Annotation> set, Set<Annotation> result,
Set<Annotation> seen, Package pkg, BeanManager bm) {
for (Annotation a1 : set) {
Class<? extends Annotation> a1Type = a1.annotationType();
if (a1Type.getPackage().equals(pkg)) {
result.add(a1);
} else if (!seen.contains(a1) && isOfInterest(a1, bm)) {
seen.add(a1);
Set<Annotation> a1Set = Set.of(a1Type.getAnnotations());
findAnnotations(a1Set, result, seen, pkg, bm);
}
}
return result;
}

private boolean isOfInterest(Annotation a, BeanManager bm) {
if (bm != null && bm.isInterceptorBinding(a.annotationType())
|| a.annotationType().isAnnotationPresent(InterceptorBinding.class)) {
Optional<String> matches = Stream.of(SKIP_PACKAGE_PREFIXES)
.filter(pp -> a.annotationType().getPackage().getName().startsWith(pp))
.findAny();
return matches.isEmpty();
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,25 @@

package io.helidon.microprofile.faulttolerance;

import java.lang.reflect.Method;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Future;

import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;

import org.eclipse.microprofile.faulttolerance.Asynchronous;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;

/**
* Class AsynchronousAntn.
*/
class AsynchronousAntn extends MethodAntn implements Asynchronous {

/**
* Constructor.
*
* @param beanClass Bean class.
* @param method The method.
* @param annotatedType The annotated type.
* @param annotatedMethod The annotated method.
*/
AsynchronousAntn(Class<?> beanClass, Method method) {
super(beanClass, method);
AsynchronousAntn(AnnotatedType<?> annotatedType, AnnotatedMethod<?> annotatedMethod) {
super(annotatedType, annotatedMethod);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,22 @@

package io.helidon.microprofile.faulttolerance;

import java.lang.reflect.Method;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;

import org.eclipse.microprofile.faulttolerance.Bulkhead;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;

/**
* Class BulkheadAntn.
*/
class BulkheadAntn extends MethodAntn implements Bulkhead {

/**
* Constructor.
*
* @param beanClass Bean class.
* @param method The method.
* @param annotatedType The annotated type.
* @param annotatedMethod The annotated method.
*/
BulkheadAntn(Class<?> beanClass, Method method) {
super(beanClass, method);
BulkheadAntn(AnnotatedType<?> annotatedType, AnnotatedMethod<?> annotatedMethod) {
super(annotatedType, annotatedMethod);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,24 @@

package io.helidon.microprofile.faulttolerance;

import java.lang.reflect.Method;
import java.time.temporal.ChronoUnit;

import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;

import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;

/**
* Class CircuitBreakerAntn.
*/
class CircuitBreakerAntn extends MethodAntn implements CircuitBreaker {

/**
* Constructor.
*
* @param beanClass The bean class.
* @param method The method.
* @param annotatedType The annotated type.
* @param annotatedMethod The annotated method.
*/
CircuitBreakerAntn(Class<?> beanClass, Method method) {
super(beanClass, method);
CircuitBreakerAntn(AnnotatedType<?> annotatedType, AnnotatedMethod<?> annotatedMethod) {
super(annotatedType, annotatedMethod);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,24 @@

import java.lang.reflect.Method;

import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;

import org.eclipse.microprofile.faulttolerance.ExecutionContext;
import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.FallbackHandler;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;

/**
* Class FallbackAntn.
*/
class FallbackAntn extends MethodAntn implements Fallback {

/**
* Constructor.
*
* @param beanClass Bean class.
* @param method The method.
* @param annotatedType The annotated type.
* @param annotatedMethod The annotated method.
*/
FallbackAntn(Class<?> beanClass, Method method) {
super(beanClass, method);
FallbackAntn(AnnotatedType<?> annotatedType, AnnotatedMethod<?> annotatedMethod) {
super(annotatedType, annotatedMethod);
}

@Override
Expand Down
Loading

0 comments on commit ae7978e

Please sign in to comment.