Skip to content

Commit c3efee9

Browse files
committed
Improve exception handling
1 parent d5cc80d commit c3efee9

File tree

13 files changed

+290
-83
lines changed

13 files changed

+290
-83
lines changed

src/integration/groovy/annotated/BeanRegistrationSpec.groovy

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ class BeanRegistrationSpec extends Specification {
3333

3434
then:
3535
ContextException e = thrown(ContextException)
36-
e.message == "Could not create bean: ${annotated.samples.beans_circular_deps.Bar.class.canonicalName}"
37-
e.cause.message == "Detected circular dependency: Bar -> Baz -> Foo -> Bar"
36+
e.message == "Could not create bean: Bar"
37+
e.cause.message == "Detected cyclic dependency: Bar -> Baz -> Foo -> Bar"
3838
}
3939

4040
def "should throw error when creating bean with a self dependency"() {
@@ -45,8 +45,8 @@ class BeanRegistrationSpec extends Specification {
4545

4646
then:
4747
ContextException e = thrown(ContextException)
48-
e.message == "Could not create bean: ${annotated.samples.beans_circular_deps.BarBar.class.canonicalName}"
49-
e.cause.message == "Detected circular dependency: BarBar -> BarBar"
48+
e.message == "Could not create bean: BarBar"
49+
e.cause.message == "Detected cyclic dependency: BarBar -> BarBar"
5050
}
5151

5252
def "should inject named beans"() {

src/integration/groovy/annotated/InjectMultipleDependenciesSpec.groovy

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ class InjectMultipleDependenciesSpec extends Specification {
3535

3636
then:
3737
ContextException e = thrown(ContextException)
38-
e.message == "Could not create bean: ${annotated.samples.optional_multiple_deps.Baz.class.canonicalName}"
39-
e.cause.message == "Beans not found for type: ${annotated.samples.optional_multiple_deps.Foo.class.canonicalName}"
38+
e.message == "Could not create bean: Baz"
39+
e.cause.message == "Beans not found for type: Foo"
4040
}
4141

4242
def "should fail injecting named list of beans"() {
@@ -47,7 +47,7 @@ class InjectMultipleDependenciesSpec extends Specification {
4747

4848
then:
4949
ContextException e = thrown(ContextException)
50-
e.message == "Could not create bean: ${annotated.samples.named_multiple_deps.Bar.class.canonicalName}"
50+
e.message == "Could not create bean: Bar"
5151
e.cause.message.startsWith("Detected named @Dependency for a list of dependencies")
5252
}
5353
}

src/main/java/com/coditory/quark/context/BeanDescriptor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ public String toString() {
4343

4444
String toShortString() {
4545
return name != null
46-
? type.getCanonicalName() + "(name: " + name + ")"
47-
: type.getCanonicalName();
46+
? type.getSimpleName() + "(name: " + name + ")"
47+
: type.getSimpleName();
4848
}
4949

5050
@Override
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.coditory.quark.context;
2+
3+
public class BeanFinalizationException extends RuntimeException {
4+
public BeanFinalizationException(String message) {
5+
super(message);
6+
}
7+
8+
public BeanFinalizationException(String message, Throwable cause) {
9+
super(message, cause);
10+
}
11+
}

src/main/java/com/coditory/quark/context/BeanFinalizer.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,32 @@ private BeanFinalizer() {
1212
throw new UnsupportedOperationException("Do not instantiate utility class");
1313
}
1414

15-
static void closeBean(Object bean, ResolutionContext context) {
15+
static void closeBean(Object bean, BeanDescriptor<?> descriptor, ResolutionContext context) {
1616
if (bean instanceof Closeable) {
17-
closeBean((Closeable) bean);
17+
closeBean((Closeable) bean, descriptor);
1818
}
1919
for (Method method : bean.getClass().getDeclaredMethods()) {
2020
if (method.isAnnotationPresent(Close.class)) {
2121
method.setAccessible(true);
22-
closeBean(bean, method, context);
22+
closeBean(bean, descriptor, method, context);
2323
}
2424
}
2525
}
2626

27-
private static void closeBean(Object bean, Method method, ResolutionContext context) {
28-
Object[] args = resolveArguments(method, context);
27+
private static void closeBean(Object bean, BeanDescriptor<?> descriptor, Method method, ResolutionContext context) {
2928
try {
29+
Object[] args = resolveArguments(method, context);
3030
method.invoke(bean, args);
3131
} catch (Exception e) {
32-
throw new ContextException("Could not close bean using method: " + method, e);
32+
throw new BeanFinalizationException("Could not close bean: " + descriptor.toShortString() + " using method: " + method, e);
3333
}
3434
}
3535

36-
private static void closeBean(Closeable bean) {
36+
private static void closeBean(Closeable bean, BeanDescriptor<?> descriptor) {
3737
try {
3838
bean.close();
3939
} catch (Exception e) {
40-
throw new ContextException("Could not close bean: " + bean.getClass().getCanonicalName(), e);
40+
throw new BeanFinalizationException("Could not close bean: " + descriptor.toShortString(), e);
4141
}
4242
}
4343
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.coditory.quark.context;
2+
3+
public class BeanInitializationException extends RuntimeException {
4+
public BeanInitializationException(String message) {
5+
super(message);
6+
}
7+
8+
public BeanInitializationException(String message, Throwable cause) {
9+
super(message, cause);
10+
}
11+
}

src/main/java/com/coditory/quark/context/BeanInitializer.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,32 @@ private BeanInitializer() {
1111
throw new UnsupportedOperationException("Do not instantiate utility class");
1212
}
1313

14-
static void initializeBean(Object bean, ResolutionContext context) {
14+
static void initializeBean(Object bean, BeanDescriptor<?> descriptor, ResolutionContext context) {
1515
if (bean instanceof Initializable) {
16-
initializeBean((Initializable) bean);
16+
initializeBean((Initializable) bean, descriptor);
1717
}
1818
for (Method method : bean.getClass().getDeclaredMethods()) {
1919
if (method.isAnnotationPresent(Init.class)) {
2020
method.setAccessible(true);
21-
initializeBean(bean, method, context);
21+
initializeBean(bean, descriptor, method, context);
2222
}
2323
}
2424
}
2525

26-
private static void initializeBean(Object bean, Method method, ResolutionContext context) {
27-
Object[] args = resolveArguments(method, context);
26+
private static void initializeBean(Object bean, BeanDescriptor<?> descriptor, Method method, ResolutionContext context) {
2827
try {
28+
Object[] args = resolveArguments(method, context);
2929
method.invoke(bean, args);
3030
} catch (Exception e) {
31-
throw new ContextException("Could not initialize bean using method: " + method, e);
31+
throw new BeanInitializationException("Could not initialize bean: " + descriptor.toShortString() + " using method: " + method, e);
3232
}
3333
}
3434

35-
private static void initializeBean(Initializable bean) {
35+
private static void initializeBean(Initializable bean, BeanDescriptor<?> descriptor) {
3636
try {
3737
bean.init();
3838
} catch (Exception e) {
39-
throw new ContextException("Could not initialize bean: " + bean.getClass().getCanonicalName(), e);
39+
throw new BeanInitializationException("Could not initialize bean: " + descriptor.toShortString(), e);
4040
}
4141
}
4242
}

src/main/java/com/coditory/quark/context/Context.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ private Map<Class<?>, List<BeanHolder<?>>> groupBeanCreatorsByType(Map<BeanDescr
7171
void init() {
7272
holders.stream()
7373
.filter(BeanHolder::isCached)
74-
.forEach(creator -> {
75-
Object bean = creator.get(emptyResolutionContext);
76-
initializeBean(bean, emptyResolutionContext);
74+
.forEach(holder -> {
75+
Object bean = holder.get(emptyResolutionContext);
76+
initializeBean(bean, holder.getDescriptor(), emptyResolutionContext);
7777
});
7878
}
7979

@@ -149,7 +149,7 @@ public <T> List<T> getAll(Class<T> type) {
149149
<T> List<T> getAll(Class<T> type, ResolutionPath path) {
150150
List<T> beans = getAllOrEmpty(type, path);
151151
if (beans.isEmpty()) {
152-
throw new ContextException("Beans not found for type: " + type.getCanonicalName());
152+
throw new ContextException("Beans not found for type: " + type.getSimpleName());
153153
}
154154
return beans;
155155
}
@@ -182,23 +182,22 @@ private <T> T createBean(BeanHolder<?> holder, BeanDescriptor<T> descriptor, Res
182182
}
183183
ResolutionContext resolutionContext = new ResolutionContext(this, path);
184184
Object bean = holder.get(resolutionContext);
185-
initializeBean(bean, resolutionContext);
185+
initializeBean(bean, descriptor, resolutionContext);
186186
return (T) bean;
187187
}
188188

189189
@Override
190190
public void close() {
191-
Set<Object> closedBeans = new HashSet<>();
192-
Set<Object> createdBeans;
191+
Set<BeanHolder<?>> closedBeans = new HashSet<>();
192+
Set<BeanHolder<?>> createdBeans;
193193
do {
194194
createdBeans = holders.stream()
195195
.filter(BeanHolder::isCached)
196-
.map(BeanHolder::getCached)
197196
.collect(toUnmodifiableSet());
198197
Set.copyOf(createdBeans).stream()
199198
.filter(bean -> !closedBeans.contains(bean))
200199
.forEach(bean -> {
201-
closeBean(bean, emptyResolutionContext);
200+
closeBean(bean.getCached(), bean.getDescriptor(), emptyResolutionContext);
202201
closedBeans.add(bean);
203202
});
204203
} while (closedBeans.size() < createdBeans.size());
@@ -209,7 +208,7 @@ private Throwable simplifyException(Throwable e, ResolutionPath path) {
209208
if (!path.isEmpty()) {
210209
return e;
211210
}
212-
ContextException rootCause = Throwables.getRootCauseOfType(e, ContextException.class);
211+
CyclicDependencyException rootCause = Throwables.getRootCauseOfType(e, CyclicDependencyException.class);
213212
return rootCause == null ? e : rootCause;
214213
}
215214
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.coditory.quark.context;
2+
3+
public class CyclicDependencyException extends RuntimeException {
4+
public CyclicDependencyException(String message) {
5+
super(message);
6+
}
7+
8+
public CyclicDependencyException(String message, Throwable cause) {
9+
super(message, cause);
10+
}
11+
}

src/main/java/com/coditory/quark/context/ResolutionPath.java

Lines changed: 9 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import java.util.ArrayList;
44
import java.util.List;
5-
import java.util.Objects;
65
import java.util.stream.Collectors;
76

7+
import static com.coditory.quark.context.BeanDescriptor.descriptor;
88
import static java.util.Objects.requireNonNull;
99

1010
final class ResolutionPath {
@@ -19,12 +19,12 @@ static ResolutionPath with(Class<?> type) {
1919
}
2020

2121
static ResolutionPath with(Class<?> type, String name) {
22-
return new ResolutionPath(List.of(new ResolutionElement(type, name)));
22+
return new ResolutionPath(List.of(descriptor(type, name)));
2323
}
2424

25-
private final List<ResolutionElement> path;
25+
private final List<BeanDescriptor<?>> path;
2626

27-
private ResolutionPath(List<ResolutionElement> path) {
27+
private ResolutionPath(List<BeanDescriptor<?>> path) {
2828
requireNonNull(path);
2929
this.path = List.copyOf(path);
3030
}
@@ -34,7 +34,7 @@ boolean isEmpty() {
3434
}
3535

3636
boolean contains(Class<?> type, String name) {
37-
return path.contains(new ResolutionElement(type, name));
37+
return path.contains(descriptor(type, name));
3838
}
3939

4040
boolean contains(Class<?> type) {
@@ -50,54 +50,15 @@ ResolutionPath add(BeanDescriptor<?> descriptor) {
5050
}
5151

5252
ResolutionPath add(Class<?> type, String name) {
53-
ResolutionElement element = new ResolutionElement(type, name);
54-
List<ResolutionElement> newPath = new ArrayList<>(path);
53+
BeanDescriptor<?> element = descriptor(type, name);
54+
List<BeanDescriptor<?>> newPath = new ArrayList<>(path);
5555
newPath.add(element);
5656
if (path.contains(element)) {
5757
String circle = newPath.stream()
58-
.map(ResolutionElement::toShortString)
58+
.map(BeanDescriptor::toShortString)
5959
.collect(Collectors.joining(" -> "));
60-
throw new ContextException("Detected circular dependency: " + circle);
60+
throw new CyclicDependencyException("Detected cyclic dependency: " + circle);
6161
}
6262
return new ResolutionPath(newPath);
6363
}
64-
65-
private static final class ResolutionElement {
66-
private final Class<?> type;
67-
private final String name;
68-
69-
ResolutionElement(Class<?> type, String name) {
70-
this.type = requireNonNull(type);
71-
this.name = name;
72-
}
73-
74-
@Override
75-
public boolean equals(Object o) {
76-
if (this == o) return true;
77-
if (o == null || getClass() != o.getClass()) return false;
78-
ResolutionElement that = (ResolutionElement) o;
79-
return Objects.equals(type, that.type) && Objects.equals(name, that.name);
80-
}
81-
82-
@Override
83-
public int hashCode() {
84-
return Objects.hash(type, name);
85-
}
86-
87-
public String toShortString() {
88-
String result = type.getSimpleName();
89-
if (name != null) {
90-
result += " (" + name + ")";
91-
}
92-
return result;
93-
}
94-
95-
@Override
96-
public String toString() {
97-
return "ResolutionElement{" +
98-
"type=" + type +
99-
", name='" + name + '\'' +
100-
'}';
101-
}
102-
}
10364
}

0 commit comments

Comments
 (0)