23
23
import java .util .ArrayList ;
24
24
import java .util .Deque ;
25
25
import java .util .List ;
26
+ import java .util .concurrent .atomic .AtomicLong ;
26
27
import java .util .concurrent .atomic .AtomicReference ;
27
28
import java .util .function .Consumer ;
28
29
import java .util .function .Predicate ;
42
43
* @author Phillip Webb
43
44
* @author Andy Wilkinson
44
45
* @author Sam Brannen
46
+ * @author Daniel Schmidt
45
47
* @see OutputCaptureExtension
46
48
* @see OutputCaptureRule
47
49
*/
@@ -51,11 +53,14 @@ class OutputCapture implements CapturedOutput {
51
53
52
54
private @ Nullable AnsiOutputState ansiOutputState ;
53
55
54
- private final AtomicReference <String > out = new AtomicReference <>(null );
56
+ private final AtomicLong outVersion = new AtomicLong ();
57
+ private final AtomicReference <VersionedCacheResult > out = new AtomicReference <>(null );
55
58
56
- private final AtomicReference <String > err = new AtomicReference <>(null );
59
+ private final AtomicLong errVersion = new AtomicLong ();
60
+ private final AtomicReference <VersionedCacheResult > err = new AtomicReference <>(null );
57
61
58
- private final AtomicReference <String > all = new AtomicReference <>(null );
62
+ private final AtomicLong allVersion = new AtomicLong ();
63
+ private final AtomicReference <VersionedCacheResult > all = new AtomicReference <>(null );
59
64
60
65
/**
61
66
* Push a new system capture session onto the stack.
@@ -108,7 +113,7 @@ public String toString() {
108
113
*/
109
114
@ Override
110
115
public String getAll () {
111
- return get (this .all , (type ) -> true );
116
+ return get (this .all , this . allVersion , (type ) -> true );
112
117
}
113
118
114
119
/**
@@ -117,7 +122,7 @@ public String getAll() {
117
122
*/
118
123
@ Override
119
124
public String getOut () {
120
- return get (this .out , Type .OUT ::equals );
125
+ return get (this .out , this . outVersion , Type .OUT ::equals );
121
126
}
122
127
123
128
/**
@@ -126,7 +131,7 @@ public String getOut() {
126
131
*/
127
132
@ Override
128
133
public String getErr () {
129
- return get (this .err , Type .ERR ::equals );
134
+ return get (this .err , this . errVersion , Type .ERR ::equals );
130
135
}
131
136
132
137
/**
@@ -138,19 +143,24 @@ void reset() {
138
143
}
139
144
140
145
void clearExisting () {
146
+ this .outVersion .incrementAndGet ();
141
147
this .out .set (null );
148
+ this .errVersion .incrementAndGet ();
142
149
this .err .set (null );
150
+ this .allVersion .incrementAndGet ();
143
151
this .all .set (null );
144
152
}
145
153
146
- private String get (AtomicReference <String > existing , Predicate <Type > filter ) {
154
+ private String get (AtomicReference <VersionedCacheResult > resultCache , AtomicLong version , Predicate <Type > filter ) {
147
155
Assert .state (!this .systemCaptures .isEmpty (),
148
156
"No system captures found. Please check your output capture registration." );
149
- String result = existing .get ();
150
- if ( result == null ) {
151
- result = build ( filter );
152
- existing . compareAndSet ( null , result ) ;
157
+ long currentVersion = version .get ();
158
+ VersionedCacheResult cached = resultCache . get ();
159
+ if ( cached != null && cached . version == currentVersion ) {
160
+ return cached . result ;
153
161
}
162
+ String result = build (filter );
163
+ resultCache .compareAndSet (null , new VersionedCacheResult (result , currentVersion ));
154
164
return result ;
155
165
}
156
166
@@ -162,6 +172,10 @@ String build(Predicate<Type> filter) {
162
172
return builder .toString ();
163
173
}
164
174
175
+ private record VersionedCacheResult (String result , long version ) {
176
+
177
+ }
178
+
165
179
/**
166
180
* A capture session that captures {@link System#out System.out} and {@link System#out
167
181
* System.err}.
0 commit comments