|
4 | 4 | import java.util.Optional; |
5 | 5 | import java.util.Stack; |
6 | 6 | import java.util.UUID; |
7 | | -import java.util.function.Consumer; |
8 | 7 | import java.util.function.Supplier; |
9 | 8 | import javax.inject.Inject; |
10 | 9 | import javax.inject.Singleton; |
| 10 | +import org.slf4j.Logger; |
| 11 | +import org.slf4j.LoggerFactory; |
11 | 12 |
|
12 | 13 | @Singleton |
13 | 14 | public class ContextFactory { |
14 | 15 |
|
| 16 | + private static final Logger LOGGER = LoggerFactory.getLogger(ContextFactory.class); |
| 17 | + |
15 | 18 | private final ThreadLocal<Stack<Context>> threadLocalContext; |
16 | | - private final Supplier<UUID> uuidSupplier; |
| 19 | + private final Supplier<Context> contextSupplier; |
17 | 20 |
|
18 | 21 | public ContextFactory() { |
19 | 22 | this(new DefaultUUIDSupplier()); |
20 | 23 | } |
21 | 24 |
|
22 | 25 | @Inject |
23 | 26 | public ContextFactory(Supplier<UUID> uuidSupplier) { |
24 | | - this.uuidSupplier = Objects.requireNonNull(uuidSupplier); |
25 | | - threadLocalContext = ThreadLocal.withInitial(Stack::new); |
| 27 | + Objects.requireNonNull(uuidSupplier); |
| 28 | + this.contextSupplier = () -> new Context(uuidSupplier.get()); |
| 29 | + this.threadLocalContext = ThreadLocal.withInitial(Stack::new); |
26 | 30 | } |
27 | 31 |
|
28 | 32 | /** |
@@ -54,35 +58,37 @@ public UUID contextUuid() { |
54 | 58 | * Removes the full stack of context. |
55 | 59 | */ |
56 | 60 | public void clearAllContexts() { |
| 61 | + LOGGER.info("Clearing all contexts"); // warn folks because this means something is wonky, or about to be. |
57 | 62 | threadLocalContext.remove(); |
58 | 63 | } |
59 | 64 |
|
60 | 65 | public <T> T withSupplier(Supplier<T> supplier) { |
61 | | - try { |
62 | | - threadLocalContext.get().push(generateContext()); |
63 | | - return supplier.get(); |
64 | | - } finally { |
65 | | - ifNotEmpty(Stack::pop); |
66 | | - } |
| 66 | + return withContextSet(supplier); |
67 | 67 | } |
68 | 68 |
|
69 | 69 | public void withRunnable(Runnable runnable) { |
70 | | - try { |
71 | | - threadLocalContext.get().push(generateContext()); |
| 70 | + withContextSet(() -> { |
72 | 71 | runnable.run(); |
73 | | - } finally { |
74 | | - ifNotEmpty(Stack::pop); |
75 | | - } |
| 72 | + return null; |
| 73 | + }); |
76 | 74 | } |
77 | 75 |
|
78 | | - private Context generateContext() { |
79 | | - return new Context(uuidSupplier.get()); |
80 | | - } |
81 | | - |
82 | | - private void ifNotEmpty(Consumer<Stack<Context>> consumer) { |
83 | | - Stack<Context> stack = threadLocalContext.get(); |
84 | | - if (!stack.isEmpty()) { |
85 | | - consumer.accept(stack); |
| 76 | + private <T> T withContextSet(Supplier<T> supplier) { |
| 77 | + final Context context = contextSupplier.get(); |
| 78 | + final Stack<Context> stack = threadLocalContext.get(); |
| 79 | + stack.push(context); |
| 80 | + try { |
| 81 | + return supplier.get(); |
| 82 | + } finally { |
| 83 | + if (stack.isEmpty()) { // someone cleared the stack? |
| 84 | + LOGGER.warn("Context stack is not empty after supplier execution, possible leak: {}", stack); |
| 85 | + } else { // stack isn't empty |
| 86 | + final Context old = stack.pop(); |
| 87 | + if (old != context) { // Who's been messing with the stack? |
| 88 | + LOGGER.warn("Incorrect context removed desired:{} removed:{}", context, old); |
| 89 | + } |
| 90 | + } |
86 | 91 | } |
87 | 92 | } |
| 93 | + |
88 | 94 | } |
0 commit comments