Skip to content

Commit b49da29

Browse files
committed
Add event bus and fix slow bean initialization logs
1 parent 9ae6190 commit b49da29

23 files changed

+756
-115
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414

1515
- name: Skip duplicates and docs
1616
id: skip
17-
uses: fkirc/skip-duplicate-actions@v4
17+
uses: fkirc/skip-duplicate-actions@v5
1818
with:
1919
paths_ignore: '["**/README.md", "LICENSE", ".gitignore", ".editorconfig", ".idea/**"]'
2020

build.gradle

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ group = 'com.coditory.quark'
1212
description = 'Coditory Quark Configuration Library'
1313

1414
dependencies {
15-
api 'org.slf4j:slf4j-api:2.0.0'
15+
api 'org.slf4j:slf4j-api:2.0.3'
1616
api 'org.jetbrains:annotations:23.0.0'
17-
testImplementation 'com.coditory.quark:quark-config:0.1.5'
18-
testImplementation 'ch.qos.logback:logback-classic:1.4.0'
17+
implementation 'com.coditory.quark:quark-eventbus:0.0.1'
18+
testImplementation 'com.coditory.quark:quark-config:0.1.11'
19+
testImplementation 'ch.qos.logback:logback-classic:1.4.4'
1920
testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0'
2021
testImplementation 'org.skyscreamer:jsonassert:1.5.1'
2122
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package annotated
2+
3+
import ch.qos.logback.classic.Logger
4+
import com.coditory.quark.context.Context
5+
import com.coditory.quark.context.base.CapturingAppender
6+
import org.slf4j.LoggerFactory
7+
import spock.lang.Specification
8+
9+
class SlowBeanCreationSpec extends Specification {
10+
CapturingAppender appender = new CapturingAppender()
11+
12+
void setup() {
13+
Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)
14+
rootLogger.addAppender(appender)
15+
}
16+
17+
void cleanup() {
18+
Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)
19+
rootLogger.detachAppender(appender)
20+
}
21+
22+
def "should log slow bean initialization"() {
23+
given:
24+
Context context = Context.scanPackage(annotated.samples.slow_beans.Bar)
25+
when:
26+
annotated.samples.slow_beans.Baz baz = context.get(annotated.samples.slow_beans.Baz)
27+
28+
then:
29+
List<String> logs = appender.getLogsByMessagePrefix("Slow bean creation")
30+
logs.size() == 1
31+
logs.first().startsWith("[WARN] Slow bean creation. Bean: Bar")
32+
}
33+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package annotated.samples.slow_beans
2+
3+
class Bar {
4+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package annotated.samples.slow_beans
2+
3+
class Baz {
4+
final Bar bar
5+
6+
Baz(Bar bar) {
7+
this.bar = bar
8+
}
9+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package annotated.samples.slow_beans
2+
3+
import com.coditory.quark.context.annotations.Bean
4+
import com.coditory.quark.context.annotations.Configuration
5+
6+
@Configuration
7+
class ConfigInit {
8+
@Bean
9+
annotated.samples.slow_beans.Bar bar() {
10+
Thread.sleep(1_000)
11+
return new annotated.samples.slow_beans.Bar()
12+
}
13+
14+
@Bean
15+
annotated.samples.slow_beans.Baz baz(annotated.samples.slow_beans.Bar bar) {
16+
return new annotated.samples.slow_beans.Baz(bar)
17+
}
18+
19+
@Bean
20+
annotated.samples.slow_beans.Foo foo(annotated.samples.slow_beans.Baz baz) {
21+
Thread.sleep(1_000)
22+
return new annotated.samples.slow_beans.Foo(baz)
23+
}
24+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package annotated.samples.slow_beans
2+
3+
import static java.util.Objects.requireNonNull
4+
5+
class Foo {
6+
final Baz baz
7+
8+
Foo(Baz baz) {
9+
this.baz = requireNonNull(baz)
10+
}
11+
12+
Foo(Bar bar) {
13+
throw new IllegalStateException("This constructor should not be used")
14+
}
15+
}

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,6 @@ private static void closeBean(Object bean, BeanDescriptor<?> descriptor, Method
3838
throw new BeanFinalizationException("Could not close bean: " + descriptor.toShortString() + " using method: " + simplifyMethodName(method), e);
3939
}
4040
log.debug("Closed bean {} using method {} in {}", descriptor.toShortString(), simplifyMethodName(method), timer.measureAndFormat());
41-
if (timer.isOverThreshold()) {
42-
log.warn("Detected long bean close operation. Bean: {}, Method: {}, Time: {}", descriptor.toShortString(), simplifyMethodName(method), timer.measureAndFormat());
43-
}
4441
}
4542

4643
private static void closeBean(Closeable bean, BeanDescriptor<?> descriptor) {
@@ -51,8 +48,5 @@ private static void closeBean(Closeable bean, BeanDescriptor<?> descriptor) {
5148
throw new BeanFinalizationException("Could not close bean: " + descriptor.toShortString(), e);
5249
}
5350
log.debug("Closed bean {} in {}", descriptor.toShortString(), timer.measureAndFormat());
54-
if (timer.isOverThreshold()) {
55-
log.warn("Detected long bean close operation. Bean: {}, Time: {}", descriptor.toShortString(), timer.measureAndFormat());
56-
}
5751
}
5852
}

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

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.coditory.quark.context;
22

3+
import com.coditory.quark.eventbus.EventEmitter;
34
import org.jetbrains.annotations.Nullable;
45
import org.slf4j.Logger;
56
import org.slf4j.LoggerFactory;
@@ -25,6 +26,7 @@ static <T> BeanHolder<T> holder(BeanDescriptor<T> descriptor, BeanCreator<T> cre
2526
private final BeanDescriptor<T> descriptor;
2627
private final Set<Class<?>> classHierarchy;
2728
private final boolean eager;
29+
private EventEmitter eventEmitter;
2830
private T bean;
2931
private boolean initialized = false;
3032
private boolean closed = false;
@@ -36,6 +38,13 @@ private BeanHolder(BeanDescriptor<T> descriptor, BeanCreator<T> creator, boolean
3638
this.eager = eager;
3739
}
3840

41+
void setEventEmitter(EventEmitter eventEmitter) {
42+
if (this.eventEmitter != null) {
43+
throw new IllegalStateException("EventEmitter was already set");
44+
}
45+
this.eventEmitter = requireNonNull(eventEmitter);
46+
}
47+
3948
public boolean isEager() {
4049
return eager;
4150
}
@@ -79,18 +88,21 @@ T getCached() {
7988
}
8089

8190
boolean isActive(ConditionContext context) {
82-
Timer timer = Timer.start();
91+
expectEventEmitter();
92+
eventEmitter.emit(new ContextEvent.BeanPreIsActiveCheckEvent(descriptor));
8393
boolean result = creator.isActive(context);
84-
if (timer.isOverThreshold()) {
85-
log.warn("Detected long bean condition check. Bean: {}, Time: {}", descriptor.toShortString(), timer.measureAndFormat());
86-
}
94+
eventEmitter.emit(new ContextEvent.BeanPostIsActiveCheckEvent(descriptor));
8795
return result;
8896
}
8997

9098
@Nullable
9199
public T get(ResolutionContext context) {
92100
if (bean == null) {
101+
expectEventEmitter();
102+
ResolutionPath path = context.getResolutionPath();
103+
eventEmitter.emit(new ContextEvent.BeanPreCreateEvent(descriptor, path));
93104
createBean(context);
105+
eventEmitter.emit(new ContextEvent.BeanPostCreateEvent(descriptor, path));
94106
}
95107
return bean;
96108
}
@@ -102,9 +114,6 @@ private void createBean(ResolutionContext context) {
102114
throw new ContextException("Expected non-null bean: " + descriptor);
103115
}
104116
log.debug("Created bean {} in {}", descriptor.toShortString(), timer.measureAndFormat());
105-
if (timer.isOverThreshold()) {
106-
log.warn("Detected long bean creation. Bean: {}, Time: {}", descriptor.toShortString(), timer.measureAndFormat());
107-
}
108117
if (!initialized) {
109118
initializeBean(bean, descriptor, context);
110119
initialized = true;
@@ -113,8 +122,18 @@ private void createBean(ResolutionContext context) {
113122

114123
void close(ResolutionContext context) {
115124
if (!closed) {
125+
expectEventEmitter();
126+
ResolutionPath path = context.getResolutionPath();
127+
eventEmitter.emit(new ContextEvent.BeanPreCloseEvent(descriptor, path));
116128
closeBean(bean, descriptor, context);
117129
closed = true;
130+
eventEmitter.emit(new ContextEvent.BeanPostCloseEvent(descriptor, path));
131+
}
132+
}
133+
134+
private void expectEventEmitter() {
135+
if (eventEmitter == null) {
136+
throw new IllegalStateException("Expected BeanHolder to have eventEmitter");
118137
}
119138
}
120139
}

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ private static void initializeBean(Object bean, BeanDescriptor<?> descriptor, Me
3737
throw new BeanInitializationException("Could not initialize bean: " + descriptor.toShortString() + " using method: " + simplifyMethodName(method), e);
3838
}
3939
log.debug("Initialized bean {} using method {} in {}", descriptor.toShortString(), simplifyMethodName(method), timer.measureAndFormat());
40-
if (timer.isOverThreshold()) {
41-
log.warn("Detected long bean initialization. Bean: {}, Method: {}, Time: {}", descriptor.toShortString(), simplifyMethodName(method), timer.measureAndFormat());
42-
}
4340
}
4441

4542
private static void initializeBean(Initializable bean, BeanDescriptor<?> descriptor) {
@@ -50,8 +47,5 @@ private static void initializeBean(Initializable bean, BeanDescriptor<?> descrip
5047
throw new BeanInitializationException("Could not initialize bean: " + descriptor.toShortString(), e);
5148
}
5249
log.debug("Initialized bean {} in {}", descriptor.toShortString(), timer.measureAndFormat());
53-
if (timer.isOverThreshold()) {
54-
log.warn("Detected long bean initialization. Bean: {}, Time: {}", descriptor.toShortString(), timer.measureAndFormat());
55-
}
5650
}
5751
}

0 commit comments

Comments
 (0)