Skip to content

Commit 87f089a

Browse files
committed
Intermediate commit #4159
- introduced pre-cache - refactored self cleaning cache - added new fields
1 parent 0587392 commit 87f089a

File tree

11 files changed

+322
-107
lines changed

11 files changed

+322
-107
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.mercedesbenz.sechub.commons.core.cache;
2+
3+
import static java.util.Objects.*;
4+
5+
import java.io.Serializable;
6+
import java.time.Duration;
7+
import java.time.Instant;
8+
9+
import javax.crypto.SealedObject;
10+
11+
import com.mercedesbenz.sechub.commons.core.security.CryptoAccess;
12+
import com.mercedesbenz.sechub.commons.core.security.CryptoAccessProvider;
13+
14+
/**
15+
* Represents the data stored in the cache under a specific key.
16+
*
17+
* <p>
18+
* The cached data can be any serializable object. It is securely sealed using a
19+
* {@link CryptoAccess} instance of type <code>T</code>.
20+
* </p>
21+
*/
22+
class CacheData<T extends Serializable> {
23+
24+
private final CryptoAccessProvider<T> cryptoAccessProvider;
25+
private final SealedObject sealedValue;
26+
private final Duration duration;
27+
private final Instant createdAt = Instant.now();
28+
29+
public CacheData(CryptoAccessProvider<T> cryptoAccessProvider, T value, Duration duration) {
30+
this.cryptoAccessProvider = requireNonNull(cryptoAccessProvider);
31+
requireNonNull(value, "Property 'value' must not be null");
32+
this.sealedValue = cryptoAccessProvider.getCryptoAccess().seal(value);
33+
this.duration = requireNonNull(duration, "Property 'duration' must not be null");
34+
}
35+
36+
public T getValue() {
37+
return cryptoAccessProvider.getCryptoAccess().unseal(sealedValue);
38+
}
39+
40+
public Duration getDuration() {
41+
return duration;
42+
}
43+
44+
public Instant getCreatedAt() {
45+
return createdAt;
46+
}
47+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.mercedesbenz.sechub.commons.core.cache;
2+
3+
import java.io.Serializable;
4+
import java.util.function.BiConsumer;
5+
6+
public interface CachePersistence<T extends Serializable> {
7+
8+
void forEach(BiConsumer<String, CacheData<T>> action);
9+
10+
void remove(String key);
11+
12+
void put(String key, CacheData<T> cacheData);
13+
14+
CacheData<T> get(String key);
15+
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.mercedesbenz.sechub.commons.core.cache;
2+
3+
import java.io.Serializable;
4+
import java.util.Map;
5+
import java.util.concurrent.ConcurrentHashMap;
6+
import java.util.function.BiConsumer;
7+
8+
/**
9+
* <b>Note:</b> This cache persistence is local to a single application instance
10+
* and is not shared across multiple instances. Avoid using it in scenarios
11+
* where a distributed caching mechanism is required.
12+
*
13+
* @param <T>
14+
*/
15+
public class InMemoryCachePersistence<T extends Serializable> implements CachePersistence<T> {
16+
17+
private final Map<String, CacheData<T>> cacheMap = new ConcurrentHashMap<>();
18+
19+
@Override
20+
public void remove(String key) {
21+
cacheMap.remove(key);
22+
}
23+
24+
@Override
25+
public void put(String key, CacheData<T> cacheData) {
26+
cacheMap.put(key, cacheData);
27+
}
28+
29+
@Override
30+
public void forEach(BiConsumer<String, CacheData<T>> action) {
31+
cacheMap.forEach(action);
32+
33+
}
34+
35+
@Override
36+
public CacheData<T> get(String key) {
37+
return cacheMap.get(key);
38+
}
39+
40+
}
Lines changed: 22 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
// SPDX-License-Identifier: MIT
22
package com.mercedesbenz.sechub.commons.core.cache;
33

4-
import static java.util.Objects.requireNonNull;
4+
import static java.util.Objects.*;
55

66
import java.io.Serializable;
77
import java.time.Duration;
88
import java.time.Instant;
99
import java.util.Optional;
10-
import java.util.concurrent.ConcurrentHashMap;
1110
import java.util.concurrent.ScheduledExecutorService;
1211
import java.util.concurrent.ScheduledFuture;
1312
import java.util.concurrent.TimeUnit;
1413

15-
import javax.crypto.SealedObject;
16-
1714
import com.mercedesbenz.sechub.commons.core.security.CryptoAccess;
15+
import com.mercedesbenz.sechub.commons.core.security.CryptoAccessProvider;
1816
import com.mercedesbenz.sechub.commons.core.shutdown.ApplicationShutdownHandler;
1917
import com.mercedesbenz.sechub.commons.core.shutdown.ShutdownListener;
2018

@@ -23,47 +21,45 @@
2321
* caching mechanism for generic data.
2422
*
2523
* <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
2725
* clear expired entries periodically. By default, the cache cleanup runs every
2826
* minute with an initial delay of 1 minute. These values can be customized via
2927
* the constructor.
3028
* </p>
3129
*
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-
*
3830
* @param <T> the type of data stored in the cache (must be of type
3931
* {@link Serializable})
4032
*
41-
* @author hamidonos
33+
* @author hamidonos, de-jcup
4234
*/
43-
public class InMemoryCache<T extends Serializable> implements ShutdownListener {
35+
public class SelfCleaningCache<T extends Serializable> implements ShutdownListener, CryptoAccessProvider<T> {
4436

4537
private static final Duration DEFAULT_CACHE_CLEAR_JOB_PERIOD = Duration.ofMinutes(1);
4638

47-
private final ConcurrentHashMap<String, CacheData> cacheMap = new ConcurrentHashMap<>();
4839
private final ScheduledExecutorService scheduledExecutorService;
4940
private final CryptoAccess<T> cryptoAccess = new CryptoAccess<>();
5041
private final ScheduledFuture<?> cacheClearJob;
5142
private final Duration cacheClearJobPeriod;
43+
private final CachePersistence<T> cachePersistence;
5244

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);
5547
}
5648

5749
/* @formatter:off */
58-
public InMemoryCache(Duration cacheClearJobPeriod,
50+
public SelfCleaningCache(CachePersistence<T> cachePersistence, Duration cacheClearJobPeriod,
5951
ScheduledExecutorService scheduledExecutorService,
6052
ApplicationShutdownHandler applicationShutdownHandler) {
6153
/* @formatter:on */
54+
this.cachePersistence= requireNonNull(cachePersistence, "Parameter 'cachePersistence' must not be null");
6255
this.scheduledExecutorService = requireNonNull(scheduledExecutorService, "Property 'scheduledExecutorService' must not be null");
6356
this.cacheClearJobPeriod = requireNonNull(cacheClearJobPeriod, "Property 'cacheClearJobPeriod' must not be null");
57+
6458
cacheClearJob = scheduleClearCacheJob();
59+
6560
requireNonNull(applicationShutdownHandler, "Property 'applicationShutdownHandler' must not be null");
6661
applicationShutdownHandler.register(this);
62+
6763
}
6864

6965
/**
@@ -81,7 +77,7 @@ public InMemoryCache(Duration cacheClearJobPeriod,
8177
*/
8278
public void put(String key, T value, Duration duration) {
8379
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));
8581
}
8682

8783
/**
@@ -98,7 +94,7 @@ public void put(String key, T value, Duration duration) {
9894
public Optional<T> get(String key) {
9995
requireNonNull(key, "Argument 'key' must not be null");
10096

101-
CacheData cacheData = cacheMap.get(key);
97+
CacheData<T> cacheData = cachePersistence.get(key);
10298

10399
if (cacheData == null) {
104100
return Optional.empty();
@@ -115,7 +111,8 @@ public Optional<T> get(String key) {
115111
* @throws NullPointerException if the specified key is null
116112
*/
117113
public void remove(String key) {
118-
cacheMap.remove(key);
114+
requireNonNull(key, "key must not be null!");
115+
cachePersistence.remove(key);
119116
}
120117

121118
public Duration getCacheClearJobPeriod() {
@@ -141,47 +138,19 @@ private ScheduledFuture<?> scheduleClearCacheJob() {
141138
private void clearCache() {
142139
Instant now = Instant.now();
143140

144-
cacheMap.forEach((key, value) -> {
141+
cachePersistence.forEach((key, value) -> {
145142
Instant cacheDataCreatedAt = value.getCreatedAt();
146143
Duration cacheDataDuration = value.getDuration();
147144

148145
if (cacheDataCreatedAt.plus(cacheDataDuration).isBefore(now)) {
149-
cacheMap.remove(key);
146+
cachePersistence.remove(key);
150147
}
151148
});
152149
}
153150

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;
185154
}
186155

187156
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.mercedesbenz.sechub.commons.core.security;
2+
3+
import java.io.Serializable;
4+
5+
public interface CryptoAccessProvider<T extends Serializable> {
6+
7+
public CryptoAccess<T> getCryptoAccess();
8+
}

0 commit comments

Comments
 (0)