Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.micronaut.context.AbstractBeanResolutionContext Maven / Gradle / Ivy
/*
* Copyright 2017-2020 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.context;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.env.CachedEnvironment;
import io.micronaut.context.annotation.InjectScope;
import io.micronaut.context.env.ConfigurationPath;
import io.micronaut.context.exceptions.CircularDependencyException;
import io.micronaut.context.scope.CustomScope;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.convert.ArgumentConversionContext;
import io.micronaut.core.naming.Named;
import io.micronaut.core.type.Argument;
import io.micronaut.core.type.ArgumentCoercible;
import io.micronaut.core.util.ObjectUtils;
import io.micronaut.inject.*;
import java.util.*;
import java.util.stream.Stream;
/**
* Default implementation of the {@link BeanResolutionContext} interface.
*
* @author Graeme Rocher
* @since 1.2.3
*/
@Internal
public abstract class AbstractBeanResolutionContext implements BeanResolutionContext {
private static final String CONSTRUCTOR_METHOD_NAME = "";
protected final DefaultBeanContext context;
protected final BeanDefinition> rootDefinition;
protected final Path path;
private Map attributes;
private Qualifier> qualifier;
private List> dependentBeans;
private BeanRegistration> dependentFactory;
private ConfigurationPath configurationPath;
/**
* @param context The bean context
* @param rootDefinition The bean root definition
*/
@Internal
protected AbstractBeanResolutionContext(DefaultBeanContext context, BeanDefinition> rootDefinition) {
this.context = context;
this.rootDefinition = rootDefinition;
this.path = new DefaultPath();
}
@Override
public ConfigurationPath getConfigurationPath() {
if (configurationPath != null) {
return configurationPath;
} else {
this.configurationPath = ConfigurationPath.newPath();
return configurationPath;
}
}
@Override
public ConfigurationPath setConfigurationPath(ConfigurationPath configurationPath) {
ConfigurationPath old = this.configurationPath;
this.configurationPath = configurationPath;
return old;
}
@NonNull
@Override
public T getBean(@NonNull Argument beanType, @Nullable Qualifier qualifier) {
return context.getBean(this, beanType, qualifier);
}
@NonNull
@Override
public Collection getBeansOfType(@NonNull Argument beanType, @Nullable Qualifier qualifier) {
return context.getBeansOfType(this, beanType, qualifier);
}
@NonNull
@Override
public Stream streamOfType(@NonNull Argument beanType, @Nullable Qualifier qualifier) {
return context.streamOfType(this, beanType, qualifier);
}
@Override
public Map mapOfType(Argument beanType, Qualifier qualifier) {
return context.mapOfType(this, beanType, qualifier);
}
@NonNull
@Override
public Optional findBean(@NonNull Argument beanType, @Nullable Qualifier qualifier) {
return context.findBean(this, beanType, qualifier);
}
@NonNull
@Override
public Collection> getBeanRegistrations(@NonNull Argument beanType, @Nullable Qualifier qualifier) {
return context.getBeanRegistrations(this, beanType, qualifier);
}
/**
* Copy the state from a previous resolution context.
*
* @param context The previous context
*/
public void copyStateFrom(@NonNull AbstractBeanResolutionContext context) {
path.addAll(context.path);
qualifier = context.qualifier;
if (context.attributes != null) {
getAttributesOrCreate().putAll(context.attributes);
}
}
@Override
public void addDependentBean(BeanRegistration beanRegistration) {
if (beanRegistration.getBeanDefinition() == rootDefinition) {
// Don't add self
return;
}
if (dependentBeans == null) {
dependentBeans = new ArrayList<>(3);
}
dependentBeans.add(beanRegistration);
}
@Override
public void destroyInjectScopedBeans() {
final CustomScope> injectScope = context.getCustomScopeRegistry()
.findScope(InjectScope.class.getName())
.orElse(null);
if (injectScope instanceof LifeCycle> cycle) {
cycle.stop();
}
}
@NonNull
@Override
public List> getAndResetDependentBeans() {
if (dependentBeans == null) {
return Collections.emptyList();
}
final List> registrations = Collections.unmodifiableList(dependentBeans);
dependentBeans = null;
return registrations;
}
@Override
public void markDependentAsFactory() {
if (dependentBeans != null) {
if (dependentBeans.isEmpty()) {
return;
}
if (dependentBeans.size() != 1) {
throw new IllegalStateException("Expected only one bean dependent!");
}
dependentFactory = dependentBeans.remove(0);
}
}
@Override
public BeanRegistration> getAndResetDependentFactoryBean() {
BeanRegistration> result = this.dependentFactory;
this.dependentFactory = null;
return result;
}
@Override
public List> popDependentBeans() {
List> result = this.dependentBeans;
this.dependentBeans = null;
return result;
}
@Override
public void pushDependentBeans(List> dependentBeans) {
if (this.dependentBeans != null && !this.dependentBeans.isEmpty()) {
throw new IllegalStateException("Found existing dependent beans!");
}
this.dependentBeans = dependentBeans;
}
@Override
public final BeanContext getContext() {
return context;
}
@Override
public final BeanDefinition getRootDefinition() {
return rootDefinition;
}
@Override
public final Path getPath() {
return path;
}
@Override
public final Object setAttribute(CharSequence key, Object value) {
return getAttributesOrCreate().put(key, value);
}
/**
* @param key The key
* @return The attribute value
*/
@Override
public final Object getAttribute(CharSequence key) {
if (attributes == null) {
return null;
}
return attributes.get(key);
}
@Override
public final Object removeAttribute(CharSequence key) {
if (attributes != null && key != null) {
return attributes.remove(key);
}
return null;
}
@Override
public Map getAttributes() {
return attributes;
}
@Override
public void setAttributes(Map attributes) {
this.attributes = attributes;
}
@Nullable
@Override
public Qualifier> getCurrentQualifier() {
return qualifier;
}
@Override
public void setCurrentQualifier(@Nullable Qualifier> qualifier) {
this.qualifier = qualifier;
}
@Override
public Optional get(CharSequence name, ArgumentConversionContext conversionContext) {
if (attributes == null) {
return Optional.empty();
}
Object value = attributes.get(name);
if (value != null && conversionContext.getArgument().getType().isInstance(value)) {
return Optional.of((T) value);
}
return Optional.empty();
}
@Override
public Optional get(CharSequence name, Class requiredType) {
if (attributes == null) {
return Optional.empty();
}
Object value = attributes.get(name);
if (requiredType.isInstance(value)) {
return Optional.of((T) value);
}
return Optional.empty();
}
protected void onNewSegment(Segment, ?> segment) {
//no-op
}
@NonNull
private Map getAttributesOrCreate() {
if (attributes == null) {
attributes = new LinkedHashMap<>(2);
}
return attributes;
}
/**
* Class that represents a default path.
*/
class DefaultPath extends LinkedList> implements Path {
public static final String RIGHT_ARROW = " --> ";
private static final String CIRCULAR_ERROR_MSG = "Circular dependency detected";
DefaultPath() {
}
@Override
public String toString() {
Iterator> i = descendingIterator();
StringBuilder pathString = new StringBuilder();
while (i.hasNext()) {
pathString.append(i.next().toString());
if (i.hasNext()) {
pathString.append(RIGHT_ARROW);
}
}
return pathString.toString();
}
@SuppressWarnings("MagicNumber")
@Override
public String toCircularString() {
Iterator> i = descendingIterator();
StringBuilder pathString = new StringBuilder();
String ls = CachedEnvironment.getProperty("line.separator");
while (i.hasNext()) {
String segmentString = i.next().toString();
pathString.append(segmentString);
if (i.hasNext()) {
pathString.append(RIGHT_ARROW);
} else {
int totalLength = pathString.length() - 3;
String spaces = String.join("", Collections.nCopies(totalLength, " "));
pathString.append(ls)
.append("^")
.append(spaces)
.append("|")
.append(ls)
.append("|")
.append(spaces)
.append("|").append(ls)
.append("|")
.append(spaces)
.append("|").append(ls).append('+');
pathString.append(String.join("", Collections.nCopies(totalLength, "-"))).append('+');
}
}
return pathString.toString();
}
@Override
public Optional> currentSegment() {
return Optional.ofNullable(peek());
}
@Override
public Path pushConstructorResolve(BeanDefinition declaringType, Argument argument) {
ConstructorInjectionPoint> constructor = declaringType.getConstructor();
if (constructor instanceof MethodInjectionPoint, ?> methodInjectionPoint) {
return pushConstructorResolve(declaringType, methodInjectionPoint.getName(), argument, constructor.getArguments());
}
return pushConstructorResolve(declaringType, CONSTRUCTOR_METHOD_NAME, argument, constructor.getArguments());
}
@Override
public Path pushConstructorResolve(BeanDefinition declaringType, String methodName, Argument argument, Argument[] arguments) {
if (CONSTRUCTOR_METHOD_NAME.equals(methodName)) {
ConstructorSegment constructorSegment = new ConstructorArgumentSegment(declaringType, (Qualifier) getCurrentQualifier(), methodName, argument, arguments);
detectCircularDependency(declaringType, argument, constructorSegment);
} else {
Segment, ?> previous = peek();
MethodSegment, ?> methodSegment = new MethodArgumentSegment(declaringType, (Qualifier) getCurrentQualifier(), methodName, argument, arguments, previous instanceof MethodSegment ms ? ms : null);
if (contains(methodSegment)) {
throw new CircularDependencyException(AbstractBeanResolutionContext.this, argument, CIRCULAR_ERROR_MSG);
} else {
push(methodSegment);
}
}
return this;
}
@Override
public Path pushBeanCreate(BeanDefinition> declaringType, Argument> beanType) {
return pushConstructorResolve(declaringType, beanType);
}
@Override
public Path pushMethodArgumentResolve(BeanDefinition declaringType, MethodInjectionPoint methodInjectionPoint, Argument argument) {
Segment, ?> previous = peek();
MethodSegment, ?> methodSegment = new MethodArgumentSegment(declaringType, (Qualifier) getCurrentQualifier(), methodInjectionPoint.getName(), argument,
methodInjectionPoint.getArguments(), previous instanceof MethodSegment ms ? ms : null);
if (contains(methodSegment)) {
throw new CircularDependencyException(AbstractBeanResolutionContext.this, methodInjectionPoint, argument, CIRCULAR_ERROR_MSG);
} else {
push(methodSegment);
}
return this;
}
@Override
public Path pushMethodArgumentResolve(BeanDefinition declaringType, String methodName, Argument argument, Argument[] arguments) {
Segment, ?> previous = peek();
MethodSegment, ?> methodSegment = new MethodArgumentSegment(declaringType, (Qualifier) getCurrentQualifier(), methodName, argument, arguments, previous instanceof MethodSegment ms ? ms : null);
if (contains(methodSegment)) {
throw new CircularDependencyException(AbstractBeanResolutionContext.this, declaringType, methodName, argument, CIRCULAR_ERROR_MSG);
} else {
push(methodSegment);
}
return this;
}
@Override
public Path pushFieldResolve(BeanDefinition declaringType, FieldInjectionPoint fieldInjectionPoint) {
FieldSegment, ?> fieldSegment = new FieldSegment<>(declaringType, getCurrentQualifier(), fieldInjectionPoint.asArgument());
if (contains(fieldSegment)) {
throw new CircularDependencyException(AbstractBeanResolutionContext.this, fieldInjectionPoint, CIRCULAR_ERROR_MSG);
} else {
push(fieldSegment);
}
return this;
}
@Override
public Path pushFieldResolve(BeanDefinition declaringType, Argument fieldAsArgument) {
FieldSegment, ?> fieldSegment = new FieldSegment<>(declaringType, getCurrentQualifier(), fieldAsArgument);
if (contains(fieldSegment)) {
throw new CircularDependencyException(AbstractBeanResolutionContext.this, declaringType, fieldAsArgument.getName(), CIRCULAR_ERROR_MSG);
} else {
push(fieldSegment);
}
return this;
}
@Override
public Path pushAnnotationResolve(BeanDefinition beanDefinition, Argument annotationMemberBeanAsArgument) {
AnnotationSegment annotationSegment = new AnnotationSegment(beanDefinition, getCurrentQualifier(), annotationMemberBeanAsArgument);
if (contains(annotationSegment)) {
throw new CircularDependencyException(AbstractBeanResolutionContext.this, beanDefinition, annotationMemberBeanAsArgument.getName(), CIRCULAR_ERROR_MSG);
} else {
push(annotationSegment);
}
return this;
}
private void detectCircularDependency(BeanDefinition declaringType, Argument argument, Segment constructorSegment) {
if (contains(constructorSegment)) {
Segment last = peek();
if (last != null) {
BeanDefinition declaringBean = last.getDeclaringType();
// if the currently injected segment is a constructor argument and the type to be constructed is the
// same as the candidate, then filter out the candidate to avoid a circular injection problem
if (!declaringBean.equals(declaringType)) {
if (declaringType instanceof ProxyBeanDefinition> proxyBeanDefinition) {
// take into account proxies
if (!proxyBeanDefinition.getTargetDefinitionType().equals(declaringBean.getClass())) {
throw new CircularDependencyException(AbstractBeanResolutionContext.this, argument, CIRCULAR_ERROR_MSG);
} else {
push(constructorSegment);
}
} else if (declaringBean instanceof ProxyBeanDefinition> proxyBeanDefinition) {
// take into account proxies
if (!proxyBeanDefinition.getTargetDefinitionType().equals(declaringType.getClass())) {
throw new CircularDependencyException(AbstractBeanResolutionContext.this, argument, CIRCULAR_ERROR_MSG);
} else {
push(constructorSegment);
}
} else {
throw new CircularDependencyException(AbstractBeanResolutionContext.this, argument, CIRCULAR_ERROR_MSG);
}
} else {
push(constructorSegment);
}
} else {
throw new CircularDependencyException(AbstractBeanResolutionContext.this, argument, CIRCULAR_ERROR_MSG);
}
} else {
push(constructorSegment);
}
}
@Override
public void push(Segment, ?> segment) {
super.push(segment);
AbstractBeanResolutionContext.this.onNewSegment(segment);
}
}
/**
* A segment that represents a method argument.
*/
public static final class ConstructorArgumentSegment extends ConstructorSegment implements ArgumentInjectionPoint {
public ConstructorArgumentSegment(BeanDefinition declaringType, Qualifier qualifier, String methodName, Argument argument, Argument[] arguments) {
super(declaringType, qualifier, methodName, argument, arguments);
}
@Override
public CallableInjectionPoint getOuterInjectionPoint() {
throw new UnsupportedOperationException("Outer injection point inaccessible from here");
}
@Override
public BeanDefinition getDeclaringBean() {
return getDeclaringType();
}
@Override
public Qualifier getDeclaringBeanQualifier() {
return getDeclaringTypeQualifier();
}
}
/**
* A segment that represents a constructor.
*/
public static class ConstructorSegment extends AbstractSegment implements ArgumentInjectionPoint {
private final String methodName;
private final Argument[] arguments;
/**
* @param declaringBeanDefinition The declaring class
* @param qualifier The qualifier
* @param methodName The methodName
* @param argument The argument
* @param arguments The arguments
*/
ConstructorSegment(BeanDefinition declaringBeanDefinition, Qualifier qualifier, String methodName, Argument argument, Argument[] arguments) {
super(declaringBeanDefinition, qualifier, declaringBeanDefinition.getBeanType().getName(), argument);
this.methodName = methodName;
this.arguments = arguments;
}
@Override
public String toString() {
StringBuilder baseString;
if (CONSTRUCTOR_METHOD_NAME.equals(methodName)) {
baseString = new StringBuilder("new ");
baseString.append(getDeclaringType().getBeanType().getSimpleName());
} else {
baseString = new StringBuilder(getDeclaringType().getBeanType().getSimpleName()).append('.');
baseString.append(methodName);
}
outputArguments(baseString, arguments);
return baseString.toString();
}
@Override
public InjectionPoint getInjectionPoint() {
return this;
}
@NonNull
@Override
public CallableInjectionPoint getOuterInjectionPoint() {
return getDeclaringType().getConstructor();
}
@Override
public BeanDefinition getDeclaringBean() {
return ConstructorSegment.this.getDeclaringType();
}
@Override
public Qualifier getDeclaringBeanQualifier() {
return ConstructorSegment.this.getDeclaringTypeQualifier();
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return getArgument().getAnnotationMetadata();
}
}
/**
* A segment that represents a method argument.
*/
public static final class MethodArgumentSegment extends MethodSegment implements ArgumentInjectionPoint {
private final MethodSegment outer;
public MethodArgumentSegment(BeanDefinition declaringType,
Qualifier qualifier,
String methodName,
Argument argument,
Argument [] arguments,
MethodSegment outer) {
super(declaringType, qualifier, methodName, argument, arguments);
this.outer = outer;
}
@Override
public CallableInjectionPoint getOuterInjectionPoint() {
if (outer == null) {
throw new IllegalStateException("Outer argument inaccessible");
}
return outer;
}
@Override
public String toString() {
BeanDefinition> declaringBean = getDeclaringBean();
if (declaringBean.hasAnnotation(Factory.class)) {
ConstructorInjectionPoint> constructor = declaringBean.getConstructor();
var baseString = new StringBuilder(constructor.getDeclaringBeanType().getSimpleName()).append('.');
baseString.append(getName());
outputArguments(baseString, getArguments());
return baseString.toString();
} else {
return super.toString();
}
}
}
/**
* A segment that represents a method.
*/
public static class MethodSegment extends AbstractSegment implements CallableInjectionPoint {
private final Argument[] arguments;
/**
* @param declaringType The declaring type
* @param qualifier The qualifier
* @param methodName The method name
* @param argument The argument
* @param arguments The arguments
*/
MethodSegment(BeanDefinition declaringType, Qualifier qualifier, String methodName, Argument argument, Argument[] arguments) {
super(declaringType, qualifier, methodName, argument);
this.arguments = arguments;
}
@Override
public String toString() {
StringBuilder baseString = new StringBuilder(getDeclaringType().getBeanType().getSimpleName()).append('.');
baseString.append(getName());
outputArguments(baseString, arguments);
return baseString.toString();
}
@Override
public InjectionPoint getInjectionPoint() {
return this;
}
@Override
public BeanDefinition getDeclaringBean() {
return getDeclaringType();
}
@Override
public Argument>[] getArguments() {
return arguments;
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return getArgument().getAnnotationMetadata();
}
@Override
public Qualifier getDeclaringBeanQualifier() {
return getDeclaringTypeQualifier();
}
}
/**
* A segment that represents a field.
*/
public static final class FieldSegment extends AbstractSegment implements InjectionPoint, ArgumentCoercible, ArgumentInjectionPoint {
/**
* @param declaringClass The declaring class
* @param qualifier The qualifier
* @param argument The argument
*/
FieldSegment(BeanDefinition declaringClass, Qualifier qualifier, Argument argument) {
super(declaringClass, qualifier, argument.getName(), argument);
}
@Override
public String toString() {
return getDeclaringType().getBeanType().getSimpleName() + "." + getName();
}
@Override
public InjectionPoint getInjectionPoint() {
return this;
}
@Override
public BeanDefinition getDeclaringBean() {
return getDeclaringType();
}
@Override
public CallableInjectionPoint getOuterInjectionPoint() {
throw new UnsupportedOperationException("Outer injection point not retrievable from here");
}
@Override
public Argument asArgument() {
return getArgument();
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return getArgument().getAnnotationMetadata();
}
@Override
public Qualifier getDeclaringBeanQualifier() {
return getDeclaringTypeQualifier();
}
}
/**
* A segment that represents annotation.
*
* @since 3.3.0
*/
public static final class AnnotationSegment extends AbstractSegment implements InjectionPoint {
/**
* @param beanDefinition The bean definition
* @param qualifier The qualifier
* @param argument The argument
*/
AnnotationSegment(BeanDefinition beanDefinition, Qualifier qualifier, Argument argument) {
super(beanDefinition, qualifier, argument.getName(), argument);
}
@Override
public String toString() {
return getName();
}
@Override
public InjectionPoint getInjectionPoint() {
return this;
}
@Override
public BeanDefinition getDeclaringBean() {
return getDeclaringType();
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return getArgument().getAnnotationMetadata();
}
@Override
public Qualifier getDeclaringBeanQualifier() {
return getDeclaringTypeQualifier();
}
}
/**
* Abstract class for a Segment.
*/
protected abstract static class AbstractSegment implements Segment, Named {
private final BeanDefinition declaringComponent;
@Nullable
private final Qualifier qualifier;
private final String name;
private final Argument argument;
/**
* @param declaringClass The declaring class
* @param qualifier The qualifier
* @param name The name
* @param argument The argument
*/
AbstractSegment(BeanDefinition declaringClass, Qualifier qualifier, String name, Argument argument) {
this.declaringComponent = declaringClass;
this.qualifier = qualifier;
this.name = name;
this.argument = argument;
}
@Override
public String getName() {
return name;
}
@Override
public BeanDefinition getDeclaringType() {
return declaringComponent;
}
@Override
public Qualifier getDeclaringTypeQualifier() {
return qualifier == null ? declaringComponent.getDeclaredQualifier() : qualifier;
}
@Override
public Argument getArgument() {
return argument;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AbstractSegment that = (AbstractSegment) o;
return declaringComponent.equals(that.declaringComponent) && name.equals(that.name) && argument.equals(that.argument);
}
@Override
public int hashCode() {
return ObjectUtils.hash(declaringComponent, name, argument);
}
/**
* @param baseString The base string
* @param arguments The arguments
*/
void outputArguments(StringBuilder baseString, Argument[] arguments) {
baseString.append('(');
for (int i = 0; i < arguments.length; i++) {
Argument> argument = arguments[i];
boolean isInjectedArgument = argument.equals(getArgument());
if (isInjectedArgument) {
baseString.append('[');
}
baseString.append(argument);
if (isInjectedArgument) {
baseString.append(']');
}
if (i != arguments.length - 1) {
baseString.append(',');
}
}
baseString.append(')');
}
}
}