Skip to content

Commit

Permalink
Merge pull request #44916 from manovotn/arcSynthBeanTypes
Browse files Browse the repository at this point in the history
Arc - synthetic bean improvements
  • Loading branch information
manovotn authored Dec 5, 2024
2 parents 83f4243 + e0d0b1a commit 50e9058
Show file tree
Hide file tree
Showing 9 changed files with 558 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
public final class SyntheticBeanBuildItem extends MultiBuildItem {

/**
* Returns a configurator object allowing for further customization of the synthetic bean.
* <p>
* The implementation class is automatically registered as a resulting bean type.
*
* @param implClazz
* @return a new configurator instance
Expand All @@ -42,6 +45,9 @@ public static ExtendedBeanConfigurator configure(Class<?> implClazz) {
}

/**
* Returns a configurator object allowing for further customization of the synthetic bean.
* <p>
* The implementation class is automatically registered as a resulting bean type.
*
* @param implClazz
* @return a new configurator instance
Expand All @@ -51,6 +57,32 @@ public static ExtendedBeanConfigurator configure(DotName implClazz) {
return new ExtendedBeanConfigurator(implClazz).addType(implClazz);
}

/**
* Returns a configurator object allowing for further customization of the synthetic bean.
* <p>
* Unlike {@link #configure(Class)}, the implementation class is <b>not</b> registered as a resulting bean type.
*
* @param implClazz
* @return a new configurator instance
* @see ExtendedBeanConfigurator#done()
*/
public static ExtendedBeanConfigurator create(Class<?> implClazz) {
return create(DotName.createSimple(implClazz.getName()));
}

/**
* Returns a configurator object allowing for further customization of the synthetic bean.
* <p>
* Unlike {@link #configure(DotName)}, the implementation class is <b>not</b> registered as a resulting bean type.
*
* @param implClazz
* @return a new configurator instance
* @see ExtendedBeanConfigurator#done()
*/
public static ExtendedBeanConfigurator create(DotName implClazz) {
return new ExtendedBeanConfigurator(implClazz);
}

private final ExtendedBeanConfigurator configurator;

SyntheticBeanBuildItem(ExtendedBeanConfigurator configurator) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package io.quarkus.arc.test.synthetic.create;

import java.util.function.Consumer;

import jakarta.enterprise.inject.Vetoed;

import org.jboss.jandex.DotName;
import org.junit.jupiter.api.Assertions;
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.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;

/**
* Tests that {@link SyntheticBeanBuildItem#create(DotName)} does not add automatically register the param type as bean type
*/
public class SyntheticBeanBuildItemCreateTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(SyntheticBeanBuildItemCreateTest.class, FooCreator.class, FooInterface.class, Foo.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.create(Foo.class)
.addType(FooInterface.class)
.scope(BuiltinScope.SINGLETON.getInfo())
.unremovable()
.creator(FooCreator.class)
.done());
}
}).produces(SyntheticBeanBuildItem.class).build();
}
};
}

@Test
public void testBeanTypes() {
ArcContainer container = Arc.container();
Assertions.assertFalse(container.select(Foo.class).isResolvable());
Assertions.assertTrue(container.select(FooInterface.class).isResolvable());
}

@Vetoed
public static class Foo implements FooInterface {
}

interface FooInterface {
}

public static class FooCreator implements BeanCreator<Foo> {

@Override
public Foo create(SyntheticCreationalContext<Foo> context) {
return new Foo();
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package io.quarkus.arc.test.synthetic.removeTypes;

import java.util.function.Consumer;

import org.jboss.jandex.DotName;
import org.jboss.jandex.Type;
import org.junit.jupiter.api.Assertions;
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.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 SyntheticBeanBuildItemRemoveTypesTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(SyntheticBeanBuildItemRemoveTypesTest.class, FooCreator.class, FooInterface.class, Foo.class,
FooSubclass.class, Charlie.class, CharlieSubclass.class, CharlieInterface.class, BarInterface.class,
BazInterface.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.create(FooSubclass.class)
.addTypeClosure(FooSubclass.class)
.removeTypes(DotName.createSimple(CharlieSubclass.class))
.removeTypes(DotName.createSimple(FooSubclass.class))
.removeTypes(Type.create(BazInterface.class))
.scope(BuiltinScope.SINGLETON.getInfo())
.unremovable()
.creator(FooCreator.class)
.done());
}
}).produces(SyntheticBeanBuildItem.class).build();
}
};
}

