Skip to content

Commit d611bff

Browse files
mkoubamanovotn
andcommitted
ArC: introduce Arc.requireContainer()
- introduce Arc.requireContainer() - introduce the quarkus.arc.log-no-container system property to log diagnostics when Arc.container() returns null Co-authored-by: Matej Novotny <[email protected]>
1 parent ca0c6c9 commit d611bff

File tree

3 files changed

+157
-3
lines changed

3 files changed

+157
-3
lines changed

independent-projects/arc/runtime/src/main/java/io/quarkus/arc/Arc.java

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
package io.quarkus.arc;
22

3+
import java.lang.StackWalker.StackFrame;
34
import java.util.concurrent.ExecutorService;
45
import java.util.concurrent.atomic.AtomicReference;
6+
import java.util.stream.Collectors;
7+
import java.util.stream.Stream;
8+
9+
import org.jboss.logging.Logger;
510

611
import io.quarkus.arc.impl.ArcContainerImpl;
712

@@ -12,6 +17,16 @@ public final class Arc {
1217

1318
private static final AtomicReference<ArcContainerImpl> INSTANCE = new AtomicReference<>();
1419

20+
/**
21+
* If this system property is set then log a warning with diagnostic information when {@link Arc#container()} returns
22+
* {@code null}.
23+
*
24+
* @see #container()
25+
*/
26+
private static final String LOG_NO_CONTAINER = "quarkus.arc.log-no-container";
27+
28+
private static final Logger LOG = Logger.getLogger(Arc.class);
29+
1530
/**
1631
* Initializes {@link ArcContainer} with default settings.
1732
* This is equal to using {@code Arc#initialize(ArcInitConfig.INSTANCE)}
@@ -46,15 +61,35 @@ public static ArcContainer initialize(ArcInitConfig config) {
4661
}
4762

4863
public static void setExecutor(ExecutorService executor) {
49-
INSTANCE.get().setExecutor(executor);
64+
ArcContainerImpl container = INSTANCE.get();
65+
if (container == null) {
66+
throw containerNotInitialized();
67+
}
68+
container.setExecutor(executor);
5069
}
5170

5271
/**
5372
*
54-
* @return the container instance
73+
* @return the container instance or {@code null} if the container is not initialized
5574
*/
5675
public static ArcContainer container() {
57-
return INSTANCE.get();
76+
ArcContainer container = INSTANCE.get();
77+
if (container == null && System.getProperty(LOG_NO_CONTAINER) != null) {
78+
LOG.warn(gatherDiagnosticInfo());
79+
}
80+
return container;
81+
}
82+
83+
/**
84+
* @return the container instance
85+
* @throws IllegalStateException if the container is not initialized
86+
*/
87+
public static ArcContainer requireContainer() {
88+
ArcContainer container = INSTANCE.get();
89+
if (container == null) {
90+
throw containerNotInitialized();
91+
}
92+
return container;
5893
}
5994

6095
public static void shutdown() {
@@ -70,4 +105,38 @@ public static void shutdown() {
70105
}
71106
}
72107

108+
private static IllegalStateException containerNotInitialized() {
109+
String msg = "ArC container not initialized: the container is not started, already shut down, or a wrong class loader was used to load the io.quarkus.arc.Arc class\n"
110+
+ "\t- CL: %s\n"
111+
+ "\t- TCCL: %s";
112+
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
113+
ClassLoader cl = Arc.class.getClassLoader();
114+
return new IllegalStateException(msg.formatted(cl, tccl));
115+
}
116+
117+
private static String gatherDiagnosticInfo() {
118+
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
119+
ClassLoader cl = Arc.class.getClassLoader();
120+
String msg = "\n"
121+
+ "==============================\n"
122+
+ "ArC: container not initialized\n"
123+
+ "------------------------------\n"
124+
+ "The container is not started, already shut down, or a wrong class loader was used to load the io.quarkus.arc.Arc class.\n"
125+
+ "\n"
126+
+ "CL: %1$s\n"
127+
+ "TCCL: %2$s\n"
128+
+ "Stack:\n\t%3$s"
129+
+ "\n"
130+
+ "===================================\n";
131+
StackWalker walker = StackWalker.getInstance();
132+
return msg.formatted(cl, tccl, walker.walk(Arc::collectStack));
133+
}
134+
135+
private static String collectStack(Stream<StackFrame> stream) {
136+
return stream
137+
.skip(1)
138+
.map(Object::toString)
139+
.collect(Collectors.joining("\n\t"));
140+
}
141+
73142
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.quarkus.arc.impl;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNull;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
8+
import java.util.List;
9+
import java.util.logging.LogRecord;
10+
import java.util.logging.Logger;
11+
12+
import org.junit.jupiter.api.Test;
13+
14+
import io.quarkus.arc.Arc;
15+
16+
public class ArcContainerTest {
17+
18+
@Test
19+
public void testContainer() {
20+
InMemoryLogHandler handler = new InMemoryLogHandler();
21+
Logger rootLogger = Logger.getLogger("");
22+
rootLogger.addHandler(handler);
23+
try {
24+
System.clearProperty("quarkus.arc.log-no-container");
25+
// By default, no action is performed
26+
assertNull(Arc.container());
27+
assertTrue(handler.records.isEmpty());
28+
29+
IllegalStateException ise = assertThrows(IllegalStateException.class, () -> Arc.requireContainer());
30+
assertTrue(ise.getMessage().contains("ArC container not initialized"));
31+
32+
// quarkus.arc.log-no-containe=true -> log a warning
33+
System.setProperty("quarkus.arc.log-no-container", "true");
34+
assertNull(Arc.container());
35+
List<LogRecord> records = handler.getRecords();
36+
assertEquals(1, records.size());
37+
LogRecord record = records.get(0);
38+
assertTrue(record.getMessage().contains("ArC: container not initialized"));
39+
} finally {
40+
System.clearProperty("quarkus.arc.log-no-container");
41+
handler.clearRecords();
42+
rootLogger.removeHandler(handler);
43+
}
44+
}
45+
46+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.quarkus.arc.impl;
2+
3+
import java.util.List;
4+
import java.util.concurrent.CopyOnWriteArrayList;
5+
import java.util.logging.Handler;
6+
import java.util.logging.Level;
7+
import java.util.logging.LogRecord;
8+
9+
public class InMemoryLogHandler extends Handler {
10+
11+
final List<LogRecord> records = new CopyOnWriteArrayList<>();
12+
13+
@Override
14+
public void publish(LogRecord record) {
15+
records.add(record);
16+
}
17+
18+
@Override
19+
public void flush() {
20+
}
21+
22+
@Override
23+
public Level getLevel() {
24+
return Level.FINE;
25+
}
26+
27+
@Override
28+
public void close() throws SecurityException {
29+
this.records.clear();
30+
}
31+
32+
public List<LogRecord> getRecords() {
33+
return records;
34+
}
35+
36+
void clearRecords() {
37+
this.records.clear();
38+
}
39+
}

0 commit comments

Comments
 (0)