istratenko
istratenko

Reputation: 11

Stackoverflow during Beans injection in Quarkus

I am trying to inject two calsses ClassA and ClassB at quarkus's startup event.

public void init(@Observes StartupEvent startupEvent, ClassA a, ClassB b) throws Exception {
      
    }

Classes:

public class ClassA {
    public ClassA() {

    }
}

public class ClassB {
    private final ClassA dependentClass;
    public ClassB(ClassA dependentClass) {
        this.dependentClass = dependentClass;
    }
}

Producers for these two classes are presented below:

@ApplicationScoped
public class Configuration {

    @Produces
    @Singleton
    public ClassA produceA() {
        return new ClassA();
    }

    @Inject
    @Produces
    @Singleton
    public ClassB produceB(ClassA dependentClass) {
        return new ClassB(dependentClass);
    }
}

As a result, I am getting stackoverflow error:

Caused by: java.lang.StackOverflowError
        at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.create(Unknown Source)
        at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.get(Unknown Source)
        at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.get(Unknown Source)
        at org.acme.getting.started.Configuration_Subclass.<init>(Unknown Source)
        at org.acme.getting.started.Configuration_Bean.create(Unknown Source)
        at org.acme.getting.started.Configuration_Bean.create(Unknown Source)
        at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:96)
        at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:29)
        at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:26)
        at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26)
        at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
        at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:26)
        at io.quarkus.arc.impl.ClientProxies.getApplicationScopedDelegate(ClientProxies.java:18)
        at org.acme.getting.started.Configuration_ClientProxy.arc$delegate(Unknown Source)
        at org.acme.getting.started.Configuration_ClientProxy.arc_contextualInstance(Unknown Source)
        at org.acme.getting.started.Configuration_ProducerMethod_produceA_612e78438d1b1b670691525f250ee747f2210b1e_Bean.create(Unknown Source)
        at org.acme.getting.started.Configuration_ProducerMethod_produceA_612e78438d1b1b670691525f250ee747f2210b1e_Bean.create(Unknown Source)
        at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:96)
        at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:29)
        at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:26)
        at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26)
        at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
        at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:26)
        at org.acme.getting.started.Configuration_ProducerMethod_produceA_612e78438d1b1b670691525f250ee747f2210b1e_Bean.get(Unknown Source)
        at org.acme.getting.started.Configuration_ProducerMethod_produceA_612e78438d1b1b670691525f250ee747f2210b1e_Bean.get(Unknown Source)
        at org.acme.getting.started.Configuration_Bean.create(Unknown Source)
        at org.acme.getting.started.Configuration_Bean.create(Unknown Source)
        at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:96)
        at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:29)
        at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:26)
        at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26)
        at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
        at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:26)
        at io.quarkus.arc.impl.ClientProxies.getApplicationScopedDelegate(ClientProxies.java:18)
        at org.acme.getting.started.Configuration_ClientProxy.arc$delegate(Unknown Source)
        at org.acme.getting.started.Configuration_ClientProxy.arc_contextualInstance(Unknown Source)
        at org.acme.getting.started.Configuration_ProducerMethod_produceA_612e78438d1b1b670691525f250ee747f2210b1e_Bean.create(Unknown Source)
        at org.acme.getting.started.Configuration_ProducerMethod_produceA_612e78438d1b1b670691525f250ee747f2210b1e_Bean.create(Unknown Source)

Please, advise me does quarkus support such producers? If is doesn't, what is the solution for produce two dependent classes in one Configuration class? My purpose is to place all Singleton producers in one place, but Singleton scope is not applicable for main config file due to https://github.com/quarkusio/quarkus/issues/3241

Upvotes: 1

Views: 632

Answers (1)

Ladicek
Ladicek

Reputation: 6597

I'm not like entirely sure, but I believe you need to remove the @Inject annotation from the produceB method. Parameters of producer methods are automatically injection points, so from that perspective, the @Inject annotation is not necessary.

What's worse, the @Inject annotation actually does something else than designate the parameters as injection points. It makes the method an initializer method (this is sometimes also known as setter injection). This means that to fully construct an instance of Configuration, the CDI container has to call the produceB method and pass it an instance of ClassA (parameters of initializer methods are also injection points). But to be able to obtain an instance of ClassA, the container needs a fully constructed instance of Configuration. That's a cycle that, I believe, leads to the stack overflow you observe.

Technically, the specification forbids an initializer method to be annotated @Produces, so this should lead to an error during build. I guess ArC (the CDI implementation in Quarkus) doesn't check this properly, which is a bug.

Upvotes: 2

Related Questions