Skip to content

Commit

Permalink
ArC: register synthetic injection points in a separate phase
Browse files Browse the repository at this point in the history
- this fixes the problem where an synthetic injection point from a
SyntheticBeanBuiltItem was not considered when detecting unused beans
  • Loading branch information
mkouba committed Oct 16, 2024
1 parent e6dd595 commit 03ca632
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -460,10 +460,12 @@ public ObserverRegistrationPhaseBuildItem registerSyntheticObservers(BeanRegistr
configurator.getValues().forEach(BeanConfigurator::done);
}

BeanProcessor beanProcessor = beanRegistrationPhase.getBeanProcessor();
beanProcessor.registerSyntheticInjectionPoints(beanRegistrationPhase.getContext());

// Initialize the type -> bean map
beanRegistrationPhase.getBeanProcessor().getBeanDeployment().initBeanByTypeMap();
beanProcessor.getBeanDeployment().initBeanByTypeMap();

BeanProcessor beanProcessor = beanRegistrationPhase.getBeanProcessor();
ObserverRegistrar.RegistrationContext registrationContext = beanProcessor.registerSyntheticObservers();

return new ObserverRegistrationPhaseBuildItem(registrationContext, beanProcessor);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package io.quarkus.arc.test.unused;

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.function.Consumer;

import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Vetoed;

import org.jboss.jandex.ClassType;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer;
import io.quarkus.arc.BeanCreator;
import io.quarkus.arc.InstanceHandle;
import io.quarkus.arc.SyntheticCreationalContext;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.processor.BuiltinScope;
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildContext;
import io.quarkus.builder.BuildStep;
import io.quarkus.test.QuarkusUnitTest;

public class UnremovableSyntheticInjectionPointTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(UnremovableSyntheticInjectionPointTest.class, Alpha.class, Gama.class, GamaCreator.class))
.addBuildChainCustomizer(buildCustomizer());

static Consumer<BuildChainBuilder> buildCustomizer() {
return new Consumer<BuildChainBuilder>() {

@Override
public void accept(BuildChainBuilder builder) {
builder.addBuildStep(new BuildStep() {

@Override
public void execute(BuildContext context) {
context.produce(SyntheticBeanBuildItem.configure(Gama.class)
.scope(BuiltinScope.SINGLETON.getInfo())
.unremovable()
.addInjectionPoint(ClassType.create(Alpha.class))
.creator(GamaCreator.class)
.done());
}
}).produces(SyntheticBeanBuildItem.class).build();
}
};
}

@Test
public void testBeans() {
ArcContainer container = Arc.container();
InstanceHandle<Alpha> alpha = container.instance(Alpha.class);
assertTrue(alpha.isAvailable());
assertTrue(alpha.get().ping());
InstanceHandle<Gama> gama = container.instance(Gama.class);
assertTrue(gama.isAvailable());
assertTrue(gama.get().ping());
}

// unused bean injected into a synthetic injection point
@ApplicationScoped
public static class Alpha {

volatile boolean flag;

@PostConstruct
void init() {
flag = true;
}

public boolean ping() {
return flag;
}

}

@Vetoed
public static class Gama {

private final Alpha alpha;

private Gama(Alpha alpha) {
this.alpha = alpha;
}

public boolean ping() {
return alpha.ping();
}

}

public static class GamaCreator implements BeanCreator<Gama> {

@Override
public Gama create(SyntheticCreationalContext<Gama> context) {
return new Gama(context.getInjectedReference(Alpha.class));
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1503,10 +1503,17 @@ private RegistrationContext registerSyntheticBeans(List<BeanRegistrar> beanRegis
buildCompatibleExtensions.runSynthesis(beanArchiveComputingIndex);
buildCompatibleExtensions.registerSyntheticBeans(context, applicationClassPredicate);
}
this.injectionPoints.addAll(context.syntheticInjectionPoints);
return context;
}

void registerSyntheticInjectionPoints(RegistrationContext context) {
if (context instanceof BeanRegistrationContextImpl beanRegistrationContext) {
this.injectionPoints.addAll(beanRegistrationContext.syntheticInjectionPoints);
} else {
throw new IllegalArgumentException("Invalid registration context found:" + context.getClass());
}
}

io.quarkus.arc.processor.ObserverRegistrar.RegistrationContext registerSyntheticObservers(
List<ObserverRegistrar> observerRegistrars) {
ObserverRegistrationContextImpl context = new ObserverRegistrationContextImpl(buildContext, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

import io.quarkus.arc.InjectableBean;
import io.quarkus.arc.processor.BeanDeploymentValidator.ValidationContext;
import io.quarkus.arc.processor.BeanRegistrar.RegistrationContext;
import io.quarkus.arc.processor.BuildExtension.BuildContext;
import io.quarkus.arc.processor.BuildExtension.Key;
import io.quarkus.arc.processor.CustomAlterableContexts.CustomAlterableContextInfo;
Expand All @@ -51,6 +52,7 @@
* <li>{@link #registerCustomContexts()}</li>
* <li>{@link #registerScopes()}</li>
* <li>{@link #registerBeans()}</li>
* <li>{@link #registerSyntheticInjectionPoints(io.quarkus.arc.processor.BeanRegistrar.RegistrationContext)}</li>
* <li>{@link BeanDeployment#initBeanByTypeMap()}</li>
* <li>{@link #registerSyntheticObservers()}</li>
* <li>{@link #initialize(Consumer, List)}</li>
Expand Down Expand Up @@ -153,6 +155,15 @@ public BeanRegistrar.RegistrationContext registerBeans() {
return beanDeployment.registerBeans(beanRegistrars);
}

/**
* Register synthetic injection points from all synthetic beans.
*
* @param context
*/
public void registerSyntheticInjectionPoints(BeanRegistrar.RegistrationContext context) {
beanDeployment.registerSyntheticInjectionPoints(context);
}

public ObserverRegistrar.RegistrationContext registerSyntheticObservers() {
return beanDeployment.registerSyntheticObservers(observerRegistrars);
}
Expand Down Expand Up @@ -560,7 +571,8 @@ public void accept(BytecodeTransformer transformer) {
};
registerCustomContexts();
registerScopes();
registerBeans();
RegistrationContext registrationContext = registerBeans();
registerSyntheticInjectionPoints(registrationContext);
beanDeployment.initBeanByTypeMap();
registerSyntheticObservers();
initialize(unsupportedBytecodeTransformer, Collections.emptyList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class RemoveUnusedBeansTest extends RemoveUnusedComponentsTest {
.beanClasses(HasObserver.class, Foo.class, FooAlternative.class, HasName.class, UnusedProducers.class,
InjectedViaInstance.class, InjectedViaInstanceWithWildcard.class, InjectedViaProvider.class, Excluded.class,
UsedProducers.class, UnusedProducerButInjected.class, UsedViaInstanceWithUnusedProducer.class,
UsesBeanViaInstance.class, UsedViaAllList.class)
UsesBeanViaInstance.class, UsedViaAllList.class, UnusedBean.class, OnlyInjectedInUnusedBean.class)
.removeUnusedBeans(true)
.addRemovalExclusion(b -> b.getBeanClass().toString().equals(Excluded.class.getName()))
.build();
Expand Down

0 comments on commit 03ca632

Please sign in to comment.