Skip to content

Commit

Permalink
Validate @WithExecutor annotations at start up time, during execution…
Browse files Browse the repository at this point in the history
… of the FT CDI extension.
  • Loading branch information
spericas committed Apr 5, 2024
1 parent 9011a59 commit 6f59471
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 35 deletions.
5 changes: 5 additions & 0 deletions microprofile/fault-tolerance/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@
<artifactId>helidon-microprofile-testing-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
package io.helidon.microprofile.faulttolerance;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

import jakarta.annotation.Priority;
Expand All @@ -33,6 +35,7 @@
import jakarta.enterprise.inject.spi.AnnotatedField;
import jakarta.enterprise.inject.spi.AnnotatedMethod;
import jakarta.enterprise.inject.spi.AnnotatedType;
import jakarta.enterprise.inject.spi.Bean;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.BeforeBeanDiscovery;
import jakarta.enterprise.inject.spi.BeforeShutdown;
Expand All @@ -50,6 +53,7 @@
import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.faulttolerance.Timeout;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;
import org.glassfish.jersey.process.internal.RequestScope;

import static jakarta.interceptor.Interceptor.Priority.LIBRARY_BEFORE;
Expand Down Expand Up @@ -210,8 +214,6 @@ void validateAnnotations(BeanManager bm,
@Initialized(ApplicationScoped.class) Object event) {
if (FaultToleranceMetrics.enabled()) {
getRegisteredMethods().forEach(annotatedMethod -> {
final AnnotatedType<?> annotatedType = annotatedMethod.getDeclaringType();

// Metrics depending on the annotationSet present
if (MethodAntn.isAnnotationPresent(annotatedMethod, Retry.class, bm)) {
new RetryAntn(annotatedMethod).validate();
Expand All @@ -230,11 +232,25 @@ void validateAnnotations(BeanManager bm,
}
if (MethodAntn.isAnnotationPresent(annotatedMethod, Asynchronous.class, bm)) {
new AsynchronousAntn(annotatedMethod).validate();
validateWithExecutor(bm, annotatedMethod.getJavaMember());
}
});
}
}

static void validateWithExecutor(BeanManager bm, Method method) {
WithExecutor withExecutor = method.getAnnotation(WithExecutor.class);
if (withExecutor != null) {
Set<Bean<?>> beans = bm.getBeans(ExecutorService.class, withExecutor);
if (beans.isEmpty()) {
throw new FaultToleranceDefinitionException("Unable to resolved named executor service '"
+ withExecutor.value() + "' at "
+ method.getDeclaringClass().getName() + "::"
+ method.getName());
}
}
}

void close(@Observes BeforeShutdown shutdown) {
FaultToleranceMetrics.close();
// we need to clear method cache, as the next start could use different config
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
* Copyright (c) 2018, 2024 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.
Expand Down Expand Up @@ -39,8 +39,6 @@ abstract class MethodAntn {

private static final AnnotationFinder ANNOTATION_FINDER = AnnotationFinder.create(Retry.class.getPackage());

private final AnnotatedType<?> annotatedType;

private final AnnotatedMethod<?> annotatedMethod;

enum MatchingType {
Expand Down Expand Up @@ -80,7 +78,6 @@ public A getAnnotation() {
*/
MethodAntn(AnnotatedMethod<?> annotatedMethod) {
this.annotatedMethod = annotatedMethod;
this.annotatedType = annotatedMethod.getDeclaringType();
}

Method method() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,10 @@
import io.helidon.faulttolerance.RetryTimeoutException;
import io.helidon.faulttolerance.Timeout;

import jakarta.enterprise.inject.UnsatisfiedResolutionException;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.interceptor.InvocationContext;
import org.eclipse.microprofile.faulttolerance.exceptions.BulkheadException;
import org.eclipse.microprofile.faulttolerance.exceptions.CircuitBreakerOpenException;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceException;
import org.eclipse.microprofile.metrics.Counter;

import static io.helidon.faulttolerance.SupplierHelper.toRuntimeException;
Expand Down Expand Up @@ -343,12 +341,8 @@ private CompletableFuture<Object> callSupplierNewThread(FtSupplier<Object> suppl
// Handle a user-provided executor via @WithExecutor
if (introspector.hasWithExecutor()) {
WithExecutor withExecutor = introspector.withExecutor();
try {
ExecutorService executorService = CDI.current().select(ExecutorService.class, withExecutor).get();
asyncBuilder.executor(executorService);
} catch (UnsatisfiedResolutionException e) {
throw new FaultToleranceException(e);
}
ExecutorService executorService = CDI.current().select(ExecutorService.class, withExecutor).get();
asyncBuilder.executor(executorService);
}

// Invoke async call
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import jakarta.enterprise.inject.Produces;
import org.eclipse.microprofile.faulttolerance.Asynchronous;
import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceException;

/**
* A stateful bean that defines some async methods.
Expand Down Expand Up @@ -188,15 +187,4 @@ CompletableFuture<String> asyncWithExecutor() {
ExecutorService executorService() {
return Executors.newFixedThreadPool(10);
}

/**
* Bad executor name, should throw a {@link FaultToleranceException}.
*
* @return A future.
*/
@Asynchronous
@WithExecutor("does-not-exist")
CompletableFuture<String> asyncWithBadExecutor() {
return CompletableFuture.completedFuture("failed");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@

import io.helidon.microprofile.testing.junit5.AddBean;
import jakarta.inject.Inject;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceException;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertThrows;

/**
* Tests for asynchronous invocations using {@code Asynchronous}.
Expand Down Expand Up @@ -132,11 +130,4 @@ void testAsyncWithExecutor() throws Exception {
future.get();
assertThat(bean.wasCalled(), is(true));
}

@Test
void testAsyncWithBadExecutor() {
assertThat(bean.wasCalled(), is(false));
assertThrows(FaultToleranceException.class, () -> bean.asyncWithBadExecutor());
assertThat(bean.wasCalled(), is(false));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2024 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.reflect.Method;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;

import jakarta.enterprise.inject.spi.BeanManager;
import org.eclipse.microprofile.faulttolerance.Asynchronous;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import static org.junit.jupiter.api.Assertions.assertThrows;

class ValidateWithExecutorTest {

static class BadWithExecutorBean {

@Asynchronous
@WithExecutor("does-not-exist")
public CompletableFuture<?> shouldNotBeCalled() {
return CompletableFuture.completedFuture(null);
}
}

@Test
void badExecutorTest() throws Exception {
Method method = BadWithExecutorBean.class.getMethod("shouldNotBeCalled");
WithExecutor withExecutor = method.getAnnotation(WithExecutor.class);
BeanManager bm = Mockito.mock(BeanManager.class);
Mockito.when(bm.getBeans(ExecutorService.class, withExecutor)).thenReturn(Collections.emptySet());
assertThrows(FaultToleranceDefinitionException.class,
() -> FaultToleranceExtension.validateWithExecutor(bm, method));
}
}

0 comments on commit 6f59471

Please sign in to comment.