All Downloads are FREE. Search and download functionalities are using the official Maven repository.
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.smallrye.faulttolerance.FaultToleranceExtension Maven / Gradle / Ivy
/*
* 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;
import static io.smallrye.faulttolerance.CdiLogger.LOG;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import jakarta.annotation.Priority;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.inject.spi.AfterDeploymentValidation;
import jakarta.enterprise.inject.spi.AnnotatedConstructor;
import jakarta.enterprise.inject.spi.AnnotatedField;
import jakarta.enterprise.inject.spi.AnnotatedMethod;
import jakarta.enterprise.inject.spi.AnnotatedType;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.BeforeBeanDiscovery;
import jakarta.enterprise.inject.spi.Extension;
import jakarta.enterprise.inject.spi.ProcessAnnotatedType;
import jakarta.enterprise.inject.spi.ProcessManagedBean;
import jakarta.enterprise.util.AnnotationLiteral;
import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.faulttolerance.Asynchronous;
import org.eclipse.microprofile.faulttolerance.Bulkhead;
import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.faulttolerance.Timeout;
import io.smallrye.common.annotation.Blocking;
import io.smallrye.common.annotation.NonBlocking;
import io.smallrye.faulttolerance.api.ApplyFaultTolerance;
import io.smallrye.faulttolerance.api.AsynchronousNonBlocking;
import io.smallrye.faulttolerance.api.CustomBackoff;
import io.smallrye.faulttolerance.api.ExponentialBackoff;
import io.smallrye.faulttolerance.api.FibonacciBackoff;
import io.smallrye.faulttolerance.api.RateLimit;
import io.smallrye.faulttolerance.autoconfig.FaultToleranceMethod;
import io.smallrye.faulttolerance.config.FaultToleranceMethods;
import io.smallrye.faulttolerance.config.FaultToleranceOperation;
import io.smallrye.faulttolerance.internal.StrategyCache;
import io.smallrye.faulttolerance.metrics.MetricsIntegration;
import io.smallrye.faulttolerance.metrics.MicroProfileMetricsProvider;
import io.smallrye.faulttolerance.metrics.MicrometerProvider;
import io.smallrye.faulttolerance.metrics.NoopProvider;
public class FaultToleranceExtension implements Extension {
private static final List> BACKOFF_ANNOTATIONS = Arrays.asList(
ExponentialBackoff.class,
FibonacciBackoff.class,
CustomBackoff.class);
/**
* @see #collectFaultToleranceOperations(ProcessManagedBean)
*/
private final ConcurrentMap faultToleranceOperations = new ConcurrentHashMap<>();
private final ConcurrentMap> existingCircuitBreakerNames = new ConcurrentHashMap<>();
private final MetricsIntegration metricsIntegration;
public FaultToleranceExtension() {
this(MetricsIntegration.MICROPROFILE_METRICS);
}
public FaultToleranceExtension(MetricsIntegration metricsIntegration) {
this.metricsIntegration = metricsIntegration;
}
void registerInterceptorBindings(@Observes BeforeBeanDiscovery bbd, BeanManager bm) {
LOG.activated(getImplementationVersion().orElse("unknown"));
// certain SmallRye annotations (@CircuitBreakerName, @[Non]Blocking, @*Backoff) alone do _not_ trigger
// the fault tolerance interceptor, only in combination with other fault tolerance annotations
bbd.addInterceptorBinding(new FTInterceptorBindingAnnotatedType<>(bm.createAnnotatedType(ApplyFaultTolerance.class)));
bbd.addInterceptorBinding(new FTInterceptorBindingAnnotatedType<>(bm.createAnnotatedType(Asynchronous.class)));
bbd.addInterceptorBinding(new FTInterceptorBindingAnnotatedType<>(
bm.createAnnotatedType(AsynchronousNonBlocking.class)));
bbd.addInterceptorBinding(new FTInterceptorBindingAnnotatedType<>(bm.createAnnotatedType(Bulkhead.class)));
bbd.addInterceptorBinding(new FTInterceptorBindingAnnotatedType<>(bm.createAnnotatedType(CircuitBreaker.class)));
bbd.addInterceptorBinding(new FTInterceptorBindingAnnotatedType<>(bm.createAnnotatedType(Fallback.class)));
bbd.addInterceptorBinding(new FTInterceptorBindingAnnotatedType<>(bm.createAnnotatedType(RateLimit.class)));
bbd.addInterceptorBinding(new FTInterceptorBindingAnnotatedType<>(bm.createAnnotatedType(Retry.class)));
bbd.addInterceptorBinding(new FTInterceptorBindingAnnotatedType<>(bm.createAnnotatedType(Timeout.class)));
bbd.addAnnotatedType(bm.createAnnotatedType(FaultToleranceInterceptor.class),
FaultToleranceInterceptor.class.getName());
bbd.addAnnotatedType(bm.createAnnotatedType(DefaultFallbackHandlerProvider.class),
DefaultFallbackHandlerProvider.class.getName());
bbd.addAnnotatedType(bm.createAnnotatedType(DefaultAsyncExecutorProvider.class),
DefaultAsyncExecutorProvider.class.getName());
bbd.addAnnotatedType(bm.createAnnotatedType(ExecutorHolder.class),
ExecutorHolder.class.getName());
bbd.addAnnotatedType(bm.createAnnotatedType(DefaultFaultToleranceOperationProvider.class),
DefaultFaultToleranceOperationProvider.class.getName());
bbd.addAnnotatedType(bm.createAnnotatedType(DefaultExistingCircuitBreakerNames.class),
DefaultExistingCircuitBreakerNames.class.getName());
switch (metricsIntegration) {
case MICROPROFILE_METRICS:
bbd.addAnnotatedType(bm.createAnnotatedType(MicroProfileMetricsProvider.class),
MicroProfileMetricsProvider.class.getName());
break;
case MICROMETER:
bbd.addAnnotatedType(bm.createAnnotatedType(MicrometerProvider.class), MicrometerProvider.class.getName());
break;
case NOOP:
bbd.addAnnotatedType(bm.createAnnotatedType(NoopProvider.class), NoopProvider.class.getName());
break;
}
bbd.addAnnotatedType(bm.createAnnotatedType(StrategyCache.class), StrategyCache.class.getName());
bbd.addAnnotatedType(bm.createAnnotatedType(CircuitBreakerMaintenanceImpl.class),
CircuitBreakerMaintenanceImpl.class.getName());
bbd.addAnnotatedType(bm.createAnnotatedType(RequestContextIntegration.class),
RequestContextIntegration.class.getName());
bbd.addAnnotatedType(bm.createAnnotatedType(SpecCompatibility.class), SpecCompatibility.class.getName());
bbd.addAnnotatedType(bm.createAnnotatedType(CdiFaultToleranceSpi.EagerDependencies.class),
CdiFaultToleranceSpi.EagerDependencies.class.getName());
bbd.addAnnotatedType(bm.createAnnotatedType(CdiFaultToleranceSpi.LazyDependencies.class),
CdiFaultToleranceSpi.LazyDependencies.class.getName());
}
void changeInterceptorPriority(@Observes ProcessAnnotatedType event) {
ConfigProvider.getConfig()
.getOptionalValue("mp.fault.tolerance.interceptor.priority", Integer.class)
.ifPresent(configuredInterceptorPriority -> {
event.configureAnnotatedType()
.remove(ann -> ann instanceof Priority)
.add(new PriorityLiteral(configuredInterceptorPriority));
});
}
/**
* Observe all enabled managed beans and identify/validate FT operations. This allows us to:
*
* Skip validation of types which are not recognized as beans (e.g. are vetoed)
* Take the final values of AnnotatedTypes
* Support annotations added via portable extensions
*
*
* @param event
*/
void collectFaultToleranceOperations(@Observes ProcessManagedBean> event) {
AnnotatedType> annotatedType = event.getAnnotatedBeanClass();
for (AnnotatedMethod> annotatedMethod : annotatedType.getMethods()) {
FaultToleranceMethod method = FaultToleranceMethods.create(annotatedMethod);
if (method.isLegitimate()) {
FaultToleranceOperation operation = FaultToleranceOperation.create(method);
operation.validate();
LOG.debugf("Found %s", operation);
faultToleranceOperations.put(getCacheKey(annotatedType.getJavaClass(), annotatedMethod.getJavaMember()),
operation);
if (operation.hasCircuitBreaker() && operation.hasCircuitBreakerName()) {
existingCircuitBreakerNames
.computeIfAbsent(operation.getCircuitBreakerName().value(), ignored -> new HashSet<>())
.add(annotatedMethod.getJavaMember().toGenericString());
}
for (Class extends Annotation> backoffAnnotation : BACKOFF_ANNOTATIONS) {
if (annotatedMethod.isAnnotationPresent(backoffAnnotation)
&& !annotatedMethod.isAnnotationPresent(Retry.class)) {
event.addDefinitionError(LOG.backoffAnnotationWithoutRetry(backoffAnnotation.getSimpleName(),
method.method));
}
if (annotatedType.isAnnotationPresent(backoffAnnotation)
&& !annotatedType.isAnnotationPresent(Retry.class)) {
event.addDefinitionError(LOG.backoffAnnotationWithoutRetry(backoffAnnotation.getSimpleName(),
annotatedType.getJavaClass()));
}
}
if (annotatedMethod.isAnnotationPresent(Asynchronous.class)
&& annotatedMethod.isAnnotationPresent(AsynchronousNonBlocking.class)) {
event.addDefinitionError(LOG.bothAsyncAndAsyncNonBlockingPresent(method.method));
}
if (annotatedType.isAnnotationPresent(Asynchronous.class)
&& annotatedType.isAnnotationPresent(AsynchronousNonBlocking.class)) {
event.addDefinitionError(LOG.bothAsyncAndAsyncNonBlockingPresent(annotatedType.getJavaClass()));
}
if (annotatedMethod.isAnnotationPresent(Blocking.class)
&& annotatedMethod.isAnnotationPresent(NonBlocking.class)) {
event.addDefinitionError(LOG.bothBlockingNonBlockingPresent(method.method));
}
if (annotatedType.isAnnotationPresent(Blocking.class)
&& annotatedType.isAnnotationPresent(NonBlocking.class)) {
event.addDefinitionError(LOG.bothBlockingNonBlockingPresent(annotatedType.getJavaClass()));
}
}
}
}
void validate(@Observes AfterDeploymentValidation event) {
for (Map.Entry> entry : existingCircuitBreakerNames.entrySet()) {
if (entry.getValue().size() > 1) {
event.addDeploymentProblem(LOG.multipleCircuitBreakersWithTheSameName(
entry.getKey(), entry.getValue()));
}
}
}
private static String getCacheKey(Class> beanClass, Method method) {
return beanClass.getName() + "::" + method.toGenericString();
}
FaultToleranceOperation getFaultToleranceOperation(Class> beanClass, Method method) {
return faultToleranceOperations.get(getCacheKey(beanClass, method));
}
Set getExistingCircuitBreakerNames() {
return existingCircuitBreakerNames.keySet();
}
private static Optional getImplementationVersion() {
return AccessController.doPrivileged(new PrivilegedAction>() {
@Override
public Optional run() {
Properties properties = new Properties();
try {
InputStream resource = this.getClass().getClassLoader()
.getResourceAsStream("smallrye-fault-tolerance.properties");
if (resource != null) {
properties.load(resource);
return Optional.ofNullable(properties.getProperty("version"));
}
} catch (IOException e) {
LOG.debug("Unable to detect SmallRye Fault Tolerance version");
}
return Optional.empty();
}
});
}
public static class FTInterceptorBindingAnnotatedType implements AnnotatedType {
public FTInterceptorBindingAnnotatedType(AnnotatedType delegate) {
this.delegate = delegate;
annotations = new HashSet<>(delegate.getAnnotations());
annotations.add(FaultToleranceBinding.Literal.INSTANCE);
}
public Class getJavaClass() {
return delegate.getJavaClass();
}
public Set> getConstructors() {
return delegate.getConstructors();
}
public Set> getMethods() {
return delegate.getMethods();
}
public Set> getFields() {
return delegate.getFields();
}
public Type getBaseType() {
return delegate.getBaseType();
}
public Set getTypeClosure() {
return delegate.getTypeClosure();
}
@SuppressWarnings("unchecked")
public S getAnnotation(Class annotationType) {
if (FaultToleranceBinding.class.equals(annotationType)) {
return (S) FaultToleranceBinding.Literal.INSTANCE;
}
return delegate.getAnnotation(annotationType);
}
public Set getAnnotations() {
return annotations;
}
public boolean isAnnotationPresent(Class extends Annotation> annotationType) {
return FaultToleranceBinding.class.equals(annotationType) || delegate.isAnnotationPresent(annotationType);
}
private AnnotatedType delegate;
private Set annotations;
}
public static class PriorityLiteral extends AnnotationLiteral implements Priority {
private static final long serialVersionUID = 1L;
private final int value;
public PriorityLiteral(int value) {
this.value = value;
}
@Override
public int value() {
return value;
}
}
}