Skip to content

Commit

Permalink
Properly handle missing classes in metadata + precalculated requireme…
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov authored Jul 3, 2024
1 parent 35fac67 commit 6f27055
Show file tree
Hide file tree
Showing 34 changed files with 2,419 additions and 749 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ private static void pushEmptyObjectsArray(GeneratorAdapter methodVisitor) {
methodVisitor.getStatic(Type.getType(ArrayUtils.class), "EMPTY_OBJECT_ARRAY", Type.getType(Object[].class));
}

private static void invokeLoadClassValueMethod(
public static void invokeLoadClassValueMethod(
Type declaringType,
ClassVisitor declaringClassWriter,
GeneratorAdapter methodVisitor,
Expand Down

Large diffs are not rendered by default.

43 changes: 40 additions & 3 deletions core/src/main/java/io/micronaut/core/util/CollectionUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,31 @@
*/
package io.micronaut.core.util;

import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.annotation.UsedByGeneratedCode;
import io.micronaut.core.convert.ConversionService;

import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;

/**
* <p>Utility methods for working with {@link java.util.Collection} types</p>.
Expand Down Expand Up @@ -458,4 +476,23 @@ public static <T> Set<T> iterableToSet(Iterable<T> iterable) {
}

}

/**
* Create an enum set from an array.
* NOTE: At least one item is required
*
* @param enums The array of enums
* @param <E> The enum type
* @return The enum set
* @since 4.6
*/
@NonNull
public static <E extends Enum<E>> EnumSet<E> enumSet(@NonNull E... enums) {
if (enums.length == 0) {
throw new IllegalStateException("At least one item is required!");
}
EnumSet<E> set = EnumSet.noneOf(enums[0].getDeclaringClass());
set.addAll(Arrays.asList(enums));
return set;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class RequiresBeanSpec extends Specification {
def list = e.message.readLines().toList()
list[0] == 'No bean of type [io.micronaut.inject.configurations.requiresproperty.RequiresProperty] exists. '
list[1] == '* [RequiresProperty] is disabled because it is within the package [io.micronaut.inject.configurations.requiresproperty] which is disabled due to bean requirements: '
list[2] == ' - Required property [data-source.url] with value [null] not present'
list[2] == ' - Required property [data-source.url] not present'

cleanup:
context.close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class MyBean {
lines[1] == "* [MyBean] requires the presence of a bean of type [test.MyClient]."
lines[2] == " * [MyClient] requires the presence of a bean of type [test.MyConfiguration]."
lines[3] == " * [MyConfiguration] is disabled because:"
lines[4] == " - Required property [myconf] with value [null] not present"
lines[4] == " - Required property [myconf] not present"
cleanup:
context.close()
}
Expand Down Expand Up @@ -89,7 +89,7 @@ class MyBean {
lines[3] == " * [MyMultiConfiguration] a candidate of [MyConfiguration] is disabled because:"
lines[4] == " - Configuration requires entries under the prefix: [myconf2.multiple.default]"
lines[5] == " * [MyDefaultConfiguration] a candidate of [MyConfiguration] is disabled because:"
lines[6] == " - Required property [myconf] with value [null] not present"
lines[6] == " - Required property [myconf] not present"
lines.size() == 7
cleanup:
context.close()
Expand Down Expand Up @@ -140,11 +140,11 @@ class MyBean {
lines[1] == "* [MyBean] requires the presence of a bean of type [test.MyClient]."
lines[2] == " * [MyClient] requires the presence of a bean of type [test.MyConfiguration]."
lines[3] == " * [MyDefaultConfiguration] a candidate of [MyConfiguration] is disabled because:"
lines[4] == " - Required property [myconf] with value [null] not present"
lines[4] == " - Required property [myconf] not present"
lines[5] == " * [MyMultiConfiguration] a candidate of [MyConfiguration] is disabled because:"
lines[6] == " - No bean of type [test.MyHelper] present within context"
lines[7] == " * [MyHelper] is disabled because:"
lines[8] == " - Required property [myconf.helper] with value [null] not present"
lines[8] == " - Required property [myconf.helper] not present"

lines.size() == 9
cleanup:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,26 @@ public boolean isEnabled(@NonNull BeanContext context, @Nullable BeanResolutionC
this, resolutionContext);
boolean enabled = condition == null || condition.matches(conditionContext);
if (!enabled) {
if (ConditionLog.LOG.isDebugEnabled()) {
if (this instanceof BeanConfiguration) {
ConditionLog.LOG.debug("{} will not be loaded due to failing conditions:", this);
} else {
ConditionLog.LOG.debug("Bean [{}] will not be loaded due to failing conditions:", this);
}
for (Failure failure : conditionContext.getFailures()) {
ConditionLog.LOG.debug("* {}", failure.getMessage());
}
}
defaultBeanContext.trackDisabledComponent(conditionContext);
onFail(conditionContext, defaultBeanContext);
}

return enabled;
}

protected final void onFail(DefaultConditionContext<AbstractBeanContextConditional> conditionContext, DefaultBeanContext defaultBeanContext) {
if (ConditionLog.LOG.isDebugEnabled()) {
if (this instanceof BeanConfiguration) {
ConditionLog.LOG.debug("{} will not be loaded due to failing conditions:", this);
} else {
ConditionLog.LOG.debug("Bean [{}] will not be loaded due to failing conditions:", this);
}
for (Failure failure : conditionContext.getFailures()) {
ConditionLog.LOG.debug("* {}", failure.getMessage());
}
}
defaultBeanContext.trackDisabledComponent(conditionContext);
}

@SuppressWarnings("java:S3416")
static final class ConditionLog {
static final Logger LOG = LoggerFactory.getLogger(Condition.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@
*/
package io.micronaut.context;

import io.micronaut.context.condition.Condition;
import io.micronaut.context.condition.ConditionContext;
import io.micronaut.context.conditions.MatchesDynamicCondition;
import io.micronaut.context.exceptions.BeanInstantiationException;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.type.Argument;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.BeanDefinitionReference;
import io.micronaut.inject.ExecutableMethodsDefinition;
import io.micronaut.inject.annotation.EvaluatedAnnotationMetadata;

import java.util.Map;

Expand All @@ -34,6 +39,10 @@
*/
public abstract class AbstractInitializableBeanDefinitionAndReference<T> extends AbstractInitializableBeanDefinition<T> implements BeanDefinitionReference<T> {

private final Throwable failedInitialization;
private final Condition[] preLoadConditions;
private final Condition[] postLoadConditions;

protected AbstractInitializableBeanDefinitionAndReference(Class<T> beanType,
@Nullable MethodOrFieldReference constructor,
@Nullable AnnotationMetadata annotationMetadata,
Expand All @@ -43,9 +52,68 @@ protected AbstractInitializableBeanDefinitionAndReference(Class<T> beanType,
@Nullable ExecutableMethodsDefinition<T> executableMethodsDefinition,
@Nullable Map<String, Argument<?>[]> typeArgumentsMap,
@NonNull PrecalculatedInfo precalculatedInfo) {
this(beanType, constructor, annotationMetadata, methodInjection, fieldInjection, annotationInjection, executableMethodsDefinition, typeArgumentsMap, precalculatedInfo, null, null, null);
}

protected AbstractInitializableBeanDefinitionAndReference(Class<T> beanType,
@Nullable MethodOrFieldReference constructor,
@Nullable AnnotationMetadata annotationMetadata,
@Nullable MethodReference[] methodInjection,
@Nullable FieldReference[] fieldInjection,
@Nullable AnnotationReference[] annotationInjection,
@Nullable ExecutableMethodsDefinition<T> executableMethodsDefinition,
@Nullable Map<String, Argument<?>[]> typeArgumentsMap,
@NonNull PrecalculatedInfo precalculatedInfo,
@Nullable Condition[] preLoadConditions,
@Nullable Condition[] postLoadConditions,
@Nullable Throwable failedInitialization) {
super(beanType, constructor, annotationMetadata, methodInjection, fieldInjection, annotationInjection, executableMethodsDefinition, typeArgumentsMap, precalculatedInfo);
this.failedInitialization = failedInitialization;
this.preLoadConditions = preLoadConditions;
this.postLoadConditions = postLoadConditions;
}

/**
* Is enabled bean definition.
*
* @param context The bean context
* @param resolutionContext The resolution context
* @param preCheck if it's a pre-load / post-load
* @return true if enabled
*/
public final boolean isEnabled(BeanContext context, BeanResolutionContext resolutionContext, boolean preCheck) {
if (preLoadConditions != null && postLoadConditions != null) {
DefaultBeanContext defaultBeanContext = (DefaultBeanContext) context;
DefaultConditionContext<AbstractBeanContextConditional> conditionContext = new DefaultConditionContext<>(
defaultBeanContext,
this, resolutionContext);
boolean matches;
if (preCheck) {
matches = matches(conditionContext, preLoadConditions);
} else {
matches = matches(conditionContext, postLoadConditions);
}
if (matches) {
return true;
}
onFail(conditionContext, defaultBeanContext);
return false;
}
if (preCheck) {
// new implementation of definition and reference always did all checks in post checks
return true;
}
return isEnabled(context, resolutionContext);
}

private static boolean matches(ConditionContext<?> conditionContext, Condition[] conditions) {
for (Condition condition : conditions) {
if (!condition.matches(conditionContext)) {
return false;
}
}
return true;
}

/**
* Represents {@link BeanDefinitionReference#getBeanDefinitionName()} when the class implements {@link BeanDefinitionReference}.
Expand All @@ -59,6 +127,9 @@ public final String getBeanDefinitionName() {

@Override
public final BeanDefinition<T> load(BeanContext context) {
if (failedInitialization != null) {
throw new BeanInstantiationException("Failed to initialize the bean [" + getBeanType() + "]: " + failedInitialization.getMessage(), failedInitialization);
}
BeanDefinition<T> definition = load();
if (definition instanceof EnvironmentConfigurable environmentConfigurable) {
if (context instanceof DefaultApplicationContext applicationContext) {
Expand All @@ -71,6 +142,22 @@ public final BeanDefinition<T> load(BeanContext context) {
if (definition instanceof BeanContextConfigurable ctxConfigurable) {
ctxConfigurable.configure(context);
}
if (postLoadConditions != null) {
for (int i = 0; i < postLoadConditions.length; i++) {
Condition postStartCondition = postLoadConditions[i];
if (postStartCondition instanceof MatchesDynamicCondition matchesDynamicCondition) {
AnnotationMetadata annotationMetadata = EvaluatedAnnotationMetadata.wrapIfNecessary(matchesDynamicCondition.annotationMetadata());
if (annotationMetadata instanceof EvaluatedAnnotationMetadata eam) {
eam.configure(context);
eam.setBeanDefinition(this);
}
postLoadConditions[i] = new MatchesDynamicCondition(
annotationMetadata
);
}
}

}
return definition;
}

Expand Down
24 changes: 18 additions & 6 deletions inject/src/main/java/io/micronaut/context/DefaultBeanContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -2087,7 +2087,8 @@ protected void initializeContext(
* @return The candidates
*/
@NonNull
protected <T> Collection<BeanDefinition<T>> findBeanCandidates(@Nullable BeanResolutionContext resolutionContext,
@Internal
public final <T> Collection<BeanDefinition<T>> findBeanCandidates(@Nullable BeanResolutionContext resolutionContext,
@NonNull Argument<T> beanType,
@Nullable BeanDefinition<?> filter) {
Predicate<BeanDefinition<T>> predicate = filter == null ? null : definition -> !definition.equals(filter);
Expand Down Expand Up @@ -4206,9 +4207,6 @@ static final class BeanDefinitionProducer {

BeanDefinitionProducer(@NonNull BeanDefinitionReference reference) {
this.reference = reference;
if (reference instanceof AbstractInitializableBeanDefinitionAndReference<?>) {
referenceEnabled = true; // Postpone validation check
}
}

public boolean isReferenceEnabled(DefaultBeanContext context) {
Expand All @@ -4222,7 +4220,7 @@ public boolean isReferenceEnabled(DefaultBeanContext context, @Nullable BeanReso
return false;
}
if (referenceEnabled == null) {
if (ref.isEnabled(context, resolutionContext)) {
if (isReferenceEnabled(ref, context, resolutionContext)) {
referenceEnabled = true;
} else {
referenceEnabled = false;
Expand All @@ -4232,6 +4230,13 @@ public boolean isReferenceEnabled(DefaultBeanContext context, @Nullable BeanReso
return referenceEnabled;
}

private boolean isReferenceEnabled(BeanDefinitionReference<?> ref, DefaultBeanContext context, BeanResolutionContext resolutionContext) {
if (ref instanceof io.micronaut.context.AbstractInitializableBeanDefinitionAndReference<?> referenceAndDefinition) {
return referenceAndDefinition.isEnabled(context, resolutionContext, true);
}
return reference.isEnabled(context);
}

public boolean isDisabled() {
if (reference == null) {
return true;
Expand All @@ -4254,7 +4259,7 @@ public boolean isDefinitionEnabled(DefaultBeanContext context, @Nullable BeanRes
if (definitionEnabled == null) {
if (isReferenceEnabled(context, resolutionContext)) {
BeanDefinition <?> def = getDefinition(context);
if (def.isEnabled(context, resolutionContext)) {
if (isDefinitionEnabled(context, resolutionContext, def)) {
definition = def;
definitionEnabled = true;
} else {
Expand All @@ -4267,6 +4272,13 @@ public boolean isDefinitionEnabled(DefaultBeanContext context, @Nullable BeanRes
return definitionEnabled;
}

private boolean isDefinitionEnabled(DefaultBeanContext context, BeanResolutionContext resolutionContext, BeanDefinition<?> def) {
if (def instanceof io.micronaut.context.AbstractInitializableBeanDefinitionAndReference<?> definitionAndReference) {
return definitionAndReference.isEnabled(context, resolutionContext, false);
}
return def.isEnabled(context, resolutionContext);
}

public <T> BeanDefinitionReference<T> getReference() {
// The reference needs to be assigned to a new variable as it can change between checks
Boolean refEnabled = referenceEnabled;
Expand Down
Loading

0 comments on commit 6f27055

Please sign in to comment.