@Test
public void testRemovingBeanTypes() {
ArcContainer container = Arc.container();
Assertions.assertTrue(container.select(Foo.class).isResolvable());
Assertions.assertTrue(container.select(FooInterface.class).isResolvable());
Assertions.assertTrue(container.select(BarInterface.class).isResolvable());
Assertions.assertTrue(container.select(Charlie.class).isResolvable());
Assertions.assertTrue(container.select(CharlieInterface.class).isResolvable());

// CharlieSubclass, FooSubclass and BazInterface should not be registered as bean types
Assertions.assertFalse(container.select(CharlieSubclass.class).isResolvable());
Assertions.assertFalse(container.select(FooSubclass.class).isResolvable());
Assertions.assertFalse(container.select(BazInterface.class).isResolvable());
}

public static class FooSubclass extends Foo implements FooInterface {
}

public static class Foo extends Charlie implements BazInterface {
}

public static class CharlieSubclass extends Charlie {
}

public static class Charlie implements CharlieInterface {
}

interface CharlieInterface {
}

interface FooInterface extends BarInterface {
}

interface BarInterface {
}

interface BazInterface {
}

public static class FooCreator implements BeanCreator<FooSubclass> {

@Override
public FooSubclass create(SyntheticCreationalContext<FooSubclass> context) {
return new FooSubclass();
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package io.quarkus.arc.test.synthetic.typeClosure;

import java.util.function.Consumer;

import jakarta.enterprise.util.TypeLiteral;

import org.jboss.jandex.ParameterizedType;
import org.junit.jupiter.api.Assertions;
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.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 SyntheticBeanBuildItemAddTypeClosureGenericsTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(SyntheticBeanBuildItemAddTypeClosureGenericsTest.class, FooCreator.class, FooInterface.class,
Foo.class,
FooSubclass.class, Charlie.class, CharlieSubclass.class, CharlieInterface.class, BarInterface.class,
BazInterface.class, Alpha.class, Beta.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.create(FooSubclass.class)
.addTypeClosure(ParameterizedType.builder(FooSubclass.class).addArgument(Beta.class).build())
.scope(BuiltinScope.SINGLETON.getInfo())
.unremovable()
.creator(FooCreator.class)
.done());
}
}).produces(SyntheticBeanBuildItem.class).build();
}
};
}

@Test
public void testBeanTypesDiscovered() {
ArcContainer container = Arc.container();

// Foo/Bar/Baz interfaces should work normally, no generics there
Assertions.assertTrue(container.select(FooInterface.class).isResolvable());
Assertions.assertTrue(container.select(BarInterface.class).isResolvable());
Assertions.assertTrue(container.select(BazInterface.class).isResolvable());

// FooSubclass is resolvable only as correct parameterized type
Assertions.assertTrue(container.select(new TypeLiteral<FooSubclass<Beta>>() {
}).isResolvable());
Assertions.assertFalse(container.select(new TypeLiteral<FooSubclass<Charlie>>() {
}).isResolvable());
Assertions.assertFalse(container.select(FooSubclass.class).isResolvable());

// Foo type should work only parameterized
Assertions.assertTrue(container.select(new TypeLiteral<Foo<Alpha, Beta>>() {
}).isResolvable());
Assertions.assertFalse(container.select(Foo.class).isResolvable());

// Foo extends Charlie raw type
// we should be able to perform resolution for raw type but not for a parameterized type
Assertions.assertTrue(container.select(Charlie.class).isResolvable());
Assertions.assertTrue(container.select(CharlieInterface.class).isResolvable());
Assertions.assertFalse(container.select(new TypeLiteral<Charlie<String>>() {
}).isResolvable());
Assertions.assertFalse(container.select(new TypeLiteral<CharlieInterface<String>>() {
}).isResolvable());

// CharlieSubclass should not be discovered as bean type
Assertions.assertFalse(container.select(CharlieSubclass.class).isResolvable());
}

public static class Alpha {

}

public static class Beta {

}

public static class FooSubclass<T> extends Foo<Alpha, Beta> implements FooInterface {
}

public static class Foo<A, B> extends Charlie implements BazInterface {
}

public static class CharlieSubclass<T> extends Charlie<String> {
}

public static class Charlie<T> implements CharlieInterface<T> {
}

interface CharlieInterface<T> {
}

interface FooInterface extends BarInterface {
}

interface BarInterface {
}

interface BazInterface {
}

public static class FooCreator implements BeanCreator<FooSubclass<Beta>> {

@Override
public FooSubclass<Beta> create(SyntheticCreationalContext<FooSubclass<Beta>> context) {
return new FooSubclass<Beta>();
}

}
}
Loading

0 comments on commit 50e9058

Please sign in to comment.