Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArC - introduce BeanStream #5830

Merged
merged 1 commit into from
Nov 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions docs/src/main/asciidoc/cdi-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -379,8 +379,8 @@ NOTE: A bean registration that is a result of an `AdditionalBeanBuildItem` is re
=== Synthetic Beans

Sometimes it is very useful to register a synthetic bean, i.e. a bean that doesn't need to have a corresponding java class.
In CDI this could be achieved using `AfterBeanDiscovery.addBean()` methods.
In Quarkus we produce a `BeanRegistrarBuildItem` and leverage the `io.quarkus.arc.processor.BeanConfigurator` API to build a synthetic bean definition.
In CDI, this could be achieved using `AfterBeanDiscovery.addBean()` methods.
In Quarkus, we produce a `BeanRegistrarBuildItem` and leverage the `io.quarkus.arc.processor.BeanConfigurator` API to build a synthetic bean definition.

[source,java]
----
Expand All @@ -398,6 +398,8 @@ BeanRegistrarBuildItem syntheticBean() {

NOTE: The output of a `BeanConfigurator` is recorded as bytecode. Therefore there are some limitations in how a synthetic bean instance is created. See also `BeanConfigurator.creator()` methods.

TIP: You can easily filter all class-based beans via the convenient `BeanStream` returned from the `RegistrationContext.beans()` method.

If an extension needs to produce other build items during the "bean registration" phase it should use the `BeanRegistrationPhaseBuildItem` instead.
The reason is that injected objects are only valid during a `@BuildStep` method invocation.

Expand Down Expand Up @@ -511,7 +513,7 @@ BeanDeploymentValidatorBuildItem beanDeploymentValidator() {
}
----

NOTE: See also `io.quarkus.arc.processor.BuildExtension.Key` to discover the available metadata.
TIP: You can easily filter all registered beans via the convenient `BeanStream` returned from the `ValidationContext.beans()` method.

If an extension needs to produce other build items during the "validation" phase it should use the `ValidationPhaseBuildItem` instead.
The reason is that injected objects are only valid during a `@BuildStep` method invocation.
Expand Down Expand Up @@ -574,13 +576,15 @@ The built-in keys located in `io.quarkus.arc.processor.BuildExtension.Key` are:
* `ANNOTATION_STORE`
** Contains an `AnnotationStore` that keeps information about all `AnnotationTarget` annotations after application of annotation transformers
* `INJECTION_POINTS`
** `List<InjectionPointInfo>` containing all injection points
** `Collection<InjectionPointInfo>` containing all injection points
* `BEANS`
** `List<BeanInfo>` containing all beans
** `Collection<BeanInfo>` containing all beans
* `REMOVED_BEANS`
** `Collection<BeanInfo>` containing all the removed beans; see <<remove_unused_beans>> for more information
* `OBSERVERS`
** `List<ObserverInfo>` containing all observers
** `Collection<ObserverInfo>` containing all observers
* `SCOPES`
** `List<ScopeInfo>` containing all scopes, including custom ones
** `Collection<ScopeInfo>` containing all scopes, including custom ones
* `QUALIFIERS`
** `Map<DotName, ClassInfo>` containing all qualifiers
* `INTERCEPTOR_BINDINGS`
Expand All @@ -600,7 +604,7 @@ Here is a summary of which extensions can access which metadata:
* `InjectionPointsTransformer`
** Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES`
* `BeanRegistrar`
** Has access to all build metadata
** Has access to `ANNOTATION_STORE`, `QUALIFIERS`, `INTERCEPTOR_BINDINGS`, `STEREOTYPES`, `BEANS`
* `BeanDeploymentValidator`
** Has access to all build metadata

Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,10 @@ void collectScheduledMethods(
AnnotationStore annotationStore = validationPhase.getContext().get(BuildExtension.Key.ANNOTATION_STORE);

// We need to collect all business methods annotated with @Scheduled first
for (BeanInfo bean : validationPhase.getContext().get(BuildExtension.Key.BEANS)) {
if (bean.isClassBean()) {
collectScheduledMethods(config, beanArchives.getIndex(), annotationStore, bean,
bean.getTarget().get().asClass(),
scheduledBusinessMethods, validationPhase.getContext());
}
for (BeanInfo bean : validationPhase.getContext().beans().classBeans()) {
collectScheduledMethods(config, beanArchives.getIndex(), annotationStore, bean,
bean.getTarget().get().asClass(),
scheduledBusinessMethods, validationPhase.getContext());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import io.quarkus.arc.deployment.ValidationPhaseBuildItem;
import io.quarkus.arc.processor.AnnotationsTransformer;
import io.quarkus.arc.processor.BeanInfo;
import io.quarkus.arc.processor.BuildExtension;
import io.quarkus.arc.processor.BuiltinScope;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.QuarkusConfig;
Expand Down Expand Up @@ -192,10 +191,8 @@ public void transform(TransformationContext ctx) {
void validateFaultToleranceAnnotations(
ValidationPhaseBuildItem validationPhase, SmallryeFaultToleranceRecorder recorder) {
List<String> beanNames = new ArrayList<>();
for (BeanInfo bean : validationPhase.getContext().get(BuildExtension.Key.BEANS)) {
if (bean.isClassBean()) {
beanNames.add(bean.getBeanClass().toString());
}
for (BeanInfo bean : validationPhase.getContext().beans().classBeans()) {
beanNames.add(bean.getBeanClass().toString());
}
recorder.validate(beanNames);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,39 +338,37 @@ void registerMetricsFromProducers(
ValidationPhaseBuildItem validationPhase,
BeanArchiveIndexBuildItem beanArchiveIndex) {
IndexView index = beanArchiveIndex.getIndex();
for (io.quarkus.arc.processor.BeanInfo bean : validationPhase.getContext().get(BuildExtension.Key.BEANS)) {
if (bean.isProducerField() || bean.isProducerMethod()) {
MetricType metricType = getMetricType(bean.getImplClazz());
if (metricType != null) {
AnnotationTarget target = bean.getTarget().get();
AnnotationInstance metricAnnotation = null;
String memberName = null;
if (bean.isProducerField()) {
FieldInfo field = target.asField();
metricAnnotation = field.annotation(METRIC);
memberName = field.name();
}
if (bean.isProducerMethod()) {
MethodInfo method = target.asMethod();
metricAnnotation = method.annotation(METRIC);
memberName = method.name();
}
if (metricAnnotation != null) {
String nameValue = metricAnnotation.valueWithDefault(index, "name").asString();
boolean absolute = metricAnnotation.valueWithDefault(index, "absolute").asBoolean();
String metricSimpleName = !nameValue.isEmpty() ? nameValue : memberName;
String declaringClassName = bean.getDeclaringBean().getImplClazz().name().toString();
String metricsFinalName = absolute ? metricSimpleName
: MetricRegistry.name(declaringClassName, metricSimpleName);
recorder.registerMetricFromProducer(
bean.getIdentifier(),
metricType,
metricsFinalName,
metricAnnotation.valueWithDefault(index, "tags").asStringArray(),
metricAnnotation.valueWithDefault(index, "description").asString(),
metricAnnotation.valueWithDefault(index, "displayName").asString(),
metricAnnotation.valueWithDefault(index, "unit").asString());
}
for (io.quarkus.arc.processor.BeanInfo bean : validationPhase.getContext().beans().producers()) {
MetricType metricType = getMetricType(bean.getImplClazz());
if (metricType != null) {
AnnotationTarget target = bean.getTarget().get();
AnnotationInstance metricAnnotation = null;
String memberName = null;
if (bean.isProducerField()) {
FieldInfo field = target.asField();
metricAnnotation = field.annotation(METRIC);
memberName = field.name();
}
if (bean.isProducerMethod()) {
MethodInfo method = target.asMethod();
metricAnnotation = method.annotation(METRIC);
memberName = method.name();
}
if (metricAnnotation != null) {
String nameValue = metricAnnotation.valueWithDefault(index, "name").asString();
boolean absolute = metricAnnotation.valueWithDefault(index, "absolute").asBoolean();
String metricSimpleName = !nameValue.isEmpty() ? nameValue : memberName;
String declaringClassName = bean.getDeclaringBean().getImplClazz().name().toString();
String metricsFinalName = absolute ? metricSimpleName
: MetricRegistry.name(declaringClassName, metricSimpleName);
recorder.registerMetricFromProducer(
bean.getIdentifier(),
metricType,
metricsFinalName,
metricAnnotation.valueWithDefault(index, "tags").asStringArray(),
metricAnnotation.valueWithDefault(index, "description").asString(),
metricAnnotation.valueWithDefault(index, "displayName").asString(),
metricAnnotation.valueWithDefault(index, "unit").asString());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,27 +118,25 @@ void validateBeanDeployment(
AnnotationStore annotationStore = validationPhase.getContext().get(BuildExtension.Key.ANNOTATION_STORE);

// We need to collect all business methods annotated with @Incoming/@Outgoing first
for (BeanInfo bean : validationPhase.getContext().get(BuildExtension.Key.BEANS)) {
if (bean.isClassBean()) {
// TODO: add support for inherited business methods
for (MethodInfo method : bean.getTarget().get().asClass().methods()) {
AnnotationInstance incoming = annotationStore.getAnnotation(method,
io.quarkus.smallrye.reactivemessaging.deployment.DotNames.INCOMING);
AnnotationInstance outgoing = annotationStore.getAnnotation(method,
io.quarkus.smallrye.reactivemessaging.deployment.DotNames.OUTGOING);
if (incoming != null || outgoing != null) {
if (incoming != null && incoming.value().asString().isEmpty()) {
validationPhase.getContext().addDeploymentProblem(
new DeploymentException("Empty @Incoming annotation on method " + method));
}
if (outgoing != null && outgoing.value().asString().isEmpty()) {
validationPhase.getContext().addDeploymentProblem(
new DeploymentException("Empty @Outgoing annotation on method " + method));
}
// TODO: validate method params and return type?
mediatorMethods.produce(new MediatorBuildItem(bean, method));
LOGGER.debugf("Found mediator business method %s declared on %s", method, bean);
for (BeanInfo bean : validationPhase.getContext().beans().classBeans()) {
// TODO: add support for inherited business methods
for (MethodInfo method : bean.getTarget().get().asClass().methods()) {
AnnotationInstance incoming = annotationStore.getAnnotation(method,
io.quarkus.smallrye.reactivemessaging.deployment.DotNames.INCOMING);
AnnotationInstance outgoing = annotationStore.getAnnotation(method,
io.quarkus.smallrye.reactivemessaging.deployment.DotNames.OUTGOING);
if (incoming != null || outgoing != null) {
if (incoming != null && incoming.value().asString().isEmpty()) {
validationPhase.getContext().addDeploymentProblem(
new DeploymentException("Empty @Incoming annotation on method " + method));
}
if (outgoing != null && outgoing.value().asString().isEmpty()) {
validationPhase.getContext().addDeploymentProblem(
new DeploymentException("Empty @Outgoing annotation on method " + method));
}
// TODO: validate method params and return type?
mediatorMethods.produce(new MediatorBuildItem(bean, method));
LOGGER.debugf("Found mediator business method %s declared on %s", method, bean);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ void validateBeanDeployment(

// Collect all business methods annotated with @Route and @RouteFilter
AnnotationStore annotationStore = validationPhase.getContext().get(BuildExtension.Key.ANNOTATION_STORE);
for (BeanInfo bean : validationPhase.getContext().get(BuildExtension.Key.BEANS)) {
for (BeanInfo bean : validationPhase.getContext().beans().classBeans()) {
if (bean.isClassBean()) {
// NOTE: inherited business methods are not taken into account
ClassInfo beanClass = bean.getTarget().get().asClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,23 +108,20 @@ void validateBeanDeployment(

// We need to collect all business methods annotated with @MessageConsumer first
AnnotationStore annotationStore = validationPhase.getContext().get(BuildExtension.Key.ANNOTATION_STORE);
for (BeanInfo bean : validationPhase.getContext().get(BuildExtension.Key.BEANS)) {
if (bean.isClassBean()) {
// TODO: inherited business methods?
for (MethodInfo method : bean.getTarget().get().asClass().methods()) {
AnnotationInstance consumeEvent = annotationStore.getAnnotation(method, CONSUME_EVENT);
if (consumeEvent != null) {
// Validate method params and return type
List<Type> params = method.parameters();
if (params.size() != 1) {
throw new IllegalStateException(String.format(
"Event consumer business method must accept exactly one parameter: %s [method: %s, bean:%s",
params, method, bean));
}
messageConsumerBusinessMethods
.produce(new EventConsumerBusinessMethodItem(bean, method, consumeEvent));
LOGGER.debugf("Found event consumer business method %s declared on %s", method, bean);
for (BeanInfo bean : validationPhase.getContext().beans().classBeans()) {
for (MethodInfo method : bean.getTarget().get().asClass().methods()) {
AnnotationInstance consumeEvent = annotationStore.getAnnotation(method, CONSUME_EVENT);
if (consumeEvent != null) {
// Validate method params and return type
List<Type> params = method.parameters();
if (params.size() != 1) {
throw new IllegalStateException(String.format(
"Event consumer business method must accept exactly one parameter: %s [method: %s, bean:%s",
params, method, bean));
}
messageConsumerBusinessMethods
.produce(new EventConsumerBusinessMethodItem(bean, method, consumeEvent));
LOGGER.debugf("Found event consumer business method %s declared on %s", method, bean);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ public final class BeanConfigurator<T> {

private final Map<String, Object> params;

private boolean isDefaultBean;
private boolean defaultBean;

private boolean removable;

/**
*
Expand All @@ -77,6 +79,7 @@ public final class BeanConfigurator<T> {
this.scope = BuiltinScope.DEPENDENT.getInfo();
this.params = new HashMap<>();
this.name = null;
this.removable = true;
}

public BeanConfigurator<T> param(String name, Class<?> value) {
Expand Down Expand Up @@ -152,7 +155,12 @@ public BeanConfigurator<T> name(String name) {
}

public BeanConfigurator<T> defaultBean() {
this.isDefaultBean = true;
this.defaultBean = true;
return this;
}

public BeanConfigurator<T> unremovable() {
this.removable = false;
return this;
}

Expand Down Expand Up @@ -216,7 +224,7 @@ public void done() {
.beanDeployment(beanDeployment).scope(scope).types(types)
.qualifiers(qualifiers)
.alternativePriority(alternativePriority).name(name).creator(creatorConsumer).destroyer(destroyerConsumer)
.params(params).defaultBean(isDefaultBean).build());
.params(params).defaultBean(defaultBean).removable(removable).build());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@ void init(Consumer<BytecodeTransformer> bytecodeTransformerConsumer) {
if (bean.getName() != null) {
continue test;
}
// Unremovable synthetic beans
if (!bean.isRemovable()) {
continue test;
}
// Custom exclusions
for (Predicate<BeanInfo> exclusion : unusedExclusions) {
if (exclusion.test(bean)) {
Expand Down Expand Up @@ -294,6 +298,9 @@ void init(Consumer<BytecodeTransformer> bytecodeTransformerConsumer) {
}
LOGGER.debugf("Removed %s unused beans in %s ms", removable.size(), System.currentTimeMillis() - removalStart);
}

buildContext.putInternal(BuildExtension.Key.REMOVED_BEANS.asString(), Collections.unmodifiableSet(removedBeans));

LOGGER.debugf("Bean deployment initialized in %s ms", System.currentTimeMillis() - start);
}

Expand Down Expand Up @@ -836,6 +843,11 @@ public <V> V put(Key<V> key, V value) {
return buildContext.put(key, value);
}

@Override
public BeanStream beans() {
return new BeanStream(get(BuildExtension.Key.BEANS));
}

};
for (BeanRegistrar registrar : beanRegistrars) {
registrar.register(registrationContext);
Expand Down Expand Up @@ -956,6 +968,16 @@ public List<Throwable> getDeploymentProblems() {
return Collections.unmodifiableList(errors);
}

@Override
public BeanStream beans() {
return new BeanStream(get(BuildExtension.Key.BEANS));
}

@Override
public BeanStream removedBeans() {
return new BeanStream(get(BuildExtension.Key.REMOVED_BEANS));
}

}

}
Loading