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 952895860541d..b58e17cbcdfb4 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 @@ -27,6 +27,7 @@ import javax.enterprise.context.spi.Contextual; import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.CreationException; import javax.enterprise.inject.IllegalProductException; import javax.enterprise.inject.literal.InjectLiteral; import javax.enterprise.inject.spi.InterceptionType; @@ -63,6 +64,8 @@ import io.quarkus.gizmo.FieldCreator; import io.quarkus.gizmo.FieldDescriptor; import io.quarkus.gizmo.FunctionCreator; +import io.quarkus.gizmo.Gizmo; +import io.quarkus.gizmo.Gizmo.StringBuilderGenerator; import io.quarkus.gizmo.MethodCreator; import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; @@ -946,7 +949,25 @@ protected void implementCreate(ClassOutput classOutput, ClassCreator beanCreator injectionPointToProviderSupplierField, reflectionRegistration, targetPackage, isApplicationClass, create); } else if (bean.isSynthetic()) { - bean.getCreatorConsumer().accept(create); + if (bean.getScope().isNormal()) { + // Normal scoped synthetic beans should never return null + MethodCreator createSynthetic = beanCreator + .getMethodCreator("createSynthetic", providerType.descriptorName(), CreationalContext.class) + .setModifiers(ACC_PRIVATE); + bean.getCreatorConsumer().accept(createSynthetic); + ResultHandle ret = create.invokeVirtualMethod(createSynthetic.getMethodDescriptor(), create.getThis(), + create.getMethodParam(0)); + BytecodeCreator nullBeanInstance = create.ifNull(ret).trueBranch(); + StringBuilderGenerator message = Gizmo.newStringBuilder(nullBeanInstance); + message.append("Null contextual instance was produced by a normal scoped synthetic bean: "); + message.append(Gizmo.toString(nullBeanInstance, nullBeanInstance.getThis())); + ResultHandle e = nullBeanInstance.newInstance( + MethodDescriptor.ofConstructor(CreationException.class, String.class), message.callToString()); + nullBeanInstance.throwException(e); + create.returnValue(ret); + } else { + bean.getCreatorConsumer().accept(create); + } } // Bridge method needed diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/buildextension/beans/NormalScopedSyntheticBeanProducedNullTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/buildextension/beans/NormalScopedSyntheticBeanProducedNullTest.java new file mode 100644 index 0000000000000..7814f10472cc3 --- /dev/null +++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/buildextension/beans/NormalScopedSyntheticBeanProducedNullTest.java @@ -0,0 +1,55 @@ +package io.quarkus.arc.test.buildextension.beans; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.CreationException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.arc.Arc; +import io.quarkus.arc.BeanCreator; +import io.quarkus.arc.processor.BeanRegistrar; +import io.quarkus.arc.test.ArcTestContainer; + +public class NormalScopedSyntheticBeanProducedNullTest { + + public static volatile boolean beanDestroyerInvoked = false; + + @RegisterExtension + public ArcTestContainer container = ArcTestContainer.builder().beanRegistrars(new TestRegistrar()).build(); + + @Test + public void testCreationException() { + CreationException e = assertThrows(CreationException.class, () -> { + Arc.container().instance(CharSequence.class).get().length(); + }); + assertTrue(e.getMessage().contains("Null contextual instance was produced by a normal scoped synthetic bean"), + e.getMessage()); + } + + static class TestRegistrar implements BeanRegistrar { + + @Override + public void register(RegistrationContext context) { + context.configure(CharSequence.class).types(CharSequence.class).unremovable().scope(ApplicationScoped.class) + .creator(CharSequenceCreator.class).done(); + } + + } + + public static class CharSequenceCreator implements BeanCreator { + + @Override + public CharSequence create(CreationalContext creationalContext, Map params) { + return null; + } + + } + +}