Skip to content

Commit 026ed7d

Browse files
committed
#140 - Are scope requirements meant to be transitive?
If a scope depends on a parent scope and that parent scope has external dependencies - then the scope should also be able to depend on those external dependencies. Ultimately the fix for this is in ScopeInfo.providesDependency() adding in the check to see if the dependency is in requires (is an external dependency of the scope).
1 parent 13cd0ee commit 026ed7d

File tree

13 files changed

+219
-4
lines changed

13 files changed

+219
-4
lines changed

inject-generator/src/main/java/io/avaje/inject/generator/AllScopes.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,17 @@ private void writeModuleCustomServicesFile() {
6464
if (jfo != null) {
6565
Writer writer = jfo.openWriter();
6666
for (Data value : scopeAnnotations.values()) {
67-
writer.write(value.moduleFullName());
68-
writer.write("\n");
67+
final String moduleFullName = value.moduleFullName();
68+
if (moduleFullName == null) {
69+
// an empty module, custom scope with no beans
70+
final TypeElement typeElement = value.annotationType();
71+
if (typeElement != null) {
72+
context.logWarn("Empty module for "+typeElement);
73+
}
74+
} else {
75+
writer.write(moduleFullName);
76+
writer.write("\n");
77+
}
6978
}
7079
writer.close();
7180
}
@@ -120,5 +129,9 @@ void write(boolean processingOver) {
120129
String moduleFullName() {
121130
return scopeInfo.moduleFullName();
122131
}
132+
133+
TypeElement annotationType() {
134+
return scopeInfo.annotationType();
135+
}
123136
}
124137
}

inject-generator/src/main/java/io/avaje/inject/generator/ScopeInfo.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ class ScopeInfo {
5252
this.annotationType = type;
5353
}
5454

