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