18
18
19
19
import java .security .Principal ;
20
20
import java .time .Duration ;
21
+ import java .util .Iterator ;
21
22
import java .util .Map ;
23
+ import java .util .Map .Entry ;
22
24
import java .util .Objects ;
23
- import java .util .concurrent .ConcurrentHashMap ;
24
25
25
26
import reactor .core .publisher .Flux ;
26
27
import reactor .core .publisher .Mono ;
30
31
import org .springframework .boot .actuate .endpoint .invoke .OperationInvoker ;
31
32
import org .springframework .util .Assert ;
32
33
import org .springframework .util .ClassUtils ;
34
+ import org .springframework .util .ConcurrentReferenceHashMap ;
33
35
import org .springframework .util .ObjectUtils ;
34
36
35
37
/**
@@ -45,6 +47,8 @@ public class CachingOperationInvoker implements OperationInvoker {
45
47
46
48
private static final boolean IS_REACTOR_PRESENT = ClassUtils .isPresent ("reactor.core.publisher.Mono" , null );
47
49
50
+ private static final int CACHE_CLEANUP_THRESHOLD = 40 ;
51
+
48
52
private final OperationInvoker invoker ;
49
53
50
54
private final long timeToLive ;
@@ -61,7 +65,7 @@ public class CachingOperationInvoker implements OperationInvoker {
61
65
Assert .isTrue (timeToLive > 0 , "TimeToLive must be strictly positive" );
62
66
this .invoker = invoker ;
63
67
this .timeToLive = timeToLive ;
64
- this .cachedResponses = new ConcurrentHashMap <>();
68
+ this .cachedResponses = new ConcurrentReferenceHashMap <>();
65
69
}
66
70
67
71
/**
@@ -78,6 +82,9 @@ public Object invoke(InvocationContext context) {
78
82
return this .invoker .invoke (context );
79
83
}
80
84
long accessTime = System .currentTimeMillis ();
85
+ if (this .cachedResponses .size () > CACHE_CLEANUP_THRESHOLD ) {
86
+ cleanExpiredCachedResponses (accessTime );
87
+ }
81
88
ApiVersion contextApiVersion = context .resolveArgument (ApiVersion .class );
82
89
Principal principal = context .resolveArgument (Principal .class );
83
90
CacheKey cacheKey = new CacheKey (contextApiVersion , principal );
@@ -90,6 +97,20 @@ public Object invoke(InvocationContext context) {
90
97
return cached .getResponse ();
91
98
}
92
99
100
+ private void cleanExpiredCachedResponses (long accessTime ) {
101
+ try {
102
+ Iterator <Entry <CacheKey , CachedResponse >> iterator = this .cachedResponses .entrySet ().iterator ();
103
+ while (iterator .hasNext ()) {
104
+ Entry <CacheKey , CachedResponse > entry = iterator .next ();
105
+ if (entry .getValue ().isStale (accessTime , this .timeToLive )) {
106
+ iterator .remove ();
107
+ }
108
+ }
109
+ }
110
+ catch (Exception ex ) {
111
+ }
112
+ }
113
+
93
114
private boolean hasInput (InvocationContext context ) {
94
115
Map <String , Object > arguments = context .getArguments ();
95
116
if (!ObjectUtils .isEmpty (arguments )) {
0 commit comments