diff --git a/arc/deployment/src/main/java/org/jboss/shamrock/arc/deployment/ArcAnnotationProcessor.java b/arc/deployment/src/main/java/org/jboss/shamrock/arc/deployment/ArcAnnotationProcessor.java index 08f0742a3fca6..81b40d26429ab 100644 --- a/arc/deployment/src/main/java/org/jboss/shamrock/arc/deployment/ArcAnnotationProcessor.java +++ b/arc/deployment/src/main/java/org/jboss/shamrock/arc/deployment/ArcAnnotationProcessor.java @@ -4,14 +4,18 @@ 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 void registerField(FieldInfo fieldInfo) { processorContext.addReflectiveField(fieldInfo); } }); + for (BiFunction, Collection> transformer : beanDeployment + .getAnnotationTransformers()) { + builder.addAnnotationTransformer(transformer); + } + builder.setOutput(new ResourceOutput() { @Override public void writeResource(Resource resource) throws IOException { diff --git a/core/deployment/src/main/java/org/jboss/shamrock/deployment/BeanDeployment.java b/core/deployment/src/main/java/org/jboss/shamrock/deployment/BeanDeployment.java index 697badf8c9af1..4bdd1ff31147f 100644 --- a/core/deployment/src/main/java/org/jboss/shamrock/deployment/BeanDeployment.java +++ b/core/deployment/src/main/java/org/jboss/shamrock/deployment/BeanDeployment.java @@ -2,16 +2,28 @@ 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 additionalBeans = new ArrayList<>(); + private final Map generatedBeans = new HashMap<>(); + + // Lite profile + private final List, Collection>> annotationTransformers = new ArrayList<>(); + // Full profile + private final List extensions = new ArrayList<>(); + public void addAdditionalBean(Class... beanClass) { additionalBeans.addAll(Arrays.stream(beanClass).map(Class::getName).collect(Collectors.toList())); } @@ -19,10 +31,19 @@ public void addAdditionalBean(Class... beanClass) { 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, Collection> transformer) { + annotationTransformers.add(transformer); + } + + public void addExtension(String extensionClass) { + extensions.add(extensionClass); + } + public List getAdditionalBeans() { return additionalBeans; } @@ -30,4 +51,13 @@ public List getAdditionalBeans() { public Map getGeneratedBeans() { return generatedBeans; } + + public List, Collection>> getAnnotationTransformers() { + return annotationTransformers; + } + + public List getExtensions() { + return extensions; + } + } diff --git a/core/deployment/src/main/java/org/jboss/shamrock/deployment/RuntimePriority.java b/core/deployment/src/main/java/org/jboss/shamrock/deployment/RuntimePriority.java index 8b77753814543..a97f980c65937 100644 --- a/core/deployment/src/main/java/org/jboss/shamrock/deployment/RuntimePriority.java +++ b/core/deployment/src/main/java/org/jboss/shamrock/deployment/RuntimePriority.java @@ -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; diff --git a/examples/permissive/pom.xml b/examples/permissive/pom.xml index d8afb1973fac4..1751cf1304691 100644 --- a/examples/permissive/pom.xml +++ b/examples/permissive/pom.xml @@ -32,6 +32,11 @@ shamrock-openapi-deployment provided + + org.jboss.shamrock + shamrock-fault-tolerance-deployment + provided + org.jboss.resteasy resteasy-jaxb-provider diff --git a/examples/permissive/src/main/java/org/jboss/shamrock/example/faulttolerance/Service.java b/examples/permissive/src/main/java/org/jboss/shamrock/example/faulttolerance/Service.java new file mode 100644 index 0000000000000..bb9c3b7983ce0 --- /dev/null +++ b/examples/permissive/src/main/java/org/jboss/shamrock/example/faulttolerance/Service.java @@ -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()); + } + +} \ No newline at end of file diff --git a/examples/permissive/src/main/java/org/jboss/shamrock/example/faulttolerance/TestResource.java b/examples/permissive/src/main/java/org/jboss/shamrock/example/faulttolerance/TestResource.java new file mode 100644 index 0000000000000..0b37dc379a0d2 --- /dev/null +++ b/examples/permissive/src/main/java/org/jboss/shamrock/example/faulttolerance/TestResource.java @@ -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; + } + +} diff --git a/examples/permissive/src/test/java/org/jboss/shamrock/example/test/FaultToleranceITCase.java b/examples/permissive/src/test/java/org/jboss/shamrock/example/test/FaultToleranceITCase.java new file mode 100644 index 0000000000000..208751e8bdb36 --- /dev/null +++ b/examples/permissive/src/test/java/org/jboss/shamrock/example/test/FaultToleranceITCase.java @@ -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 { + +} diff --git a/examples/permissive/src/test/java/org/jboss/shamrock/example/test/FaultToleranceTestCase.java b/examples/permissive/src/test/java/org/jboss/shamrock/example/test/FaultToleranceTestCase.java new file mode 100644 index 0000000000000..f5a56c262802d --- /dev/null +++ b/examples/permissive/src/test/java/org/jboss/shamrock/example/test/FaultToleranceTestCase.java @@ -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())); + } +} diff --git a/examples/strict/pom.xml b/examples/strict/pom.xml index b67f1bcf5acec..0cd9776d0ea19 100644 --- a/examples/strict/pom.xml +++ b/examples/strict/pom.xml @@ -101,6 +101,11 @@ shamrock-rest-client-deployment provided + + org.jboss.shamrock + shamrock-fault-tolerance-deployment + provided + org.postgresql postgresql-protean diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/AnnotationTransformer.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/AnnotationTransformer.java new file mode 100644 index 0000000000000..5ed6c3961b190 --- /dev/null +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/AnnotationTransformer.java @@ -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, Collection>> transformers = null; + + Collection getAnnotations(AnnotationTarget target) { + Collection annotations = null; + for (BiFunction, Collection> transformer : transformers) { + annotations = transformer.apply(target, annotations); + } + return annotations; + } + +} diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanDeployment.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanDeployment.java index 8668a9f22d1ad..9e4cb8d36166e 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanDeployment.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanDeployment.java @@ -8,6 +8,7 @@ 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 additionalBeanDefiningAnnotations) { + private final List, Collection>> annotationTransformers; + + BeanDeployment(IndexView index, Collection additionalBeanDefiningAnnotations, + List, Collection>> 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 @@ ClassInfo getInterceptorBinding(DotName name) { return interceptorBindings.get(name); } + Collection getAnnotations(AnnotationTarget target) { + Collection 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, Collection> transformer : annotationTransformers) { + annotations = transformer.apply(target, annotations); + } + return annotations; + } + void init() { long start = System.currentTimeMillis(); for (BeanInfo bean : beans) { @@ -142,8 +169,8 @@ private List findBeans(List beanDefiningAnnotations, List findBeans(List beanDefiningAnnotations, List> interceptedMethods; private Map> lifecycleInterceptors; - + private final Integer alternativePriority; /** @@ -201,11 +201,11 @@ List getBoundInterceptors() { DisposerInfo getDisposer() { return disposer; } - + boolean isAlternative() { return alternativePriority != null; } - + Integer getAlternativePriority() { return alternativePriority; } @@ -278,7 +278,7 @@ private void putLifecycleInterceptors(Map 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)) { diff --git a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanProcessor.java b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanProcessor.java index a83f9abafa9f3..9acd08586dea1 100644 --- a/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanProcessor.java +++ b/ext/arc/processor/src/main/java/org/jboss/protean/arc/processor/BeanProcessor.java @@ -10,12 +10,14 @@ 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 static Builder builder() { private final ReflectionRegistration reflectionRegistration; + private final List, Collection>> annotationTransformers; + private BeanProcessor(String name, IndexView index, Collection additionalBeanDefiningAnnotations, ResourceOutput output, - boolean sharedAnnotationLiterals, ReflectionRegistration reflectionRegistration) { + boolean sharedAnnotationLiterals, ReflectionRegistration reflectionRegistration, + List, Collection>> annotationTransformers) { this.reflectionRegistration = reflectionRegistration; Objects.requireNonNull(output); this.name = name; @@ -61,11 +66,12 @@ private BeanProcessor(String name, IndexView index, Collection addition 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 static class Builder { private ReflectionRegistration reflectionRegistration = ReflectionRegistration.NOOP; + private final List, Collection>> annotationTransformers = new ArrayList<>(); + public Builder setName(String name) { this.name = name; return this; @@ -200,9 +208,14 @@ public Builder setReflectionRegistration(ReflectionRegistration reflectionRegist return this; } + public Builder addAnnotationTransformer(BiFunction, Collection> transformer) { + this.annotationTransformers.add(transformer); + return this; + } + public BeanProcessor build() { return new BeanProcessor(name, addBuiltinQualifiersIfNeeded(index), additionalBeanDefiningAnnotations, output, sharedAnnotationLiterals, - reflectionRegistration); + reflectionRegistration, annotationTransformers); } } diff --git a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanGeneratorTest.java b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanGeneratorTest.java index cd3cdc252f759..beba57566578b 100644 --- a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanGeneratorTest.java +++ b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanGeneratorTest.java @@ -13,10 +13,6 @@ 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 void testGenerator() throws IOException { 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(); diff --git a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoInjectionsTest.java b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoInjectionsTest.java index 069d86dbeae57..b6688f91a2cbd 100644 --- a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoInjectionsTest.java +++ b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoInjectionsTest.java @@ -16,9 +16,6 @@ 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 void testInjections() throws IOException { 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 injections = barBean.getInjections(); assertEquals(3, injections.size()); diff --git a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoQualifiersTest.java b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoQualifiersTest.java index a049ff9ca52a3..be8baef1f7e21 100644 --- a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoQualifiersTest.java +++ b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoQualifiersTest.java @@ -9,15 +9,12 @@ 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 void testQualifiers() throws IOException { 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); diff --git a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoTypesTest.java b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoTypesTest.java index 9388e9c346ee8..3f0d044fc9696 100644 --- a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoTypesTest.java +++ b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/BeanInfoTypesTest.java @@ -18,9 +18,6 @@ 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 void testResolver() throws IOException { 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); diff --git a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/ClientProxyGeneratorTest.java b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/ClientProxyGeneratorTest.java index b47a4cbe0590f..453246fb9ccfc 100644 --- a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/ClientProxyGeneratorTest.java +++ b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/ClientProxyGeneratorTest.java @@ -12,11 +12,6 @@ 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(); diff --git a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/InterceptorGeneratorTest.java b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/InterceptorGeneratorTest.java index 9e760827b4415..9959dadf77d75 100644 --- a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/InterceptorGeneratorTest.java +++ b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/InterceptorGeneratorTest.java @@ -17,11 +17,6 @@ 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() diff --git a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/SubclassGeneratorTest.java b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/SubclassGeneratorTest.java index ea184bc04a97d..25ab9f5ce9730 100644 --- a/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/SubclassGeneratorTest.java +++ b/ext/arc/processor/src/test/java/org/jboss/protean/arc/processor/SubclassGeneratorTest.java @@ -20,12 +20,6 @@ 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(); diff --git a/fault-tolerance/deployment/pom.xml b/fault-tolerance/deployment/pom.xml new file mode 100644 index 0000000000000..978d79f8ac647 --- /dev/null +++ b/fault-tolerance/deployment/pom.xml @@ -0,0 +1,26 @@ + + + + shamrock-fault-tolerance + org.jboss.shamrock + 1.0.0.Alpha1-SNAPSHOT + ../ + + 4.0.0 + + shamrock-fault-tolerance-deployment + + + + org.jboss.shamrock + shamrock-core-deployment + + + org.jboss.shamrock + shamrock-fault-tolerance-runtime + + + + diff --git a/fault-tolerance/deployment/src/main/java/org/jboss/shamrock/faulttolerance/deployment/FaultToleranceAnnotationProcessor.java b/fault-tolerance/deployment/src/main/java/org/jboss/shamrock/faulttolerance/deployment/FaultToleranceAnnotationProcessor.java new file mode 100644 index 0000000000000..943640e630033 --- /dev/null +++ b/fault-tolerance/deployment/src/main/java/org/jboss/shamrock/faulttolerance/deployment/FaultToleranceAnnotationProcessor.java @@ -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 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 ftClasses = new HashSet<>(); + for (DotName annotation : FT_ANNOTATIONS) { + Collection 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, Collection>() { + @Override + public Collection apply(AnnotationTarget target, Collection annotations) { + if (Kind.CLASS != target.kind() || !ftClasses.contains(target.asClass().name().toString())) { + return annotations; + } + // Add @HystrixCommandBinding + List 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; + } + +} diff --git a/fault-tolerance/deployment/src/main/java/org/jboss/shamrock/faulttolerance/deployment/FaultToleranceSetup.java b/fault-tolerance/deployment/src/main/java/org/jboss/shamrock/faulttolerance/deployment/FaultToleranceSetup.java new file mode 100644 index 0000000000000..ae9bdfbee8b89 --- /dev/null +++ b/fault-tolerance/deployment/src/main/java/org/jboss/shamrock/faulttolerance/deployment/FaultToleranceSetup.java @@ -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()); + } +} diff --git a/fault-tolerance/deployment/src/main/resources/META-INF/services/org.jboss.shamrock.deployment.ShamrockSetup b/fault-tolerance/deployment/src/main/resources/META-INF/services/org.jboss.shamrock.deployment.ShamrockSetup new file mode 100644 index 0000000000000..202d596921562 --- /dev/null +++ b/fault-tolerance/deployment/src/main/resources/META-INF/services/org.jboss.shamrock.deployment.ShamrockSetup @@ -0,0 +1 @@ +org.jboss.shamrock.faulttolerance.deployment.FaultToleranceSetup \ No newline at end of file diff --git a/fault-tolerance/pom.xml b/fault-tolerance/pom.xml new file mode 100644 index 0000000000000..2154148e51a96 --- /dev/null +++ b/fault-tolerance/pom.xml @@ -0,0 +1,19 @@ + + + + shamrock-parent + org.jboss.shamrock + 1.0.0.Alpha1-SNAPSHOT + + 4.0.0 + + shamrock-fault-tolerance + pom + + deployment + runtime + + + diff --git a/fault-tolerance/runtime/pom.xml b/fault-tolerance/runtime/pom.xml new file mode 100644 index 0000000000000..1e7e108e07835 --- /dev/null +++ b/fault-tolerance/runtime/pom.xml @@ -0,0 +1,40 @@ + + + + shamrock-fault-tolerance + org.jboss.shamrock + 1.0.0.Alpha1-SNAPSHOT + ../ + + 4.0.0 + + shamrock-fault-tolerance-runtime + + + + org.jboss.shamrock + shamrock-core-runtime + + + io.smallrye + smallrye-fault-tolerance + + + org.slf4j + slf4j-api + + + + + + + + + maven-dependency-plugin + + + + + diff --git a/fault-tolerance/runtime/src/main/java/org/jboss/shamrock/faulttolerance/runtime/ShamrockFallbackHandlerProvider.java b/fault-tolerance/runtime/src/main/java/org/jboss/shamrock/faulttolerance/runtime/ShamrockFallbackHandlerProvider.java new file mode 100644 index 0000000000000..64e25a3fa3a58 --- /dev/null +++ b/fault-tolerance/runtime/src/main/java/org/jboss/shamrock/faulttolerance/runtime/ShamrockFallbackHandlerProvider.java @@ -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 instance; + + @Override + public FallbackHandler get(FaultToleranceOperation operation) { + if (operation.hasFallback()) { + return new FallbackHandler() { + @SuppressWarnings("unchecked") + @Override + public T handle(ExecutionContext context) { + Class clazz = operation.getFallback().get(FallbackConfig.VALUE); + FallbackHandler fallbackHandlerInstance = (FallbackHandler) instance.select(clazz).get(); + try { + return fallbackHandlerInstance.handle(context); + } finally { + // The instance exists to service a single invocation only + instance.destroy(fallbackHandlerInstance); + } + } + }; + } + return null; + } + +} diff --git a/fault-tolerance/runtime/src/main/java/org/jboss/shamrock/faulttolerance/runtime/ShamrockFaultToleranceOperationProvider.java b/fault-tolerance/runtime/src/main/java/org/jboss/shamrock/faulttolerance/runtime/ShamrockFaultToleranceOperationProvider.java new file mode 100644 index 0000000000000..ce440c8b2b51c --- /dev/null +++ b/fault-tolerance/runtime/src/main/java/org/jboss/shamrock/faulttolerance/runtime/ShamrockFaultToleranceOperationProvider.java @@ -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); + } + +} diff --git a/pom.xml b/pom.xml index 4190770ce8574..48c8feb7f77a0 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ 2.1.0.Beta1 4.0.0.Beta5 - 3.0.4.Final + 3.0.5.Final 2.0.12.Final 3.3.8.Final 1.0.0.Final @@ -25,6 +25,7 @@ 1.0.1 1.1.0 1.0.2 + 1.0.2 1 1.2.1.Final 1.5.1.Final @@ -80,6 +81,7 @@ transactions agroal rest-client + fault-tolerance examples @@ -281,6 +283,16 @@ shamrock-arc-runtime ${project.version} + + org.jboss.shamrock + shamrock-fault-tolerance-deployment + ${project.version} + + + org.jboss.shamrock + shamrock-fault-tolerance-runtime + ${project.version} + org.jboss.protean.gizmo @@ -380,6 +392,11 @@ smallrye-open-api ${smallrye-open-api.version} + + io.smallrye + smallrye-fault-tolerance + ${smallrye-fault-tolerance.version} + javax.inject javax.inject diff --git a/weld/deployment/src/main/java/org/jboss/shamrock/weld/deployment/WeldAnnotationProcessor.java b/weld/deployment/src/main/java/org/jboss/shamrock/weld/deployment/WeldAnnotationProcessor.java index 247110d36633c..c2c03d7737f4e 100644 --- a/weld/deployment/src/main/java/org/jboss/shamrock/weld/deployment/WeldAnnotationProcessor.java +++ b/weld/deployment/src/main/java/org/jboss/shamrock/weld/deployment/WeldAnnotationProcessor.java @@ -44,6 +44,9 @@ public void process(ArchiveContext archiveContext, ProcessorContext processorCon 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); diff --git a/weld/runtime/src/main/java/org/jboss/shamrock/weld/runtime/WeldDeploymentTemplate.java b/weld/runtime/src/main/java/org/jboss/shamrock/weld/runtime/WeldDeploymentTemplate.java index fe2062bd66e54..2211be783e926 100644 --- a/weld/runtime/src/main/java/org/jboss/shamrock/weld/runtime/WeldDeploymentTemplate.java +++ b/weld/runtime/src/main/java/org/jboss/shamrock/weld/runtime/WeldDeploymentTemplate.java @@ -15,6 +15,7 @@ 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 void addClass(SeContainerInitializer initializer, Class clazz) { public void addInterceptor(SeContainerInitializer initialize, Class interceptorClass) { initialize.enableInterceptors(interceptorClass); } + + @SuppressWarnings("unchecked") + public void addExtension(SeContainerInitializer initializer, Class extensionClazz) { + initializer.addExtensions((Class)extensionClazz); + } @ContextObject("weld.container") public SeContainer doBoot(StartupContext startupContext, SeContainerInitializer initializer) throws Exception {