Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Commit d7ff7b8

Browse files
committed
Add support for start/stop lifecycle callbacks fix jooby-project#41
* by implementing org.jooby.Managed * with @PostConstruct @PreDestroy annotations
1 parent 8788083 commit d7ff7b8

File tree

19 files changed

+1243
-211
lines changed

19 files changed

+1243
-211
lines changed

coverage-report/pom.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,6 @@
204204
<version>${project.version}</version>
205205
</dependency>
206206

207-
<!-- H2 database -->
208-
<dependency>
209-
<groupId>com.h2database</groupId>
210-
<artifactId>h2</artifactId>
211-
</dependency>
212-
213207
<!-- Test dependencies -->
214208
<dependency>
215209
<groupId>org.jooby</groupId>
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package org.jooby;
2+
3+
import java.util.concurrent.CountDownLatch;
4+
5+
import javax.annotation.PostConstruct;
6+
import javax.inject.Singleton;
7+
8+
import org.jooby.test.ServerFeature;
9+
import org.junit.Test;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
public class PostConstructFeature extends ServerFeature {
14+
15+
/** The logging system. */
16+
private static final Logger log = LoggerFactory.getLogger(PostConstructFeature.class);
17+
18+
private static CountDownLatch counter;
19+
20+
public static class ManagedObject {
21+
22+
@PostConstruct
23+
public void start() throws Exception {
24+
log.info("starting: {}", getClass().getName());
25+
counter.countDown();
26+
}
27+
28+
}
29+
30+
@Singleton
31+
public static class SingletonObject {
32+
33+
@PostConstruct
34+
public void start() throws Exception {
35+
log.info("starting: {}", this);
36+
counter.countDown();
37+
}
38+
39+
}
40+
41+
public static class ManagedExObject {
42+
43+
@PostConstruct
44+
public void start() throws Exception {
45+
throw new Exception("intentional err");
46+
}
47+
48+
}
49+
50+
{
51+
52+
get("/proto", req -> {
53+
int n = req.param("n").intValue();
54+
for (int i = 0; i < n; i++) {
55+
req.require(ManagedObject.class);
56+
}
57+
return "OK";
58+
});
59+
60+
get("/singleton", req -> {
61+
int n = req.param("n").intValue();
62+
for (int i = 0; i < n; i++) {
63+
req.require(SingletonObject.class);
64+
}
65+
return "OK";
66+
});
67+
68+
get("/protoex", req ->
69+
req.require(ManagedExObject.class));
70+
}
71+
72+
@Test
73+
public void startOneProto() throws Exception {
74+
counter = new CountDownLatch(1);
75+
request()
76+
.get("/proto?n=" + counter.getCount())
77+
.expect("OK");
78+
counter.await();
79+
}
80+
81+
@Test
82+
public void startNSingleton() throws Exception {
83+
counter = new CountDownLatch(1);
84+
request()
85+
.get("/singleton?n=7")
86+
.expect("OK");
87+
counter.await();
88+
}
89+
90+
@Test
91+
public void startTwoProto() throws Exception {
92+
counter = new CountDownLatch(2);
93+
request()
94+
.get("/proto?n=" + counter.getCount())
95+
.expect("OK");
96+
counter.await();
97+
}
98+
99+
@Test
100+
public void startNProto() throws Exception {
101+
counter = new CountDownLatch(5);
102+
request()
103+
.get("/proto?n=" + counter.getCount())
104+
.expect("OK");
105+
counter.await();
106+
}
107+
108+
@Test
109+
public void startWithCheckedException() throws Exception {
110+
request()
111+
.get("/protoex")
112+
.expect(500);
113+
}
114+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package org.jooby;
2+
3+
import static org.junit.Assert.assertEquals;
4+
5+
import java.util.concurrent.atomic.AtomicInteger;
6+
7+
import javax.annotation.PreDestroy;
8+
import javax.inject.Provider;
9+
import javax.inject.Singleton;
10+
11+
import org.jooby.internal.LifecycleProcessor;
12+
import org.junit.Test;
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
16+
import com.google.inject.Guice;
17+
import com.google.inject.Injector;
18+
19+
public class PreDestroyFeature {
20+
21+
/** The logging system. */
22+
private static final Logger log = LoggerFactory.getLogger(PreDestroyFeature.class);
23+
24+
private static AtomicInteger counter;
25+
26+
public static class ManagedObject {
27+
28+
@PreDestroy
29+
public void stop() throws Exception {
30+
log.info("stopping: {}", getClass().getName());
31+
counter.incrementAndGet();
32+
}
33+
34+
}
35+
36+
@Singleton
37+
public static class SingletonObject {
38+
39+
40+
@PreDestroy
41+
public void stop() throws Exception {
42+
log.info("stopping: {}", getClass().getName());
43+
counter.incrementAndGet();
44+
}
45+
46+
}
47+
48+
49+
public static class SingletonProvider<T> implements Provider<T>, Managed {
50+
51+
private T value;
52+
53+
public SingletonProvider(final T value) {
54+
this.value = value;
55+
}
56+
57+
@Override
58+
public void start() throws Exception {
59+
}
60+
61+
@Override
62+
public void stop() throws Exception {
63+
log.info("stopping: {}", getClass().getName());
64+
counter.incrementAndGet();
65+
}
66+
67+
@Override
68+
public T get() {
69+
return value;
70+
}
71+
72+
}
73+
74+
@Test
75+
public void noStopForProto() throws Exception {
76+
counter = new AtomicInteger(0);
77+
78+
Injector injector = Guice.createInjector();
79+
80+
injector.getInstance(ManagedObject.class);
81+
injector.getInstance(ManagedObject.class);
82+
injector.getInstance(ManagedObject.class);
83+
84+
LifecycleProcessor.onPreDestroy(injector, log);
85+
86+
assertEquals(0, counter.get());
87+
}
88+
89+
@Test
90+
public void stopShouldWorkOnSingletonObjects() throws Exception {
91+
counter = new AtomicInteger(0);
92+
93+
Injector injector = Guice.createInjector();
94+
95+
injector.getInstance(SingletonObject.class);
96+
97+
LifecycleProcessor.onPreDestroy(injector, log);
98+
99+
assertEquals(1, counter.get());
100+
}
101+
102+
@Test
103+
public void stopShouldWorkOnProviderOfSingleton() throws Exception {
104+
counter = new AtomicInteger(0);
105+
106+
Injector injector = Guice.createInjector(binder -> {
107+
binder.bind(Object.class).toProvider(new SingletonProvider<>(new Object())).in(Singleton.class);
108+
});
109+
110+
injector.getInstance(Object.class);
111+
112+
LifecycleProcessor.onPreDestroy(injector, log);
113+
114+
assertEquals(1, counter.get());
115+
}
116+
117+
@Test
118+
public void stopShouldBeExecutedOnlyOnce() throws Exception {
119+
counter = new AtomicInteger(0);
120+
121+
Injector injector = Guice.createInjector();
122+
123+
injector.getInstance(SingletonObject.class);
124+
injector.getInstance(SingletonObject.class);
125+
injector.getInstance(SingletonObject.class);
126+
injector.getInstance(SingletonObject.class);
127+
128+
LifecycleProcessor.onPreDestroy(injector, log);
129+
130+
assertEquals(1, counter.get());
131+
}
132+
133+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package org.jooby;
2+
3+
import java.util.concurrent.CountDownLatch;
4+
5+
import javax.inject.Singleton;
6+
7+
import org.jooby.test.ServerFeature;
8+
import org.junit.Test;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
12+
public class StartManagedObjectFeature extends ServerFeature {
13+
14+
/** The logging system. */
15+
private static final Logger log = LoggerFactory.getLogger(StartManagedObjectFeature.class);
16+
17+
private static CountDownLatch counter;
18+
19+
public static class ManagedObject implements Managed {
20+
21+
@Override
22+
public void start() throws Exception {
23+
log.info("starting: {}", getClass().getName());
24+
counter.countDown();
25+
}
26+
27+
@Override
28+
public void stop() throws Exception {
29+
}
30+
31+
}
32+
33+
public static class ManagedExObject implements Managed {
34+
35+
@Override
36+
public void start() throws Exception {
37+
throw new Exception("intentional err");
38+
}
39+
40+
@Override
41+
public void stop() throws Exception {
42+
}
43+
44+
}
45+
46+
@Singleton
47+
public static class SingletonObject implements Managed {
48+
49+
@Override
50+
public void start() throws Exception {
51+
log.info("starting: {}", this);
52+
counter.countDown();
53+
}
54+
55+
@Override
56+
public void stop() throws Exception {
57+
}
58+
59+
}
60+
61+
{
62+
63+
get("/proto", req -> {
64+
int n = req.param("n").intValue();
65+
for (int i = 0; i < n; i++) {
66+
req.require(ManagedObject.class);
67+
}
68+
return "OK";
69+
});
70+
71+
get("/protoex", req ->
72+
req.require(ManagedExObject.class)
73+
);
74+
75+
get("/singleton", req -> {
76+
int n = req.param("n").intValue();
77+
for (int i = 0; i < n; i++) {
78+
req.require(SingletonObject.class);
79+
}
80+
return "OK";
81+
});
82+
}
83+
84+
@Test
85+
public void startOneProto() throws Exception {
86+
counter = new CountDownLatch(1);
87+
request()
88+
.get("/proto?n=" + counter.getCount())
89+
.expect("OK");
90+
counter.await();
91+
}
92+
93+
@Test
94+
public void startNSingleton() throws Exception {
95+
counter = new CountDownLatch(1);
96+
request()
97+
.get("/singleton?n=7")
98+
.expect("OK");
99+
counter.await();
100+
}
101+
102+
@Test
103+
public void startTwoProto() throws Exception {
104+
counter = new CountDownLatch(2);
105+
request()
106+
.get("/proto?n=" + counter.getCount())
107+
.expect("OK");
108+
counter.await();
109+
}
110+
111+
@Test
112+
public void startNProto() throws Exception {
113+
counter = new CountDownLatch(5);
114+
request()
115+
.get("/proto?n=" + counter.getCount())
116+
.expect("OK");
117+
counter.await();
118+
}
119+
120+
@Test
121+
public void startWithCheckedException() throws Exception {
122+
request()
123+
.get("/protoex")
124+
.expect(500);
125+
}
126+
127+
}

0 commit comments

Comments
 (0)