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

Merge duplicated plexus component definitions #44

Merged
merged 1 commit into from
May 18, 2023
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
4 changes: 4 additions & 0 deletions container/src/main/java/io/smallrye/beanbag/Bean.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ static <T> Comparator<Bean<? extends T>> byPriority() {
this.result = new Pending(definition.getBeanSupplier());
}

BeanDefinition<T> getDefinition() {
return definition;
}

Class<?> getType() {
return definition.getType();
}
Expand Down
36 changes: 28 additions & 8 deletions container/src/main/java/io/smallrye/beanbag/BeanBag.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
Expand All @@ -23,27 +24,30 @@ public final class BeanBag {
final List<BeanDefinition<?>> definitions = new ArrayList<>();
final List<BeanDefinition<?>> singletonBeans = new ArrayList<>();
for (BeanBuilder<?> beanBuilder : builder.beanBuilders) {
BeanDefinition<?> definition = makeDefinition(beanBuilder);
if (beanBuilder.singleton) {
singletonBeans.add(definition);
} else {
definitions.add(definition);
}
addDefinitionsTo(beanBuilder, beanBuilder.singleton ? singletonBeans : definitions);
}
// create a copy of the non-singleton scope so singletons can inject from there
final ScopeDefinition scopeDefinition = new ScopeDefinition(List.copyOf(definitions));
singletonScope = new Scope(this, null, scopeDefinition, new ScopeDefinition(singletonBeans));
this.scopeDefinition = scopeDefinition;
}

