Add MicroProfile Fault Tolerance

- works only with ReportUnsupportedElementsAtRuntime
This commit is contained in:
Martin Kouba
2018-09-07 16:00:50 +02:00
parent 8ef045f727
commit 18e6ecdc86
31 changed files with 537 additions and 52 deletions

View File

@@ -4,14 +4,18 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.CompositeIndex;
import org.jboss.jandex.DotName;
@@ -86,6 +90,11 @@ public class ArcAnnotationProcessor implements ResourceProcessor {
processorContext.addReflectiveField(fieldInfo);
}
});
for (BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>> transformer : beanDeployment
.getAnnotationTransformers()) {
builder.addAnnotationTransformer(transformer);
}
builder.setOutput(new ResourceOutput() {
@Override
public void writeResource(Resource resource) throws IOException {

View File

@@ -2,16 +2,28 @@ package org.jboss.shamrock.deployment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
public class BeanDeployment {
private final List<String> additionalBeans = new ArrayList<>();
private final Map<String, byte[]> generatedBeans = new HashMap<>();
// Lite profile
private final List<BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>>> annotationTransformers = new ArrayList<>();
// Full profile
private final List<String> extensions = new ArrayList<>();
public void addAdditionalBean(Class<?>... beanClass) {
additionalBeans.addAll(Arrays.stream(beanClass).map(Class::getName).collect(Collectors.toList()));
}
@@ -19,10 +31,19 @@ public class BeanDeployment {
public void addAdditionalBean(String... beanClass) {
additionalBeans.addAll(Arrays.stream(beanClass).collect(Collectors.toList()));
}
public void addGeneratedBean(String name, byte[] bean) {
generatedBeans.put(name, bean);
}
public void addAnnotationTransformer(BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>> transformer) {
annotationTransformers.add(transformer);
}
public void addExtension(String extensionClass) {
extensions.add(extensionClass);
}
public List<String> getAdditionalBeans() {
return additionalBeans;
}
@@ -30,4 +51,13 @@ public class BeanDeployment {
public Map<String, byte[]> getGeneratedBeans() {
return generatedBeans;
}
public List<BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>>> getAnnotationTransformers() {
return annotationTransformers;
}
public List<String> getExtensions() {
return extensions;
}
}

View File

@@ -9,6 +9,7 @@ public class RuntimePriority {
public static final int UNDERTOW_CREATE_DEPLOYMENT = 100;
public static final int UNDERTOW_REGISTER_SERVLET = 200;
public static final int FAULT_TOLERANCE_DEPLOYMENT = 250;
public static final int HEALTH_DEPLOYMENT = 260;
public static final int WELD_DEPLOYMENT = 300;
public static final int JAXRS_DEPLOYMENT = 350;

View File

@@ -32,6 +32,11 @@
<artifactId>shamrock-openapi-deployment</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.shamrock</groupId>
<artifactId>shamrock-fault-tolerance-deployment</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>

View File

@@ -0,0 +1,30 @@
package org.jboss.shamrock.example.faulttolerance;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.faulttolerance.Retry;
@ApplicationScoped
public class Service {
static final int THRESHOLD = 2;
private String name;
@PostConstruct
void init() {
name = "Lucie";
}
@Retry(maxRetries = 10)
public String getName(AtomicInteger counter) {
if (counter.incrementAndGet() >= THRESHOLD) {
return name;
}
throw new IllegalStateException("Counter=" + counter.get());
}
}

View File

@@ -0,0 +1,22 @@
package org.jboss.shamrock.example.faulttolerance;
import java.util.concurrent.atomic.AtomicInteger;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@Path("/ft")
public class TestResource {
@Inject
Service service;
@GET
public String getName() {
AtomicInteger counter = new AtomicInteger();
String name = service.getName(counter);
return counter + ":" + name;
}
}

View File

@@ -0,0 +1,9 @@
package org.jboss.shamrock.example.test;
import org.jboss.shamrock.junit.GraalTest;
import org.junit.runner.RunWith;
@RunWith(GraalTest.class)
public class FaultToleranceITCase extends FaultToleranceTestCase {
}

View File

@@ -0,0 +1,29 @@
package org.jboss.shamrock.example.test;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import org.jboss.shamrock.junit.ShamrockTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(ShamrockTest.class)
public class FaultToleranceTestCase {
@Test
public void testRetry() throws Exception {
URL uri = new URL("http://localhost:8080/rest/ft");
URLConnection connection = uri.openConnection();
InputStream in = connection.getInputStream();
byte[] buf = new byte[100];
int r;
ByteArrayOutputStream out = new ByteArrayOutputStream();
while ((r = in.read(buf)) > 0) {
out.write(buf, 0, r);
}
Assert.assertEquals("2:Lucie", new String(out.toByteArray()));
}
}

View File

@@ -101,6 +101,11 @@
<artifactId>shamrock-rest-client-deployment</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.shamrock</groupId>
<artifactId>shamrock-fault-tolerance-deployment</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql-protean</artifactId>

View File

@@ -0,0 +1,22 @@
package org.jboss.protean.arc.processor;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
public class AnnotationTransformer {
private List<BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>>> transformers = null;
Collection<AnnotationInstance> getAnnotations(AnnotationTarget target) {
Collection<AnnotationInstance> annotations = null;
for (BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>> transformer : transformers) {
annotations = transformer.apply(target, annotations);
}
return annotations;
}
}

View File

@@ -8,6 +8,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import javax.enterprise.inject.spi.DefinitionException;
@@ -49,7 +50,10 @@ public class BeanDeployment {
private final InterceptorResolver interceptorResolver;
BeanDeployment(IndexView index, Collection<DotName> additionalBeanDefiningAnnotations) {
private final List<BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>>> annotationTransformers;
BeanDeployment(IndexView index, Collection<DotName> additionalBeanDefiningAnnotations,
List<BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>>> annotationTransformers) {
long start = System.currentTimeMillis();
this.index = index;
this.qualifiers = findQualifiers(index);
@@ -61,6 +65,7 @@ public class BeanDeployment {
this.beans = findBeans(initBeanDefiningAnnotations(additionalBeanDefiningAnnotations), observers);
this.observers = observers;
this.interceptorResolver = new InterceptorResolver(this);
this.annotationTransformers = annotationTransformers;
LOGGER.infof("Build deployment created in %s ms", System.currentTimeMillis() - start);
}
@@ -96,6 +101,28 @@ public class BeanDeployment {
return interceptorBindings.get(name);
}
Collection<AnnotationInstance> getAnnotations(AnnotationTarget target) {
Collection<AnnotationInstance> annotations = null;
switch (target.kind()) {
case CLASS:
annotations = target.asClass().classAnnotations();
break;
case METHOD:
annotations = target.asMethod().annotations();
case FIELD:
annotations = target.asField().annotations();
default:
throw new UnsupportedOperationException();
}
if (annotationTransformers == null || annotationTransformers.isEmpty()) {
return annotations;
}
for (BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>> transformer : annotationTransformers) {
annotations = transformer.apply(target, annotations);
}
return annotations;
}
void init() {
long start = System.currentTimeMillis();
for (BeanInfo bean : beans) {
@@ -142,8 +169,8 @@ public class BeanDeployment {
continue;
}
if (beanClass.nestingType().equals(NestingType.ANONYMOUS) || beanClass.nestingType().equals(NestingType.LOCAL)
|| (beanClass.nestingType().equals(NestingType.INNER) && !Modifier.isStatic(beanClass.flags())) ||
Modifier.isInterface(beanClass.flags())) {
|| (beanClass.nestingType().equals(NestingType.INNER) && !Modifier.isStatic(beanClass.flags()))
|| Modifier.isInterface(beanClass.flags())) {
// Skip interfaces, annonymous, local and inner classes
continue;
}
@@ -190,7 +217,7 @@ public class BeanDeployment {
beans.add(Beans.createProducerMethod(producerMethod, declaringBean, this, findDisposer(declaringBean, producerMethod, disposers)));
}
}
for (FieldInfo producerField : producerFields) {
BeanInfo declaringBean = beanClassToBean.get(producerField.declaringClass());
if (declaringBean != null) {

View File

@@ -47,7 +47,7 @@ class BeanInfo {
private Map<MethodInfo, List<InterceptorInfo>> interceptedMethods;
private Map<InterceptionType, List<InterceptorInfo>> lifecycleInterceptors;
private final Integer alternativePriority;
/**
@@ -201,11 +201,11 @@ class BeanInfo {
DisposerInfo getDisposer() {
return disposer;
}
boolean isAlternative() {
return alternativePriority != null;
}
Integer getAlternativePriority() {
return alternativePriority;
}
@@ -278,7 +278,7 @@ class BeanInfo {
}
private void addClassLevelBindings(ClassInfo classInfo, Collection<AnnotationInstance> bindings) {
classInfo.classAnnotations().stream()
beanDeployment.getAnnotations(classInfo).stream()
.filter(a -> beanDeployment.getInterceptorBinding(a.name()) != null && bindings.stream().noneMatch(e -> e.name().equals(a.name())))
.forEach(a -> bindings.add(a));
if (classInfo.superClassType() != null && !classInfo.superClassType().name().equals(DotNames.OBJECT)) {

View File

@@ -10,12 +10,14 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Default;
import javax.inject.Named;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.CompositeIndex;
import org.jboss.jandex.DotName;
@@ -52,8 +54,11 @@ public class BeanProcessor {
private final ReflectionRegistration reflectionRegistration;
private final List<BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>>> annotationTransformers;
private BeanProcessor(String name, IndexView index, Collection<DotName> additionalBeanDefiningAnnotations, ResourceOutput output,
boolean sharedAnnotationLiterals, ReflectionRegistration reflectionRegistration) {
boolean sharedAnnotationLiterals, ReflectionRegistration reflectionRegistration,
List<BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>>> annotationTransformers) {
this.reflectionRegistration = reflectionRegistration;
Objects.requireNonNull(output);
this.name = name;
@@ -61,11 +66,12 @@ public class BeanProcessor {
this.additionalBeanDefiningAnnotations = additionalBeanDefiningAnnotations;
this.output = output;
this.sharedAnnotationLiterals = sharedAnnotationLiterals;
this.annotationTransformers = annotationTransformers;
}
public BeanDeployment process() throws IOException {
BeanDeployment beanDeployment = new BeanDeployment(new IndexWrapper(index), additionalBeanDefiningAnnotations);
BeanDeployment beanDeployment = new BeanDeployment(new IndexWrapper(index), additionalBeanDefiningAnnotations, annotationTransformers);
beanDeployment.init();
BeanGenerator beanGenerator = new BeanGenerator();
@@ -170,6 +176,8 @@ public class BeanProcessor {
private ReflectionRegistration reflectionRegistration = ReflectionRegistration.NOOP;
private final List<BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>>> annotationTransformers = new ArrayList<>();
public Builder setName(String name) {
this.name = name;
return this;
@@ -200,9 +208,14 @@ public class BeanProcessor {
return this;
}
public Builder addAnnotationTransformer(BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>> transformer) {
this.annotationTransformers.add(transformer);
return this;
}
public BeanProcessor build() {
return new BeanProcessor(name, addBuiltinQualifiersIfNeeded(index), additionalBeanDefiningAnnotations, output, sharedAnnotationLiterals,
reflectionRegistration);
reflectionRegistration, annotationTransformers);
}
}

View File

@@ -13,10 +13,6 @@ import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import org.jboss.jandex.Index;
import org.jboss.protean.arc.processor.AnnotationLiteralProcessor;
import org.jboss.protean.arc.processor.BeanDeployment;
import org.jboss.protean.arc.processor.BeanGenerator;
import org.jboss.protean.arc.processor.BeanProcessor;
import org.jboss.protean.arc.processor.types.Foo;
import org.jboss.protean.arc.processor.types.FooQualifier;
import org.junit.Test;
@@ -27,7 +23,7 @@ public class BeanGeneratorTest {
public void testGenerator() throws IOException {
Index index = index(Foo.class, FooQualifier.class, AbstractList.class, AbstractCollection.class, Collection.class, List.class, Iterable.class);
BeanDeployment deployment = new BeanDeployment(index, null);
BeanDeployment deployment = new BeanDeployment(index, null, null);
deployment.init();
BeanGenerator generator = new BeanGenerator();
@@ -40,7 +36,7 @@ public class BeanGeneratorTest {
public void testGeneratorForNormalScopedProducer() throws IOException {
Index index = index(Producer.class, Collection.class, List.class, Iterable.class);
BeanDeployment deployment = new BeanDeployment(index, null);
BeanDeployment deployment = new BeanDeployment(index, null, null);
deployment.init();
BeanGenerator generator = new BeanGenerator();

View File

@@ -16,9 +16,6 @@ import org.jboss.jandex.Index;
import org.jboss.jandex.ParameterizedType;
import org.jboss.jandex.Type;
import org.jboss.jandex.Type.Kind;
import org.jboss.protean.arc.processor.BeanDeployment;
import org.jboss.protean.arc.processor.BeanInfo;
import org.jboss.protean.arc.processor.Injection;
import org.jboss.protean.arc.processor.types.Bar;
import org.jboss.protean.arc.processor.types.Foo;
import org.jboss.protean.arc.processor.types.FooQualifier;
@@ -41,7 +38,7 @@ public class BeanInfoInjectionsTest {
Type fooType = Type.create(name(Foo.class), Kind.CLASS);
Type listStringType = ParameterizedType.create(name(List.class), new Type[] { Type.create(name(String.class), Kind.CLASS) }, null);
BeanDeployment deployment = new BeanDeployment(index, null);
BeanDeployment deployment = new BeanDeployment(index, null, null);
BeanInfo barBean = deployment.getBeans().stream().filter(b -> b.getTarget().equals(barClass)).findFirst().get();
List<Injection> injections = barBean.getInjections();
assertEquals(3, injections.size());

View File

@@ -9,15 +9,12 @@ import java.io.IOException;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.protean.arc.processor.BeanDeployment;
import org.jboss.protean.arc.processor.BeanInfo;
import org.jboss.protean.arc.processor.Beans;
import org.jboss.protean.arc.processor.types.Bar;
import org.jboss.protean.arc.processor.types.Foo;
import org.jboss.protean.arc.processor.types.FooQualifier;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.jboss.protean.arc.processor.types.Bar;
import org.jboss.protean.arc.processor.types.Foo;
import org.jboss.protean.arc.processor.types.FooQualifier;
import org.junit.Test;
/**
@@ -33,7 +30,7 @@ public class BeanInfoQualifiersTest {
DotName fooQualifierName = name(FooQualifier.class);
ClassInfo fooClass = index.getClassByName(fooName);
BeanInfo bean = Beans.createClassBean(fooClass, new BeanDeployment(index, null));
BeanInfo bean = Beans.createClassBean(fooClass, new BeanDeployment(index, null, null));
AnnotationInstance requiredFooQualifier = index.getAnnotations(fooQualifierName).stream()
.filter(a -> Kind.FIELD.equals(a.target().kind()) && a.target().asField().name().equals("foo")).findFirst().orElse(null);

View File

@@ -18,9 +18,6 @@ import org.jboss.jandex.Index;
import org.jboss.jandex.ParameterizedType;
import org.jboss.jandex.Type;
import org.jboss.jandex.Type.Kind;
import org.jboss.protean.arc.processor.BeanDeployment;
import org.jboss.protean.arc.processor.BeanInfo;
import org.jboss.protean.arc.processor.Beans;
import org.jboss.protean.arc.processor.types.Bar;
import org.jboss.protean.arc.processor.types.Foo;
import org.jboss.protean.arc.processor.types.FooQualifier;
@@ -38,7 +35,7 @@ public class BeanInfoTypesTest {
Index index = index(Foo.class, Bar.class, FooQualifier.class, AbstractList.class, AbstractCollection.class, Collection.class, List.class,
Iterable.class);
BeanDeployment deployment = new BeanDeployment(index, null);
BeanDeployment deployment = new BeanDeployment(index, null, null);
DotName fooName = name(Foo.class);
ClassInfo fooClass = index.getClassByName(fooName);

View File

@@ -12,11 +12,6 @@ import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
import org.jboss.jandex.Index;
import org.jboss.protean.arc.processor.AnnotationLiteralProcessor;
import org.jboss.protean.arc.processor.BeanDeployment;
import org.jboss.protean.arc.processor.BeanGenerator;
import org.jboss.protean.arc.processor.BeanProcessor;
import org.jboss.protean.arc.processor.ClientProxyGenerator;
import org.jboss.protean.arc.processor.ResourceOutput.Resource;
import org.junit.Test;
@@ -26,7 +21,7 @@ public class ClientProxyGeneratorTest {
public void testGenerator() throws IOException {
Index index = index(Producer.class, List.class, Collection.class, Iterable.class, AbstractList.class, MyList.class);
BeanDeployment deployment = new BeanDeployment(index, null);
BeanDeployment deployment = new BeanDeployment(index, null, null);
deployment.init();
BeanGenerator beanGenerator = new BeanGenerator();

View File

@@ -17,11 +17,6 @@ import javax.interceptor.InvocationContext;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.jboss.protean.arc.processor.AnnotationLiteralProcessor;
import org.jboss.protean.arc.processor.BeanDeployment;
import org.jboss.protean.arc.processor.BeanProcessor;
import org.jboss.protean.arc.processor.InterceptorGenerator;
import org.jboss.protean.arc.processor.InterceptorInfo;
import org.jboss.protean.arc.processor.types.Baz;
import org.junit.Test;
@@ -31,7 +26,7 @@ public class InterceptorGeneratorTest {
public void testGenerator() throws IOException {
Index index = index(MyInterceptor.class, MyBinding.class, Baz.class);
BeanDeployment deployment = new BeanDeployment(index, null);
BeanDeployment deployment = new BeanDeployment(index, null, null);
deployment.init();
InterceptorInfo myInterceptor = deployment.getInterceptors().stream()

View File

@@ -20,12 +20,6 @@ import javax.interceptor.InvocationContext;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.jboss.protean.arc.processor.AnnotationLiteralProcessor;
import org.jboss.protean.arc.processor.BeanDeployment;
import org.jboss.protean.arc.processor.BeanGenerator;
import org.jboss.protean.arc.processor.BeanInfo;
import org.jboss.protean.arc.processor.BeanProcessor;
import org.jboss.protean.arc.processor.SubclassGenerator;
import org.jboss.protean.arc.processor.ResourceOutput.Resource;
import org.jboss.protean.arc.processor.types.Baz;
import org.junit.Test;
@@ -36,7 +30,7 @@ public class SubclassGeneratorTest {
public void testGenerator() throws IOException {
Index index = index(SimpleBean.class, Simple.class, SimpleInterceptor.class, Baz.class);
BeanDeployment deployment = new BeanDeployment(index, null);
BeanDeployment deployment = new BeanDeployment(index, null, null);
deployment.init();
BeanGenerator beanGenerator = new BeanGenerator();

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>shamrock-fault-tolerance</artifactId>
<groupId>org.jboss.shamrock</groupId>
<version>1.0.0.Alpha1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shamrock-fault-tolerance-deployment</artifactId>
<dependencies>
<dependency>
<groupId>org.jboss.shamrock</groupId>
<artifactId>shamrock-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.shamrock</groupId>
<artifactId>shamrock-fault-tolerance-runtime</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,119 @@
package org.jboss.shamrock.faulttolerance.deployment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import javax.inject.Inject;
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.FallbackHandler;
import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.faulttolerance.Timeout;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.jboss.shamrock.deployment.ArchiveContext;
import org.jboss.shamrock.deployment.BeanDeployment;
import org.jboss.shamrock.deployment.ProcessorContext;
import org.jboss.shamrock.deployment.ResourceProcessor;
import org.jboss.shamrock.deployment.RuntimePriority;
import org.jboss.shamrock.faulttolerance.runtime.ShamrockFallbackHandlerProvider;
import org.jboss.shamrock.faulttolerance.runtime.ShamrockFaultToleranceOperationProvider;
import com.netflix.hystrix.HystrixCircuitBreaker;
import io.smallrye.faulttolerance.DefaultFallbackHandlerProvider;
import io.smallrye.faulttolerance.DefaultFaultToleranceOperationProvider;
import io.smallrye.faulttolerance.DefaultHystrixConcurrencyStrategy;
import io.smallrye.faulttolerance.HystrixCommandBinding;
import io.smallrye.faulttolerance.HystrixCommandInterceptor;
import io.smallrye.faulttolerance.HystrixExtension;
import io.smallrye.faulttolerance.HystrixInitializer;
public class FaultToleranceAnnotationProcessor implements ResourceProcessor {
private static final DotName[] FT_ANNOTATIONS = { DotName.createSimple(Asynchronous.class.getName()), DotName.createSimple(Bulkhead.class.getName()),
DotName.createSimple(CircuitBreaker.class.getName()), DotName.createSimple(Fallback.class.getName()), DotName.createSimple(Retry.class.getName()),
DotName.createSimple(Timeout.class.getName()) };
@Inject
BeanDeployment beanDeployment;
@Override
public void process(ArchiveContext archiveContext, ProcessorContext processorContext) throws Exception {
IndexView index = archiveContext.getCombinedIndex();
// Add reflective acccess to fallback handlers
Collection<ClassInfo> fallbackHandlers = index.getAllKnownImplementors(DotName.createSimple(FallbackHandler.class.getName()));
for (ClassInfo fallbackHandler : fallbackHandlers) {
processorContext.addReflectiveClass(true, false, fallbackHandler.name().toString());
}
processorContext.addReflectiveClass(false, true, HystrixCircuitBreaker.Factory.class.getName());
// Add HystrixCommandBinding to app classes
Set<String> ftClasses = new HashSet<>();
for (DotName annotation : FT_ANNOTATIONS) {
Collection<AnnotationInstance> annotationInstances = index.getAnnotations(annotation);
for (AnnotationInstance instance : annotationInstances) {
if (instance.target().kind() == Kind.CLASS) {
ftClasses.add(instance.target().asClass().toString());
} else if (instance.target().kind() == Kind.METHOD) {
ftClasses.add(instance.target().asMethod().declaringClass().toString());
}
}
// Needed for substrate VM
processorContext.addReflectiveClass(true, false, annotation.toString());
}
if (!ftClasses.isEmpty()) {
beanDeployment.addAnnotationTransformer(new BiFunction<AnnotationTarget, Collection<AnnotationInstance>, Collection<AnnotationInstance>>() {
@Override
public Collection<AnnotationInstance> apply(AnnotationTarget target, Collection<AnnotationInstance> annotations) {
if (Kind.CLASS != target.kind() || !ftClasses.contains(target.asClass().name().toString())) {
return annotations;
}
// Add @HystrixCommandBinding
List<AnnotationInstance> modified = new ArrayList<>(annotations);
modified.add(AnnotationInstance.create(DotName.createSimple(HystrixCommandBinding.class.getName()), target, new AnnotationValue[0]));
return modified;
}
});
}
// TODO there should be a proper way to detect a shamrock "feature"
try {
Class.forName("org.jboss.protean.arc.Arc");
// Register bean classes
beanDeployment.addAdditionalBean(HystrixCommandInterceptor.class);
beanDeployment.addAdditionalBean(HystrixInitializer.class);
beanDeployment.addAdditionalBean(DefaultHystrixConcurrencyStrategy.class);
beanDeployment.addAdditionalBean(ShamrockFaultToleranceOperationProvider.class);
beanDeployment.addAdditionalBean(ShamrockFallbackHandlerProvider.class);
} catch (Exception e) {
// Full CDI
beanDeployment.addExtension(HystrixExtension.class.getName());
processorContext.addReflectiveClass(true, true, HystrixCommandInterceptor.class.getName());
processorContext.addReflectiveClass(true, true, HystrixInitializer.class.getName());
processorContext.addReflectiveClass(true, true, DefaultHystrixConcurrencyStrategy.class.getName());
processorContext.addReflectiveClass(true, true, DefaultFaultToleranceOperationProvider.class.getName());
processorContext.addReflectiveClass(true, true, DefaultFallbackHandlerProvider.class.getName());
}
}
@Override
public int getPriority() {
return RuntimePriority.FAULT_TOLERANCE_DEPLOYMENT;
}
}

View File

@@ -0,0 +1,12 @@
package org.jboss.shamrock.faulttolerance.deployment;
import org.jboss.shamrock.deployment.SetupContext;
import org.jboss.shamrock.deployment.ShamrockSetup;
public class FaultToleranceSetup implements ShamrockSetup {
@Override
public void setup(SetupContext context) {
context.addResourceProcessor(new FaultToleranceAnnotationProcessor());
}
}

View File

@@ -0,0 +1 @@
org.jboss.shamrock.faulttolerance.deployment.FaultToleranceSetup

19
fault-tolerance/pom.xml Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>shamrock-parent</artifactId>
<groupId>org.jboss.shamrock</groupId>
<version>1.0.0.Alpha1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shamrock-fault-tolerance</artifactId>
<packaging>pom</packaging>
<modules>
<module>deployment</module>
<module>runtime</module>
</modules>
</project>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>shamrock-fault-tolerance</artifactId>
<groupId>org.jboss.shamrock</groupId>
<version>1.0.0.Alpha1-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shamrock-fault-tolerance-runtime</artifactId>
<dependencies>
<dependency>
<groupId>org.jboss.shamrock</groupId>
<artifactId>shamrock-core-runtime</artifactId>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-fault-tolerance</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,47 @@
package org.jboss.shamrock.faulttolerance.runtime;
import javax.annotation.Priority;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import org.eclipse.microprofile.faulttolerance.ExecutionContext;
import org.eclipse.microprofile.faulttolerance.FallbackHandler;
import io.smallrye.faulttolerance.FallbackHandlerProvider;
import io.smallrye.faulttolerance.config.FallbackConfig;
import io.smallrye.faulttolerance.config.FaultToleranceOperation;
@Dependent
@Alternative
@Priority(1)
public class ShamrockFallbackHandlerProvider implements FallbackHandlerProvider {
@Inject
@Any
Instance<Object> instance;
@Override
public <T> FallbackHandler<T> get(FaultToleranceOperation operation) {
if (operation.hasFallback()) {
return new FallbackHandler<T>() {
@SuppressWarnings("unchecked")
@Override
public T handle(ExecutionContext context) {
Class<?> clazz = operation.getFallback().get(FallbackConfig.VALUE);
FallbackHandler<T> fallbackHandlerInstance = (FallbackHandler<T>) instance.select(clazz).get();
try {
return fallbackHandlerInstance.handle(context);
} finally {
// The instance exists to service a single invocation only
instance.destroy(fallbackHandlerInstance);
}
}
};
}
return null;
}
}

View File

@@ -0,0 +1,22 @@
package org.jboss.shamrock.faulttolerance.runtime;
import java.lang.reflect.Method;
import javax.annotation.Priority;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Alternative;
import io.smallrye.faulttolerance.FaultToleranceOperationProvider;
import io.smallrye.faulttolerance.config.FaultToleranceOperation;
@Dependent
@Alternative
@Priority(1)
public class ShamrockFaultToleranceOperationProvider implements FaultToleranceOperationProvider {
@Override
public FaultToleranceOperation apply(Method method) {
return FaultToleranceOperation.of(method);
}
}

19
pom.xml
View File

@@ -17,7 +17,7 @@
<properties>
<version.org.jandex>2.1.0.Beta1</version.org.jandex>
<resteasy.version>4.0.0.Beta5</resteasy.version>
<weld-se-core.version>3.0.4.Final</weld-se-core.version>
<weld-se-core.version>3.0.5.Final</weld-se-core.version>
<undertow.version>2.0.12.Final</undertow.version>
<xnio.version>3.3.8.Final</xnio.version>
<jboss-servlet-api_4.0_spec.version>1.0.0.Final</jboss-servlet-api_4.0_spec.version>
@@ -25,6 +25,7 @@
<smallrye-health.version>1.0.1</smallrye-health.version>
<smallrye-metrics.version>1.1.0</smallrye-metrics.version>
<smallrye-open-api.version>1.0.2</smallrye-open-api.version>
<smallrye-fault-tolerance.version>1.0.2</smallrye-fault-tolerance.version>
<javax.inject.version>1</javax.inject.version>
<jboss-classfilewriter.version>1.2.1.Final</jboss-classfilewriter.version>
<jboss-invocation.version>1.5.1.Final</jboss-invocation.version>
@@ -80,6 +81,7 @@
<module>transactions</module>
<module>agroal</module>
<module>rest-client</module>
<module>fault-tolerance</module>
<module>examples</module>
</modules>
<build>
@@ -281,6 +283,16 @@
<artifactId>shamrock-arc-runtime</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.shamrock</groupId>
<artifactId>shamrock-fault-tolerance-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.shamrock</groupId>
<artifactId>shamrock-fault-tolerance-runtime</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.protean.gizmo</groupId>
@@ -380,6 +392,11 @@
<artifactId>smallrye-open-api</artifactId>
<version>${smallrye-open-api.version}</version>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-fault-tolerance</artifactId>
<version>${smallrye-fault-tolerance.version}</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>

View File

@@ -44,6 +44,9 @@ public class WeldAnnotationProcessor implements ResourceProcessor {
for (String clazz : beanDeployment.getGeneratedBeans().keySet()) {
template.addClass(init, recorder.classProxy(clazz));
}
for (String extensionClazz : beanDeployment.getExtensions()) {
template.addExtension(init, recorder.classProxy(extensionClazz));
}
SeContainer weld = template.doBoot(null, init);
template.initBeanContainer(weld);
template.setupInjection(null, weld);

View File

@@ -15,6 +15,7 @@ import javax.enterprise.inject.Instance;
import javax.enterprise.inject.se.SeContainer;
import javax.enterprise.inject.se.SeContainerInitializer;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.Extension;
import org.jboss.shamrock.runtime.BeanContainer;
import org.jboss.shamrock.runtime.ContextObject;
@@ -70,6 +71,11 @@ public class WeldDeploymentTemplate {
public void addInterceptor(SeContainerInitializer initialize, Class<?> interceptorClass) {
initialize.enableInterceptors(interceptorClass);
}
@SuppressWarnings("unchecked")
public void addExtension(SeContainerInitializer initializer, Class<?> extensionClazz) {
initializer.addExtensions((Class<? extends Extension>)extensionClazz);
}
@ContextObject("weld.container")
public SeContainer doBoot(StartupContext startupContext, SeContainerInitializer initializer) throws Exception {