From b50f56da5b180d546c3f5fa9f5df91ebe0ddf99e Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Mon, 30 Jan 2023 16:36:51 +0100 Subject: [PATCH 1/8] ArC: fix InstanceHandle.close() to behave as specified in strict mode This means that it delegates to `destroy()` not only for `@Dependent` beans, but for all beans. To avoid breaking existing users, this is only done in the strict mode. --- .../java/io/quarkus/arc/InstanceHandle.java | 6 ++ .../src/test/resources/testng.xml | 6 -- .../io/quarkus/arc/test/ArcTestContainer.java | 3 +- .../destroy/InstanceHandleDestroyTest.java | 81 +++++++++++++++++++ 4 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/destroy/InstanceHandleDestroyTest.java diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InstanceHandle.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InstanceHandle.java index 63bce6bf5fd68..dbca61638c8d1 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InstanceHandle.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InstanceHandle.java @@ -64,6 +64,12 @@ default InjectableBean getBean() { */ @Override default void close() { + // https://github.com/quarkusio/quarkus/issues/33665 + if (Arc.container().strictCompatibility()) { + destroy(); + return; + } + InjectableBean bean = getBean(); if (bean == null || Dependent.class.equals(bean.getScope())) { destroy(); diff --git a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml b/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml index 844b4b621e59a..41731805956b1 100644 --- a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml +++ b/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml @@ -100,12 +100,6 @@ - - - - - - diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java index 6855a1c35a9b0..05b799d33fde4 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/ArcTestContainer.java @@ -29,6 +29,7 @@ import org.junit.jupiter.api.extension.ExtensionContext; import io.quarkus.arc.Arc; +import io.quarkus.arc.ArcInitConfig; import io.quarkus.arc.ComponentsProvider; import io.quarkus.arc.ResourceReferenceProvider; import io.quarkus.arc.processor.AlternativePriorities; @@ -468,7 +469,7 @@ public void writeResource(Resource resource) throws IOException { .setContextClassLoader(testClassLoader); // Now we are ready to initialize Arc - Arc.initialize(); + Arc.initialize(ArcInitConfig.builder().setStrictCompatibility(strictCompatibility).build()); } catch (Throwable e) { if (shouldFail) { diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/destroy/InstanceHandleDestroyTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/destroy/InstanceHandleDestroyTest.java new file mode 100644 index 0000000000000..4cd5c46fc9198 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/instance/destroy/InstanceHandleDestroyTest.java @@ -0,0 +1,81 @@ +package io.quarkus.arc.test.instance.destroy; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Dependent; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; +import io.quarkus.arc.test.ArcTestContainer; + +public class InstanceHandleDestroyTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer.Builder() + .beanClasses(MyDependentBean.class, MyAppScopedBean.class) + .strictCompatibility(true) + .build(); + + @Test + public void testDestroy() { + assertFalse(MyDependentBean.DESTROYED.get()); + try (InstanceHandle handle = Arc.container().instance(MyDependentBean.class)) { + assertNotNull(handle.get().toString()); + } + assertTrue(MyDependentBean.DESTROYED.get()); + + // normal-scoped + String oldId; + assertFalse(MyAppScopedBean.DESTROYED.get()); + try (InstanceHandle handle = Arc.container().instance(MyAppScopedBean.class)) { + assertNotNull(handle.get().toString()); + oldId = handle.get().getId(); + } + assertTrue(MyAppScopedBean.DESTROYED.get()); + + String newId = Arc.container().instance(MyAppScopedBean.class).get().getId(); + assertNotEquals(oldId, newId); + } + + @Dependent + static class MyDependentBean { + static final AtomicBoolean DESTROYED = new AtomicBoolean(false); + + @PreDestroy + void destroy() { + DESTROYED.set(true); + } + } + + @ApplicationScoped + static class MyAppScopedBean { + static final AtomicBoolean DESTROYED = new AtomicBoolean(false); + + String id; + + String getId() { + return id; + } + + @PostConstruct + void init() { + this.id = UUID.randomUUID().toString(); + } + + @PreDestroy + void destroy() { + DESTROYED.set(true); + } + } +} From 9c951143d6eda14edb9b705f0de74e544115bd50 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 3 Mar 2023 18:08:36 +0100 Subject: [PATCH 2/8] ArC: fix inheritance of interceptor binding annotations Class-level interceptor bindings declared on a superclass should only be taken into account when they are `@Inherited`. ArC however inherits all interceptor bindings from superclasses, even if they are _not_ declared `@Inherited`. This commit fixes that in the strict mode, to not affect Quarkus security (which assumes that security annotations are inherited, even if they are not). Method-level annotations are never inherited. This means that the lowest override of a method determines whether method-level interceptor binding annotations are present. ArC used to behave differently. If a method on a class had no interceptor binding annotations but overrode a method from a superclass that _did_ have interceptor binding annotations, the method-level annotations from the superclass were inherited. This commit fixes that. --- .../io/quarkus/arc/processor/BeanInfo.java | 13 ++- .../io/quarkus/arc/processor/Methods.java | 14 +-- .../src/test/resources/testng.xml | 4 - .../inherited/InheritedBindingOnBeanTest.java | 96 +++++++++++++++++++ ...itedMethodsWithInterceptorBindingTest.java | 72 ++++++++++++++ .../InheritedClassLevel.java | 2 + 6 files changed, 187 insertions(+), 14 deletions(-) create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/inherited/InheritedBindingOnBeanTest.java create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/inherited/InheritedMethodsWithInterceptorBindingTest.java diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java index 160e9cb46cba8..9e904309f8882 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java @@ -797,7 +797,7 @@ private void putLifecycleInterceptors(Map li private void addClassLevelBindings(ClassInfo targetClass, Collection bindings) { List classLevelBindings = new ArrayList<>(); - doAddClassLevelBindings(targetClass, classLevelBindings, Set.of()); + doAddClassLevelBindings(targetClass, classLevelBindings, Set.of(), false); bindings.addAll(classLevelBindings); if (!stereotypes.isEmpty()) { // interceptor binding declared on a bean class replaces an interceptor binding of the same type @@ -808,22 +808,27 @@ private void addClassLevelBindings(ClassInfo targetClass, Collection bindings, Set skip) { + private void doAddClassLevelBindings(ClassInfo classInfo, Collection bindings, Set skip, + boolean onlyInherited) { beanDeployment.getAnnotations(classInfo).stream() .filter(a -> beanDeployment.getInterceptorBinding(a.name()) != null) .filter(a -> !skip.contains(a.name())) + .filter(a -> !onlyInherited + || beanDeployment.hasAnnotation(beanDeployment.getInterceptorBinding(a.name()), DotNames.INHERITED)) .forEach(bindings::add); if (classInfo.superClassType() != null && !classInfo.superClassType().name().equals(DotNames.OBJECT)) { ClassInfo superClass = getClassByName(beanDeployment.getBeanArchiveIndex(), classInfo.superName()); if (superClass != null) { - doAddClassLevelBindings(superClass, bindings, skip); + // proper interceptor binding inheritance only in strict mode, due to Quarkus expecting security + // annotations (such as `@RolesAllowed`) to be inherited, even though they are not `@Inherited` + doAddClassLevelBindings(superClass, bindings, skip, beanDeployment.strictCompatibility); } } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java index c6c3c7e4df94a..5f57163fe681e 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java @@ -183,18 +183,20 @@ private static Set addInterceptedMethodCandidates(BeanDeployment bea skipPredicate.startProcessing(classInfo, originalClassInfo); for (MethodInfo method : classInfo.methods()) { + MethodKey key = new MethodKey(method); + if (candidates.containsKey(key)) { + continue; + } + // Note that we must merge the bindings first Set bindings = mergeBindings(beanDeployment, originalClassInfo, classLevelBindings, ignoreMethodLevelBindings, method, noClassInterceptorsMethods); - if (bindings.isEmpty() && !targetHasAroundInvokes) { - // No bindings found and target class does not declare around invoke interceptor methods - continue; - } + boolean possiblyIntercepted = !bindings.isEmpty() || targetHasAroundInvokes; if (skipPredicate.test(method)) { continue; } boolean addToCandidates = true; - if (Modifier.isFinal(method.flags())) { + if (Modifier.isFinal(method.flags()) && possiblyIntercepted) { if (transformUnproxyableClasses && !isNoninterceptableKotlinMethod(method)) { methodsFromWhichToRemoveFinal.add(NameAndDescriptor.fromMethodInfo(method)); } else { @@ -203,7 +205,7 @@ private static Set addInterceptedMethodCandidates(BeanDeployment bea } } if (addToCandidates) { - candidates.computeIfAbsent(new Methods.MethodKey(method), key -> bindings); + candidates.putIfAbsent(key, bindings); } } skipPredicate.methodsProcessed(); diff --git a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml b/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml index 41731805956b1..60e5d372194dc 100644 --- a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml +++ b/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml @@ -60,11 +60,7 @@ - - - - diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/inherited/InheritedBindingOnBeanTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/inherited/InheritedBindingOnBeanTest.java new file mode 100644 index 0000000000000..6f08c6cf41389 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/inherited/InheritedBindingOnBeanTest.java @@ -0,0 +1,96 @@ +package io.quarkus.arc.test.interceptors.bindings.inherited; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.inject.Singleton; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.test.ArcTestContainer; + +public class InheritedBindingOnBeanTest { + @RegisterExtension + public ArcTestContainer container = new ArcTestContainer.Builder() + .beanClasses(MyBean.class, FooBinding.class, BarBinding.class, FooInterceptor.class, BarInterceptor.class) + .strictCompatibility(true) // correct interceptor binding inheritance + .build(); + + @Test + public void testInterception() { + MyBean bean = Arc.container().instance(MyBean.class).get(); + assertNotNull(bean); + bean.doSomething(); + assertTrue(FooInterceptor.intercepted); + assertFalse(BarInterceptor.intercepted); + } + + @FooBinding + @BarBinding + static class MySuperclass { + } + + @Singleton + static class MyBean extends MySuperclass { + void doSomething() { + } + } + + @Target({ TYPE, METHOD }) + @Retention(RUNTIME) + @Documented + @InterceptorBinding + @Inherited + @interface FooBinding { + } + + @Target({ TYPE, METHOD }) + @Retention(RUNTIME) + @Documented + @InterceptorBinding + // not @Inherited + @interface BarBinding { + } + + @FooBinding + @Interceptor + @Priority(1) + static class FooInterceptor { + static boolean intercepted = false; + + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + intercepted = true; + return ctx.proceed(); + } + } + + @BarBinding + @Interceptor + @Priority(1) + static class BarInterceptor { + static boolean intercepted = false; + + @AroundInvoke + Object intercept(InvocationContext ctx) throws Exception { + intercepted = true; + return ctx.proceed(); + } + } +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/inherited/InheritedMethodsWithInterceptorBindingTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/inherited/InheritedMethodsWithInterceptorBindingTest.java new file mode 100644 index 0000000000000..590648eaba2de --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/bindings/inherited/InheritedMethodsWithInterceptorBindingTest.java @@ -0,0 +1,72 @@ +package io.quarkus.arc.test.interceptors.bindings.inherited; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.Dependent; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InterceptorBinding; +import jakarta.interceptor.InvocationContext; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.test.ArcTestContainer; + +public class InheritedMethodsWithInterceptorBindingTest { + @RegisterExtension + ArcTestContainer container = new ArcTestContainer(MyBean.class, MyInterceptorBinding.class, MyInterceptor.class); + + @Test + public void test() { + MyBean bean = Arc.container().instance(MyBean.class).get(); + assertEquals("foobar", bean.foobar()); + assertEquals("intercepted: foobar", bean.foobarNotInherited()); + } + + static class MySuperclass { + @MyInterceptorBinding + String foobar() { + return "this should be ignored"; + } + + @MyInterceptorBinding + String foobarNotInherited() { + return "foobar"; + } + } + + @Dependent + static class MyBean extends MySuperclass { + @Override + String foobar() { + return "foobar"; + } + } + + @Target({ ElementType.TYPE, ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + @Documented + @InterceptorBinding + @interface MyInterceptorBinding { + } + + @MyInterceptorBinding + @Priority(1) + @Interceptor + static class MyInterceptor { + @AroundInvoke + public Object intercept(InvocationContext ctx) throws Exception { + return "intercepted: " + ctx.proceed(); + } + } + +} diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/noclassinterceptors/InheritedClassLevel.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/noclassinterceptors/InheritedClassLevel.java index a844883ff8152..299571408e9f1 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/noclassinterceptors/InheritedClassLevel.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/interceptors/noclassinterceptors/InheritedClassLevel.java @@ -6,6 +6,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -15,5 +16,6 @@ @Retention(RUNTIME) @Documented @InterceptorBinding +@Inherited public @interface InheritedClassLevel { } From 778abc8d375fcc7b859df7d1e3fde3d6f7629d77 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Mon, 22 May 2023 11:07:38 +0200 Subject: [PATCH 3/8] ArC: defer unproxyability errors to runtime in strict mode The CDI specification demands that if an injection point resolves to a bean, unproxyability of that bean should be treated as a deployment problem. ArC used to treat all unproxyable beans uniformly, even those that are not injected anywhere. This commit changes that: in strict mode, only unproxyable beans that are actually injected somewhere trigger a deployment-time error. Unproxyable beans that are not injected anywhere do not cause a deployment-time error and instead, the `Bean` class (its `get(CreationalContext)` method, specifically) is generated in a way that `UnproxyableResolutionException` is thrown when obtaining a contextual reference, as expected by the CDI spec. --- .../quarkus/arc/processor/BeanDeployment.java | 24 ++++- .../quarkus/arc/processor/BeanGenerator.java | 10 ++- .../io/quarkus/arc/processor/BeanInfo.java | 4 +- .../java/io/quarkus/arc/processor/Beans.java | 90 +++++++++++-------- .../arc/processor/ClientProxyGenerator.java | 6 ++ .../src/test/resources/testng.xml | 9 -- ...> FinalMethodIllegalWhenInjectedTest.java} | 13 ++- ...FinalMethodIllegalWhenNotInjectedTest.java | 35 ++++++++ 8 files changed, 141 insertions(+), 50 deletions(-) rename independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/clientproxy/finalmethod/{FinalMethodIllegalTest.java => FinalMethodIllegalWhenInjectedTest.java} (76%) create mode 100644 independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/clientproxy/finalmethod/FinalMethodIllegalWhenNotInjectedTest.java diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java index f5fb1364f1109..8af6b4fefca8d 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java @@ -104,6 +104,8 @@ public class BeanDeployment { private final Set removedBeans; + private final Set beansWithRuntimeDeferredUnproxyableError; + private final Map> customContexts; private final Map beanDefiningAnnotations; @@ -149,6 +151,7 @@ public class BeanDeployment { this.removeUnusedBeans = builder.removeUnusedBeans; this.unusedExclusions = removeUnusedBeans ? new ArrayList<>(builder.removalExclusions) : null; this.removedBeans = removeUnusedBeans ? new CopyOnWriteArraySet<>() : Collections.emptySet(); + this.beansWithRuntimeDeferredUnproxyableError = Collections.newSetFromMap(new ConcurrentHashMap<>()); this.customContexts = new ConcurrentHashMap<>(); this.excludeTypes = builder.excludeTypes != null ? new ArrayList<>(builder.excludeTypes) : Collections.emptyList(); @@ -506,6 +509,14 @@ public Collection getRemovedBeans() { return Collections.unmodifiableSet(removedBeans); } + boolean hasRuntimeDeferredUnproxyableError(BeanInfo bean) { + return beansWithRuntimeDeferredUnproxyableError.contains(bean); + } + + void deferUnproxyableErrorToRuntime(BeanInfo bean) { + beansWithRuntimeDeferredUnproxyableError.add(bean); + } + public Collection getQualifiers() { return Collections.unmodifiableCollection(qualifiers.values()); } @@ -1522,6 +1533,17 @@ private void validateBeans(List errors, Consumer Map> namedBeans = new HashMap<>(); Set classesReceivingNoArgsCtor = new HashSet<>(); + // this set is only used in strict compatible mode (see `Beans.validateBean()`), + // so no need to initialize it otherwise + Set injectedBeans = new HashSet<>(); + if (strictCompatibility) { + for (InjectionPointInfo injectionPoint : this.injectionPoints) { + if (injectionPoint.hasResolvedBean()) { + injectedBeans.add(injectionPoint.getResolvedBean()); + } + } + } + for (BeanInfo bean : beans) { if (bean.getName() != null) { List named = namedBeans.get(bean.getName()); @@ -1532,7 +1554,7 @@ private void validateBeans(List errors, Consumer named.add(bean); findNamespaces(bean, namespaces); } - bean.validate(errors, bytecodeTransformerConsumer, classesReceivingNoArgsCtor); + bean.validate(errors, bytecodeTransformerConsumer, classesReceivingNoArgsCtor, injectedBeans); } if (!namedBeans.isEmpty()) { diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java index 15af009794e8a..40f5469aa85b4 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java @@ -30,6 +30,7 @@ import jakarta.enterprise.context.spi.CreationalContext; import jakarta.enterprise.inject.CreationException; import jakarta.enterprise.inject.IllegalProductException; +import jakarta.enterprise.inject.UnproxyableResolutionException; import jakarta.enterprise.inject.literal.InjectLiteral; import jakarta.enterprise.inject.spi.InterceptionType; import jakarta.interceptor.InvocationContext; @@ -1927,7 +1928,10 @@ protected void implementGet(BeanInfo bean, ClassCreator beanCreator, ProviderTyp MethodCreator get = beanCreator.getMethodCreator("get", providerType.descriptorName(), CreationalContext.class) .setModifiers(ACC_PUBLIC); - if (BuiltinScope.DEPENDENT.is(bean.getScope())) { + if (bean.getDeployment().hasRuntimeDeferredUnproxyableError(bean)) { + get.throwException(UnproxyableResolutionException.class, "Bean not proxyable: " + bean); + get.returnValue(get.loadNull()); + } else if (BuiltinScope.DEPENDENT.is(bean.getScope())) { // @Dependent pseudo-scope // Foo instance = create(ctx) ResultHandle instance = get.invokeVirtualMethod( @@ -2214,6 +2218,10 @@ private ResultHandle wrapCurrentInjectionPoint(BeanInfo bean, } private void initializeProxy(BeanInfo bean, String baseName, ClassCreator beanCreator) { + if (bean.getDeployment().hasRuntimeDeferredUnproxyableError(bean)) { + return; + } + // Add proxy volatile field String proxyTypeName = getProxyTypeName(bean, baseName); beanCreator.getFieldCreator(FIELD_NAME_PROXY, proxyTypeName) diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java index 9e904309f8882..94b5567a1df75 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanInfo.java @@ -582,8 +582,8 @@ public String getClientProxyPackageName() { } void validate(List errors, Consumer bytecodeTransformerConsumer, - Set classesReceivingNoArgsCtor) { - Beans.validateBean(this, errors, bytecodeTransformerConsumer, classesReceivingNoArgsCtor); + Set classesReceivingNoArgsCtor, Set injectedBeans) { + Beans.validateBean(this, errors, bytecodeTransformerConsumer, classesReceivingNoArgsCtor, injectedBeans); } void validateInterceptorDecorator(List errors, Consumer bytecodeTransformerConsumer) { diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java index f6200b874084f..31ba850b8f231 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java @@ -711,25 +711,34 @@ static void validateInterceptorDecorator(BeanInfo bean, List errors, } static void validateBean(BeanInfo bean, List errors, Consumer bytecodeTransformerConsumer, - Set classesReceivingNoArgsCtor) { + Set classesReceivingNoArgsCtor, Set injectedBeans) { + + // by default, we fail deployment due to unproxyability for all beans, but in strict mode, + // we only do that for beans that are injected somewhere -- and defer the error to runtime otherwise, + // due to CDI spec requirements + boolean failIfNotProxyable = bean.getDeployment().strictCompatibility ? injectedBeans.contains(bean) : true; + if (bean.isClassBean()) { ClassInfo beanClass = bean.getTarget().get().asClass(); String classifier = bean.getScope().isNormal() ? "Normal scoped" : null; if (classifier == null && bean.isSubclassRequired()) { classifier = "Intercepted"; + failIfNotProxyable = true; } if (Modifier.isFinal(beanClass.flags()) && classifier != null) { // Client proxies and subclasses require a non-final class if (bean.getDeployment().transformUnproxyableClasses) { bytecodeTransformerConsumer .accept(new BytecodeTransformer(beanClass.name().toString(), new FinalClassTransformFunction())); - } else { + } else if (failIfNotProxyable) { errors.add(new DeploymentException(String.format("%s bean must not be final: %s", classifier, bean))); + } else { + bean.getDeployment().deferUnproxyableErrorToRuntime(bean); } } if (bean.getDeployment().strictCompatibility && classifier != null) { - validateNonStaticFinalMethods(beanClass, bean.getDeployment().getBeanArchiveIndex(), - classifier, errors); + validateNonStaticFinalMethods(bean, beanClass, bean.getDeployment().getBeanArchiveIndex(), + classifier, errors, failIfNotProxyable); } MethodInfo noArgsConstructor = beanClass.method(Methods.INIT); @@ -754,13 +763,16 @@ static void validateBean(BeanInfo bean, List errors, Consumer errors, Consumer errors, Consumer errors, Consumer errors, Consumer errors) { + private static void validateNonStaticFinalMethods(BeanInfo bean, ClassInfo clazz, IndexView beanArchiveIndex, + String classifier, List errors, boolean failIfNotProxyable) { // see also Methods.skipForClientProxy() while (!clazz.name().equals(DotNames.OBJECT)) { for (MethodInfo method : clazz.methods()) { @@ -920,9 +936,13 @@ private static void validateNonStaticFinalMethods(ClassInfo clazz, IndexView bea } if (Modifier.isFinal(method.flags())) { - errors.add(new DeploymentException(String.format( - "%s bean must not declare non-static final methods with public, protected or default visibility: %s", - classifier, method))); + if (failIfNotProxyable) { + errors.add(new DeploymentException(String.format( + "%s bean must not declare non-static final methods with public, protected or default visibility: %s", + classifier, method))); + } else { + bean.getDeployment().deferUnproxyableErrorToRuntime(bean); + } } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java index ea0277af08f00..8421c9aed59ee 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java @@ -78,6 +78,12 @@ public ClientProxyGenerator(Predicate applicationClassPredicate, boolea Collection generate(BeanInfo bean, String beanClassName, Consumer bytecodeTransformerConsumer, boolean transformUnproxyableClasses) { + // see `BeanGenerator` -- if this bean is unproxyable and that error is deferred to runtime, + // we don't need to (and cannot, in fact) generate the client proxy class + if (bean.getDeployment().hasRuntimeDeferredUnproxyableError(bean)) { + return Collections.emptySet(); + } + ProviderType providerType = new ProviderType(bean.getProviderType()); ClassInfo providerClass = getClassByName(bean.getDeployment().getBeanArchiveIndex(), providerType.name()); String baseName = getBaseName(bean, beanClassName); diff --git a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml b/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml index 60e5d372194dc..87e459298438e 100644 --- a/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml +++ b/independent-projects/arc/tcks/cdi-tck-runner/src/test/resources/testng.xml @@ -81,15 +81,6 @@ - - - - - - - - - diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/clientproxy/finalmethod/FinalMethodIllegalTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/clientproxy/finalmethod/FinalMethodIllegalWhenInjectedTest.java similarity index 76% rename from independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/clientproxy/finalmethod/FinalMethodIllegalTest.java rename to independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/clientproxy/finalmethod/FinalMethodIllegalWhenInjectedTest.java index fc7da12739ff8..748735b30dbfe 100644 --- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/clientproxy/finalmethod/FinalMethodIllegalTest.java +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/clientproxy/finalmethod/FinalMethodIllegalWhenInjectedTest.java @@ -5,17 +5,19 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Dependent; import jakarta.enterprise.inject.spi.DeploymentException; +import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.arc.test.ArcTestContainer; -public class FinalMethodIllegalTest { +public class FinalMethodIllegalWhenInjectedTest { @RegisterExtension public ArcTestContainer container = ArcTestContainer.builder() - .beanClasses(Moo.class) + .beanClasses(Moo.class, MooConsumer.class) .strictCompatibility(true) .shouldFail() .build(); @@ -34,4 +36,11 @@ final int getVal() { return -1; } } + + // to trigger deployment-time error (in strict compatible mode) + @Dependent + static class MooConsumer { + @Inject + Moo moo; + } } diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/clientproxy/finalmethod/FinalMethodIllegalWhenNotInjectedTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/clientproxy/finalmethod/FinalMethodIllegalWhenNotInjectedTest.java new file mode 100644 index 0000000000000..d82d2a136e437 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/clientproxy/finalmethod/FinalMethodIllegalWhenNotInjectedTest.java @@ -0,0 +1,35 @@ +package io.quarkus.arc.test.clientproxy.finalmethod; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.UnproxyableResolutionException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.test.ArcTestContainer; + +public class FinalMethodIllegalWhenNotInjectedTest { + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder() + .beanClasses(Moo.class) + .strictCompatibility(true) + .shouldFail() + .build(); + + @Test + public void test() { + assertThrows(UnproxyableResolutionException.class, () -> { + Arc.container().instance(Moo.class).get(); + }); + } + + @ApplicationScoped + static class Moo { + final int getVal() { + return -1; + } + } +} From 22c5798ca8b3d4a653c327db18c657be6f4128a2 Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 19 May 2023 11:34:01 +0200 Subject: [PATCH 4/8] ArC: upgrade Arquillian to 1.7.0.Final --- build-parent/pom.xml | 2 +- independent-projects/arc/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-parent/pom.xml b/build-parent/pom.xml index aecfcfa96f848..03fbb4cb2d221 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -49,7 +49,7 @@ 0.21.0 - 1.7.0.Alpha13 + 1.7.0.Final jdt_apt diff --git a/independent-projects/arc/pom.xml b/independent-projects/arc/pom.xml index fa9dc00ae202f..720a01b64e47c 100644 --- a/independent-projects/arc/pom.xml +++ b/independent-projects/arc/pom.xml @@ -58,7 +58,7 @@ 1.6.4 5.3.1 - 1.7.0.Alpha14 + 1.7.0.Final 2.0.1 4.0.9 4.13.2 From cd25ac994fa1712c8209784d31073dbbb0057643 Mon Sep 17 00:00:00 2001 From: Ales Justin Date: Mon, 29 May 2023 14:10:22 +0200 Subject: [PATCH 5/8] Fix issue #33593 --- .../inprocess/InProcessGrpcServerBuilderProvider.java | 5 +++-- .../java/io/quarkus/grpc/runtime/config/Enabled.java | 9 +++++++++ .../io/quarkus/grpc/runtime/config/InProcess.java | 10 +++++++++- .../main/java/io/quarkus/grpc/runtime/config/Xds.java | 10 +++++++++- .../grpc/xds/XdsGrpcServerBuilderProvider.java | 11 +++-------- 5 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/Enabled.java diff --git a/extensions/grpc/inprocess/src/main/java/io/quarkus/grpc/inprocess/InProcessGrpcServerBuilderProvider.java b/extensions/grpc/inprocess/src/main/java/io/quarkus/grpc/inprocess/InProcessGrpcServerBuilderProvider.java index db7155197d0a8..d47468927d67e 100644 --- a/extensions/grpc/inprocess/src/main/java/io/quarkus/grpc/inprocess/InProcessGrpcServerBuilderProvider.java +++ b/extensions/grpc/inprocess/src/main/java/io/quarkus/grpc/inprocess/InProcessGrpcServerBuilderProvider.java @@ -12,6 +12,7 @@ import io.grpc.ServerServiceDefinition; import io.grpc.inprocess.InProcessChannelBuilder; import io.grpc.inprocess.InProcessServerBuilder; +import io.quarkus.grpc.runtime.config.Enabled; import io.quarkus.grpc.runtime.config.GrpcClientConfiguration; import io.quarkus.grpc.runtime.config.GrpcServerConfiguration; import io.quarkus.grpc.spi.GrpcBuilderProvider; @@ -24,7 +25,7 @@ public class InProcessGrpcServerBuilderProvider implements GrpcBuilderProvider { @Override public boolean providesServer(GrpcServerConfiguration configuration) { - return configuration.inProcess.enabled; + return Enabled.isEnabled(configuration.inProcess); } @Override @@ -69,7 +70,7 @@ public String serverInfo(String host, int port, GrpcServerConfiguration configur @Override public boolean providesChannel(GrpcClientConfiguration configuration) { - return configuration.inProcess.enabled; + return Enabled.isEnabled(configuration.inProcess); } @Override diff --git a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/Enabled.java b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/Enabled.java new file mode 100644 index 0000000000000..76871cb252c08 --- /dev/null +++ b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/Enabled.java @@ -0,0 +1,9 @@ +package io.quarkus.grpc.runtime.config; + +public interface Enabled { + boolean isEnabled(); + + static boolean isEnabled(Enabled enabled) { + return enabled != null && enabled.isEnabled(); + } +} diff --git a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/InProcess.java b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/InProcess.java index b5ba5072d15c9..370bb3917e853 100644 --- a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/InProcess.java +++ b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/InProcess.java @@ -2,13 +2,21 @@ import io.quarkus.runtime.annotations.ConfigGroup; import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.IgnoreProperty; /** * In-process config * * in-process usage */ @ConfigGroup -public class InProcess { +public class InProcess implements Enabled { + + @Override + @IgnoreProperty + public boolean isEnabled() { + return enabled; + } + /** * Explicitly enable use of in-process. */ diff --git a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/Xds.java b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/Xds.java index a5e62f7256a01..5c5bdd6754e03 100644 --- a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/Xds.java +++ b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/config/Xds.java @@ -2,13 +2,21 @@ import io.quarkus.runtime.annotations.ConfigGroup; import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.IgnoreProperty; /** * XDS config * * XDS usage */ @ConfigGroup -public class Xds { +public class Xds implements Enabled { + + @Override + @IgnoreProperty + public boolean isEnabled() { + return enabled; + } + /** * Explicitly enable use of XDS. */ diff --git a/extensions/grpc/xds/src/main/java/io/quarkus/grpc/xds/XdsGrpcServerBuilderProvider.java b/extensions/grpc/xds/src/main/java/io/quarkus/grpc/xds/XdsGrpcServerBuilderProvider.java index 3aa43fcbd918d..69db93834c07b 100644 --- a/extensions/grpc/xds/src/main/java/io/quarkus/grpc/xds/XdsGrpcServerBuilderProvider.java +++ b/extensions/grpc/xds/src/main/java/io/quarkus/grpc/xds/XdsGrpcServerBuilderProvider.java @@ -21,6 +21,7 @@ import io.grpc.xds.XdsServerBuilder; import io.grpc.xds.XdsServerCredentials; import io.quarkus.grpc.runtime.config.ClientXds; +import io.quarkus.grpc.runtime.config.Enabled; import io.quarkus.grpc.runtime.config.GrpcClientConfiguration; import io.quarkus.grpc.runtime.config.GrpcServerConfiguration; import io.quarkus.grpc.runtime.config.Xds; @@ -37,8 +38,7 @@ public class XdsGrpcServerBuilderProvider implements GrpcBuilderProvider { @Override public boolean providesServer(GrpcServerConfiguration configuration) { - Xds xds = configuration.xds; - return xds != null && xds.enabled; + return Enabled.isEnabled(configuration.xds); } @Override @@ -96,12 +96,7 @@ public String serverInfo(String host, int port, GrpcServerConfiguration configur @Override public boolean providesChannel(GrpcClientConfiguration configuration) { - Xds xds = configuration.xds; - if (xds != null) { - return xds.enabled || XDS.equalsIgnoreCase(configuration.nameResolver); - } else { - return false; - } + return Enabled.isEnabled(configuration.xds) || XDS.equalsIgnoreCase(configuration.nameResolver); } @Override From cf78b58da11feb6bb5db608055ab373c92bcc71a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 12:30:05 +0000 Subject: [PATCH 6/8] Bump wildfly-elytron.version from 2.1.0.Final to 2.2.0.Final Bumps `wildfly-elytron.version` from 2.1.0.Final to 2.2.0.Final. Updates `wildfly-elytron` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-ssh-util` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-auth-server` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-password-impl` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-realm` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-realm-token` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-realm-jdbc` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-realm-ldap` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-ssl` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-sasl-plain` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-sasl-digest` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-sasl-external` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-sasl-oauth2` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-sasl-scram` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-x500-cert` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-credential` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-sasl-gs2` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-asn1` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-sasl-gssapi` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-security-manager-action` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-auth` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-base` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-http` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-keystore` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-mechanism-digest` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-mechanism-gssapi` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-mechanism-oauth2` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-mechanism-scram` from 2.1.0.Final to 2.2.0.Final Updates `wildfly-elytron-mechanism` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-permission` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-provider-util` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-sasl` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-util` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-x500-cert-util` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) Updates `wildfly-elytron-x500` from 2.1.0.Final to 2.2.0.Final - [Commits](https://github.com/wildfly-security/wildfly-elytron/compare/2.1.0.Final...2.2.0.Final) --- updated-dependencies: - dependency-name: org.wildfly.security:wildfly-elytron dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-ssh-util dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-auth-server dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-password-impl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-realm dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-realm-token dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-realm-jdbc dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-realm-ldap dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-ssl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-plain dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-digest dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-external dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-oauth2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-scram dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-x500-cert dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-credential dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-gs2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-asn1 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl-gssapi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-security-manager-action dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-auth dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-base dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-http dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-keystore dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-digest dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-gssapi dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-oauth2 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-mechanism-scram dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-mechanism dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-permission dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-provider-util dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-sasl dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-util dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-x500-cert-util dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.wildfly.security:wildfly-elytron-x500 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index d1046dbb1a270..4e5bfe4e4c77b 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -117,7 +117,7 @@ 2.0.0.Final 1.5.4.Final-format-001 1.0.1.Final - 2.1.0.Final + 2.2.0.Final 3.5.0.Final 4.4.2 4.5.14 From 711336d5d836e7be755ed65cf74aeb8e4698f390 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 29 May 2023 16:15:53 +0300 Subject: [PATCH 7/8] Mark test failure in time for callbacks to know about it Fixes: #33647 --- .../quarkus/it/main/QuarkusTestCallbacksTestCase.java | 6 ++++++ .../junit/AbstractQuarkusTestWithContextExtension.java | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java index c61618bdcc2b5..305b2136b90a3 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java @@ -114,6 +114,12 @@ public void testCallbackContextIsFailed() { assertTrue(TestContextCheckerBeforeEachCallback.CONTEXT.getTestStatus().isTestFailed()); } + @Test + @Order(6) + public void testCallbackContextIsNotFailedAgain() { + assertTrue(TestContextCheckerBeforeEachCallback.CONTEXT.getTestStatus().isTestFailed()); + } + @Target({ METHOD }) @Retention(RUNTIME) public @interface TestAnnotation { diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractQuarkusTestWithContextExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractQuarkusTestWithContextExtension.java index 037e4946b58e2..68b1b601ed90c 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractQuarkusTestWithContextExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractQuarkusTestWithContextExtension.java @@ -4,13 +4,21 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler; +import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; import org.junit.jupiter.api.extension.TestWatcher; public abstract class AbstractQuarkusTestWithContextExtension extends AbstractTestWithCallbacksExtension - implements LifecycleMethodExecutionExceptionHandler, TestWatcher { + implements TestExecutionExceptionHandler, LifecycleMethodExecutionExceptionHandler, TestWatcher { public static final String IO_QUARKUS_TESTING_TYPE = "io.quarkus.testing.type"; + @Override + public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { + markTestAsFailed(context, throwable); + + throw throwable; + } + @Override public void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable) throws Throwable { markTestAsFailed(context, throwable); From 72ff30bd21c9589078dd6b102640a1493d0198fc Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 29 May 2023 16:39:04 +0300 Subject: [PATCH 8/8] Remove the last traces of Fernflower It seems like #30909 didn't remove everything --- .../quarkus/deployment/pkg/PackageConfig.java | 2 +- .../pkg/steps/JarResultBuildStep.java | 70 ------------------- 2 files changed, 1 insertion(+), 71 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java index 15a330f329630..9a11ba17a57a9 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java @@ -312,7 +312,7 @@ public static class QuiltFlowerConfig { public String version; /** - * The directory into which to save the fernflower tool if it doesn't exist + * The directory into which to save the Quiltflower tool if it doesn't exist */ @ConfigItem(defaultValue = "${user.home}/.quarkus") public String jarDirectory; diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java index 3936e20529b2e..841dbdde49a4f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java @@ -1571,76 +1571,6 @@ public Context(String versionStr, Path jarLocation, Path decompiledOutputDir) { } - class FernflowerDecompiler implements Decompiler { - - private Context context; - private Path decompilerJar; - - @Override - public void init(Context context) { - this.context = context; - this.decompilerJar = context.jarLocation.resolve(String.format("fernflower-%s.jar", context.versionStr)); - } - - @Override - public boolean downloadIfNecessary() { - if (Files.exists(decompilerJar)) { - return true; - } - String downloadURL = String.format("https://jitpack.io/com/github/fesh0r/fernflower/%s/fernflower-%s.jar", - context.versionStr, context.versionStr); - try (BufferedInputStream in = new BufferedInputStream(new URL(downloadURL).openStream()); - FileOutputStream fileOutputStream = new FileOutputStream(decompilerJar.toFile())) { - byte[] dataBuffer = new byte[1024]; - int bytesRead; - while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { - fileOutputStream.write(dataBuffer, 0, bytesRead); - } - return true; - } catch (IOException e) { - log.error("Unable to download Fernflower from " + downloadURL, e); - return false; - } - } - - @Override - public boolean decompile(Path jarToDecompile) { - int exitCode; - try { - ProcessBuilder processBuilder = new ProcessBuilder( - Arrays.asList("java", "-jar", decompilerJar.toAbsolutePath().toString(), - jarToDecompile.toAbsolutePath().toString(), - context.decompiledOutputDir.toAbsolutePath().toString())); - if (log.isDebugEnabled()) { - processBuilder.inheritIO(); - } else { - processBuilder.redirectError(ProcessBuilder.Redirect.DISCARD.file()) - .redirectOutput(ProcessBuilder.Redirect.DISCARD.file()); - } - exitCode = processBuilder.start().waitFor(); - } catch (Exception e) { - log.error("Failed to launch decompiler.", e); - return false; - } - - if (exitCode != 0) { - log.errorf("Fernflower decompiler exited with error code: %d.", exitCode); - return false; - } - - String jarFileName = jarToDecompile.getFileName().toString(); - Path decompiledJar = context.decompiledOutputDir.resolve(jarFileName); - try { - ZipUtils.unzip(decompiledJar, context.decompiledOutputDir.resolve(jarFileName.replace(DOT_JAR, ""))); - Files.deleteIfExists(decompiledJar); - } catch (IOException ignored) { - // it doesn't really matter if we can't unzip the jar as we do it merely for user convenience - } - - return true; - } - } - class QuiltflowerDecompiler implements Decompiler { private Context context;