private <T> BeanDefinition<T> makeDefinition(final BeanBuilder<T> beanBuilder) {
private <T> void addDefinitionsTo(final BeanBuilder<T> beanBuilder, List<BeanDefinition<?>> definitions) {
final String name = beanBuilder.name;
final Set<String> aliases = beanBuilder.aliases;
final Set<Class<? super T>> restrictedTypes = Set
.copyOf(Objects.requireNonNullElse(beanBuilder.restrictedTypes, List.of()));
final BeanSupplier<T> supplier = beanBuilder.supplier;
final int priority = beanBuilder.priority;
final Class<T> type = beanBuilder.type;
return new BeanDefinition<>(name, priority, type, restrictedTypes, supplier);
BeanDefinition<T> definition = new BeanDefinition<>(name, priority, type, restrictedTypes, supplier);
definitions.add(definition);
if (aliases != null) {
for (String alias : aliases) {
definitions.add(
new BeanDefinition<>(alias, priority, type, restrictedTypes, scope -> scope.requireBean(definition)));
}
}
}

/**
Expand Down Expand Up @@ -185,6 +189,7 @@ public static final class BeanBuilder<T> {
private int priority = 0;
private List<Class<? super T>> restrictedTypes;
private String name = "";
private Set<String> aliases;
private BeanSupplier<T> supplier;
private boolean singleton;

Expand Down Expand Up @@ -217,6 +222,21 @@ public BeanBuilder<T> setName(final String name) {
return this;
}

/**
* Add another name that this bean can be identified by.
*
* @param alias the bean alias (must not be {@code null})
* @return this builder (not {@code null})
*/
public BeanBuilder<T> addAlias(final String alias) {
Assert.checkNotNullParam("alias", alias);
if (aliases == null) {
aliases = new HashSet<>();
}
aliases.add(alias);
return this;
}

/**
* Set the supplier for this bean.
* Setting a supplier will overwrite a supplier created via {@link #buildSupplier()} (if any).
Expand Down
18 changes: 18 additions & 0 deletions container/src/main/java/io/smallrye/beanbag/Scope.java
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,24 @@ public <T> T getBean(final Class<T> type, final String name, final boolean optio
throw nbe;
}

<T> T requireBean(final BeanDefinition<T> definition) {
Class<T> type = definition.getType();
final List<Bean<? extends T>> beans = getBeansByType(type);
for (Bean<? extends T> bean : beans) {
if (bean.getDefinition() == definition) {
return bean.get(resolutionScope);
}
}
StringBuilder msgBuilder = new StringBuilder("No matching bean available: type is ");
msgBuilder.append(type);
String name = definition.getName();
if (!name.isEmpty()) {
msgBuilder.append(", name is \"").append(name).append('"');
}
final NoSuchBeanException nbe = new NoSuchBeanException(msgBuilder.toString());
throw nbe;
}

public BeanBag getContainer() {
return container;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public void testWagonThings() {
mavenFactory.getContainer().requireBean(Wagon.class);
mavenFactory.getContainer().requireBean(Wagon.class, "file");
mavenFactory.getContainer().requireBean(Wagon.class, "http");
mavenFactory.getContainer().requireBean(Wagon.class, "https");

HttpWagon wagon = mavenFactory.getContainer().requireBean(HttpWagon.class);
assertNotNull(wagon);
Expand Down
93 changes: 81 additions & 12 deletions sisu/src/main/java/io/smallrye/beanbag/sisu/Sisu.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,18 @@ private void parseComponentSet(final XMLStreamReader xr, final ClassLoader class

private void parseComponents(final XMLStreamReader xr, final ClassLoader classLoader, final DependencyFilter filter)
throws XMLStreamException {
Map<Class<?>, Component<?>> map = new HashMap<>();
while (xr.hasNext()) {
switch (xr.next()) {
case XMLStreamReader.END_ELEMENT: {
for (Component<?> component : map.values()) {
addBeanFromXml(component, filter, classLoader);
}
return;
}
case XMLStreamReader.START_ELEMENT: {
if (xr.getLocalName().equals("component")) {
parseComponent(xr, classLoader, filter);
parseComponent(xr, map, classLoader, filter);
} else {
consume(xr);
}
Expand All @@ -181,7 +185,8 @@ private void parseComponents(final XMLStreamReader xr, final ClassLoader classLo
}
}

private void parseComponent(final XMLStreamReader xr, final ClassLoader classLoader, final DependencyFilter filter)
private void parseComponent(final XMLStreamReader xr, final Map<Class<?>, Component<?>> map, final ClassLoader classLoader,
final DependencyFilter filter)
throws XMLStreamException {

Class<?> clazz = null;
Expand Down Expand Up @@ -281,8 +286,44 @@ private void parseComponent(final XMLStreamReader xr, final ClassLoader classLoa
}
}

// add the bean the plexus way
addBeanFromXml(clazz, type, name, singleton, filter, requirements, classLoader);
Component<?> component = map.get(clazz);
if (component == null) {
component = Component.of(clazz, type, name, singleton, requirements);
map.put(clazz, component);
} else {
assert clazz == component.clazz;
if (component.types != null) {
component.types.add((Class) type);
}
// else already unrestricted; ignore new type restriction
if (name != null && !name.equals(component.name)) {
Set<String> aliases = component.aliases;
if (aliases == null) {
aliases = component.aliases = new HashSet<>();
}
aliases.add(name);
}
component.requirements = concatenate(component.requirements, requirements);
}
}

static <T> List<T> concatenate(List<T> a, List<T> b) {
if (a.isEmpty()) {
if (b.isEmpty()) {
return List.of();
} else {
return b;
}
} else {
if (b.isEmpty()) {
return a;
} else {
ArrayList<T> out = new ArrayList<>(a.size() + b.size());
out.addAll(a);
out.addAll(b);
return List.copyOf(out);
}
}
}

private List<Requirement> parseRequirements(final XMLStreamReader xr, final ClassLoader classLoader,
Expand Down Expand Up @@ -352,28 +393,56 @@ private Requirement parseRequirement(final XMLStreamReader xr, final ClassLoader
return null;
}

static final class Component<T> {
final Class<T> clazz;
Set<Class<? super T>> types;
String name;
Set<String> aliases;
boolean singleton;
List<Requirement> requirements;

Component(final Class<T> clazz) {
this.clazz = clazz;
}

public static <T> Component<T> of(final Class<?> clazz, final Class<?> type, final String name, final boolean singleton,
final List<Requirement> requirements) {
Component<T> c = new Component<>((Class<T>) clazz);
if (type != null && type != clazz) {
c.types = new HashSet<>(Set.of((Class<? super T>) type));
}
c.name = name;
c.singleton = singleton;
c.requirements = requirements;
return c;
}
}

static final class Requirement {
String injectType;
String injectName;
String fieldName;
}

private <T> void addBeanFromXml(final Class<T> clazz, final Class<?> type, final String named, final boolean singleton,
final DependencyFilter filter, List<Requirement> injections, final ClassLoader classLoader) {
private <T> void addBeanFromXml(Component<T> component, final DependencyFilter filter, final ClassLoader classLoader) {
Class<T> clazz = component.clazz;
if (!visited.add(clazz)) {
// duplicate
return;
}
final BeanBag.BeanBuilder<T> beanBuilder = builder.addBean(clazz);
final Annotations clazzAnnotations = Annotations.of(clazz);
if (singleton) {
if (component.singleton) {
beanBuilder.setSingleton(true);
}
if (named != null && !named.isEmpty()) {
beanBuilder.setName(named);
if (component.name != null && !component.name.isEmpty()) {
beanBuilder.setName(component.name);
}
if (component.aliases != null) {
component.aliases.forEach(beanBuilder::addAlias);
}
if (type != null && !type.equals(clazz)) {
beanBuilder.addRestrictedTypes(List.of((Class<? super T>) type));
if (component.types != null) {
beanBuilder.addRestrictedTypes(List.copyOf(component.types));
}
final BeanBag.SupplierBuilder<T> supplierBuilder = beanBuilder.buildSupplier();
// despite being a legacy component, there's no reason why we couldn't inject things like normal
Expand Down Expand Up @@ -416,7 +485,7 @@ private <T> void addBeanFromXml(final Class<T> clazz, final Class<?> type, final
}

// now add our manual injections
for (Requirement req : injections) {
for (Requirement req : component.requirements) {
String fieldName = req.fieldName;
if (fieldName == null) {
continue;
Expand Down