55+
@Override
56+
public String toString() {
57+
return "ScopeInfo{" +
58+
"name=" + name +
59+
", metaData=" + metaData +
60+
'}';
61+
}
62+
5563
void details(String name, Element contextElement) {
5664
if (name == null || name.isEmpty()) {
5765
final String simpleName = contextElement.getSimpleName().toString();
@@ -85,6 +93,10 @@ void initialiseName(String topPackage) throws IOException {
8593
}
8694
}
8795

96+
TypeElement annotationType() {
97+
return annotationType;
98+
}
99+
88100
JavaFileObject moduleFile() {
89101
return moduleFile;
90102
}
@@ -267,7 +279,7 @@ void buildAtInjectModule(Append writer) {
267279
writer.append("@InjectModule(");
268280
boolean leadingComma = false;
269281
if (!provides.isEmpty()) {
270-
attributeClasses(leadingComma, writer, "provides", provides);
282+
attributeClasses(false, writer, "provides", provides);
271283
leadingComma = true;
272284
}
273285
if (!requires.isEmpty()) {
@@ -357,7 +369,7 @@ boolean providedByOtherModule(String dependency) {
357369
final ScopeInfo requiredScope = scopes.get(require);
358370
if (requiredScope != null) {
359371
if (requiredScope.providesDependency(dependency)) {
360-
// context.logWarn("dependency "+dependency+" provided by other scope "+requiredScope.name);
372+
// context.logWarn("dependency " + dependency + " provided by other scope " + requiredScope.name);
361373
return true;
362374
}
363375
}
@@ -369,6 +381,9 @@ boolean providedByOtherModule(String dependency) {
369381
* Return true if this module provides the dependency.
370382
*/
371383
boolean providesDependency(String dependency) {
384+
if (requires.contains(dependency)) {
385+
return true;
386+
}
372387
for (MetaData meta : metaData.values()) {
373388
if (dependency.equals(meta.getType())) {
374389
return true;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.example.customext0;
2+
3+
@Ext0Scope
4+
public class Ext0Other {
5+
6+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.example.customext0;
2+
3+
import io.avaje.inject.InjectModule;
4+
import jakarta.inject.Scope;
5+
6+
@Scope
7+
@InjectModule(requires = {Ext0iface.class, Ext0conc.class})
8+
public @interface Ext0Scope {
9+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.example.customext0;
2+
3+
import io.avaje.inject.BeanScope;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.assertj.core.api.Assertions.assertThat;
7+
8+
class Ext0ScopeTest {
9+
10+
@Test
11+
void wire() {
12+
13+
final BeanScope scope = BeanScope.newBuilder()
14+
.withBean(Ext0iface.class, new If0())
15+
.withBean(Ext0conc.class, new Ext0conc())
16+
.withModules(new Ext0Module())
17+
.build();
18+
19+
final Ext0Other other = scope.get(Ext0Other.class);
20+
assertThat(other).isNotNull();
21+
}
22+
23+
static class If0 implements Ext0iface {
24+
25+
}
26+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.example.customext0;
2+
3+
public class Ext0conc {
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.example.customext0;
2+
3+
public interface Ext0iface {
4+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.example.customext1;
2+
3+
import org.example.customext0.Ext0Other;
4+
import org.example.customext0.Ext0conc;
5+
import org.example.customext0.Ext0iface;
6+
7+
@Ext1Scope
8+
public class Ext1Bean {
9+
10+
final Ext0iface ext0iface;
11+
final Ext0conc ext0conc;
12+
final Ext0Other ext0Other;
13+
final Ext1iface ext1iface;
14+
final Ext1conc ext1conc;
15+
16+
public Ext1Bean(Ext0iface ext0iface, Ext0conc ext0conc, Ext0Other ext0Other, Ext1iface ext1iface, Ext1conc ext1conc) {
17+
this.ext0iface = ext0iface;
18+
this.ext0conc = ext0conc;
19+
this.ext0Other = ext0Other;
20+
this.ext1iface = ext1iface;
21+
this.ext1conc = ext1conc;
22+
}
23+
24+
public Ext0iface ext0iface() {
25+
return ext0iface;
26+
}
27+
28+
public Ext0conc ext0conc() {
29+
return ext0conc;
30+
}
31+
32+
public Ext0Other ext0Other() {
33+
return ext0Other;
34+
}
35+
36+
public Ext1iface ext1iface() {
37+
return ext1iface;
38+
}
39+
40+
public Ext1conc ext1conc() {
41+
return ext1conc;
42+
}
43+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.example.customext1;
2+
3+
import io.avaje.inject.InjectModule;
4+
import jakarta.inject.Scope;
5+
import org.example.customext0.Ext0Scope;
6+
7+
@Scope
8+
@InjectModule(requires = {Ext0Scope.class, Ext1iface.class, Ext1conc.class})
9+
public @interface Ext1Scope {
10+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package org.example.customext1;
2+
3+
import io.avaje.inject.BeanScope;
4+
import org.example.customext0.Ext0Other;
5+
import org.example.customext0.Ext0conc;
6+
import org.example.customext0.Ext0iface;
7+
import org.junit.jupiter.api.Test;
8+
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
import static org.junit.jupiter.api.Assertions.assertNotNull;
11+
12+
class Ext1ScopeTest {
13+
14+
/*
15+
Unfortunately with both modules in test-classes plus this test it can not compile.
16+
So this wireParentChild() works when scopes are in src/main (and the test here in src/test).
17+
18+
@Test
19+
void wireParentChild() {
20+
21+
final BeanScope parentScope = BeanScope.newBuilder()
22+
.withBean(Ext0iface.class, new If0())
23+
.withBean(Ext0conc.class, new Ext0conc())
24+
.withModules(new Ext0Module())
25+
.build();
26+
27+
final BeanScope scope = BeanScope.newBuilder()
28+
.withBean(Ext1iface.class, new If1())
29+
.withBean(Ext1conc.class, new Ext1conc())
30+
.withModules(new Ext1Module())
31+
.withParent(parentScope)
32+
.build();
33+
34+
final Ext1Bean ext1Bean = scope.get(Ext1Bean.class);
35+
assertThat(ext1Bean).isNotNull();
36+
37+
assertNotNull(ext1Bean.ext0iface());
38+
assertNotNull(ext1Bean.ext0conc());
39+
assertNotNull(ext1Bean.ext0Other());
40+
assertNotNull(ext1Bean.ext1iface());
41+
assertNotNull(ext1Bean.ext1conc());
42+
}
43+
*/
44+
45+
@Test
46+
void wireBoth() {
47+
48+
// wire everything using only Ext1Module so simulating
49+
// Ext0Module via external dependencies
50+
final BeanScope scope = BeanScope.newBuilder()
51+
.withBean(Ext0iface.class, new If0())
52+
.withBean(Ext0conc.class, new Ext0conc())
53+
.withBean(Ext0Other.class, new Ext0Other())
54+
.withBean(Ext1iface.class, new If1())
55+
.withBean(Ext1conc.class, new Ext1conc())
56+
.withModules(new Ext1Module()) //new Ext0Module(),
57+
.build();
58+
59+
final Ext1Bean ext1Bean = scope.get(Ext1Bean.class);
60+
assertThat(ext1Bean).isNotNull();
61+
62+
assertNotNull(ext1Bean.ext0iface());
63+
assertNotNull(ext1Bean.ext0conc());
64+
assertNotNull(ext1Bean.ext0Other());
65+
assertNotNull(ext1Bean.ext1iface());
66+
assertNotNull(ext1Bean.ext1conc());
67+
}
68+
69+
static class If0 implements Ext0iface {
70+
71+
}
72+
static class If1 implements Ext1iface {
73+
74+
}
75+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.example.customext1;
2+
3+
public class Ext1conc {
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.example.customext1;
2+
3+
public interface Ext1iface {
4+
}

inject/src/main/java/io/avaje/inject/spi/DBuilder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ public <T> T get(Class<T> cls, String name) {
200200
if (!beanList.isEmpty()) {
201201
msg += ". Check @Named or Qualifier being used";
202202
}
203+
msg += ". Check for missing module? [ missing beanScopeBuilder.withModules() ]";
204+
msg += ". If it is expected to be externally provided, missing beanScopeBuilder.withBean() ?";
203205
throw new IllegalStateException(msg);
204206
}
205207
return bean;

0 commit comments

Comments
 (0)