1
1
// SPDX-License-Identifier: MIT
2
2
package com .mercedesbenz .sechub .commons .core .cache ;
3
3
4
- import static java .util .Objects .requireNonNull ;
4
+ import static java .util .Objects .* ;
5
5
6
6
import java .io .Serializable ;
7
7
import java .time .Duration ;
8
8
import java .time .Instant ;
9
9
import java .util .Optional ;
10
- import java .util .concurrent .ConcurrentHashMap ;
11
10
import java .util .concurrent .ScheduledExecutorService ;
12
11
import java .util .concurrent .ScheduledFuture ;
13
12
import java .util .concurrent .TimeUnit ;
14
13
15
- import javax .crypto .SealedObject ;
16
-
17
14
import com .mercedesbenz .sechub .commons .core .security .CryptoAccess ;
15
+ import com .mercedesbenz .sechub .commons .core .security .CryptoAccessProvider ;
18
16
import com .mercedesbenz .sechub .commons .core .shutdown .ApplicationShutdownHandler ;
19
17
import com .mercedesbenz .sechub .commons .core .shutdown .ShutdownListener ;
20
18
23
21
* caching mechanism for generic data.
24
22
*
25
23
* <p>
26
- * It uses a {@link ConcurrentHashMap } to store data and a scheduled task to
24
+ * It uses a {@link CachePersistence } to store data and a scheduled task to
27
25
* clear expired entries periodically. By default, the cache cleanup runs every
28
26
* minute with an initial delay of 1 minute. These values can be customized via
29
27
* the constructor.
30
28
* </p>
31
29
*
32
- * <p>
33
- * <b>Note:</b> This cache is local to a single application instance and is not
34
- * shared across multiple instances. Avoid using it in scenarios where a
35
- * distributed caching mechanism is required.
36
- * </p>
37
- *
38
30
* @param <T> the type of data stored in the cache (must be of type
39
31
* {@link Serializable})
40
32
*
41
- * @author hamidonos
33
+ * @author hamidonos, de-jcup
42
34
*/
43
- public class InMemoryCache <T extends Serializable > implements ShutdownListener {
35
+ public class SelfCleaningCache <T extends Serializable > implements ShutdownListener , CryptoAccessProvider < T > {
44
36
45
37
private static final Duration DEFAULT_CACHE_CLEAR_JOB_PERIOD = Duration .ofMinutes (1 );
46
38
47
- private final ConcurrentHashMap <String , CacheData > cacheMap = new ConcurrentHashMap <>();
48
39
private final ScheduledExecutorService scheduledExecutorService ;
49
40
private final CryptoAccess <T > cryptoAccess = new CryptoAccess <>();
50
41
private final ScheduledFuture <?> cacheClearJob ;
51
42
private final Duration cacheClearJobPeriod ;
43
+ private final CachePersistence <T > cachePersistence ;
52
44
53
- public InMemoryCache ( ScheduledExecutorService scheduledExecutorService , ApplicationShutdownHandler applicationShutdownHandler ) {
54
- this (DEFAULT_CACHE_CLEAR_JOB_PERIOD , scheduledExecutorService , applicationShutdownHandler );
45
+ public SelfCleaningCache ( CachePersistence < T > cachePersistence , ScheduledExecutorService scheduledExecutorService , ApplicationShutdownHandler applicationShutdownHandler ) {
46
+ this (cachePersistence , DEFAULT_CACHE_CLEAR_JOB_PERIOD , scheduledExecutorService , applicationShutdownHandler );
55
47
}
56
48
57
49
/* @formatter:off */
58
- public InMemoryCache ( Duration cacheClearJobPeriod ,
50
+ public SelfCleaningCache ( CachePersistence < T > cachePersistence , Duration cacheClearJobPeriod ,
59
51
ScheduledExecutorService scheduledExecutorService ,
60
52
ApplicationShutdownHandler applicationShutdownHandler ) {
61
53
/* @formatter:on */
54
+ this .cachePersistence = requireNonNull (cachePersistence , "Parameter 'cachePersistence' must not be null" );
62
55
this .scheduledExecutorService = requireNonNull (scheduledExecutorService , "Property 'scheduledExecutorService' must not be null" );
63
56
this .cacheClearJobPeriod = requireNonNull (cacheClearJobPeriod , "Property 'cacheClearJobPeriod' must not be null" );
57
+
64
58
cacheClearJob = scheduleClearCacheJob ();
59
+
65
60
requireNonNull (applicationShutdownHandler , "Property 'applicationShutdownHandler' must not be null" );
66
61
applicationShutdownHandler .register (this );
62
+
67
63
}
68
64
69
65
/**
@@ -81,7 +77,7 @@ public InMemoryCache(Duration cacheClearJobPeriod,
81
77
*/
82
78
public void put (String key , T value , Duration duration ) {
83
79
requireNonNull (key , "Argument 'key' must not be null" );
84
- cacheMap .put (key , new CacheData ( value , duration ));
80
+ cachePersistence .put (key , new CacheData < T >( this , value , duration ));
85
81
}
86
82
87
83
/**
@@ -98,7 +94,7 @@ public void put(String key, T value, Duration duration) {
98
94
public Optional <T > get (String key ) {
99
95
requireNonNull (key , "Argument 'key' must not be null" );
100
96
101
- CacheData cacheData = cacheMap .get (key );
97
+ CacheData < T > cacheData = cachePersistence .get (key );
102
98
103
99
if (cacheData == null ) {
104
100
return Optional .empty ();
@@ -115,7 +111,8 @@ public Optional<T> get(String key) {
115
111
* @throws NullPointerException if the specified key is null
116
112
*/
117
113
public void remove (String key ) {
118
- cacheMap .remove (key );
114
+ requireNonNull (key , "key must not be null!" );
115
+ cachePersistence .remove (key );
119
116
}
120
117
121
118
public Duration getCacheClearJobPeriod () {
@@ -141,47 +138,19 @@ private ScheduledFuture<?> scheduleClearCacheJob() {
141
138
private void clearCache () {
142
139
Instant now = Instant .now ();
143
140
144
- cacheMap .forEach ((key , value ) -> {
141
+ cachePersistence .forEach ((key , value ) -> {
145
142
Instant cacheDataCreatedAt = value .getCreatedAt ();
146
143
Duration cacheDataDuration = value .getDuration ();
147
144
148
145
if (cacheDataCreatedAt .plus (cacheDataDuration ).isBefore (now )) {
149
- cacheMap .remove (key );
146
+ cachePersistence .remove (key );
150
147
}
151
148
});
152
149
}
153
150
154
- /**
155
- * Represents the data stored in the cache under a specific key.
156
- *
157
- * <p>
158
- * The cached data can be any serializable object. It is securely sealed using a
159
- * {@link CryptoAccess} instance of type <code>T</code>.
160
- * </p>
161
- */
162
- private class CacheData {
163
-
164
- private final SealedObject sealedValue ;
165
- private final Duration duration ;
166
- private final Instant createdAt = Instant .now ();
167
-
168
- public CacheData (T value , Duration duration ) {
169
- requireNonNull (value , "Property 'value' must not be null" );
170
- this .sealedValue = InMemoryCache .this .cryptoAccess .seal (value );
171
- this .duration = requireNonNull (duration , "Property 'duration' must not be null" );
172
- }
173
-
174
- public T getValue () {
175
- return cryptoAccess .unseal (sealedValue );
176
- }
177
-
178
- public Duration getDuration () {
179
- return duration ;
180
- }
181
-
182
- public Instant getCreatedAt () {
183
- return createdAt ;
184
- }
151
+ @ Override
152
+ public CryptoAccess <T > getCryptoAccess () {
153
+ return cryptoAccess ;
185
154
}
186
155
187
156
}
0 commit comments