/* * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved. */ package net.curisit.securis.utils; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** * CacheTTL *
* Simple in-memory cache with per-entry TTL (time-to-live). A background * cleaning thread periodically removes expired entries. * *
Type-safety note: Besides generic getters, this cache provides
* {@link #getSet(String, Class)} to safely retrieve {@code Set Threading: This implementation is lightweight and uses a single
* cleaner thread. The internal map is not synchronized beyond the remove loop,
* which is acceptable for low-concurrency scenarios. For heavier usage,
* consider switching to a {@code ConcurrentHashMap} and/or a scheduled executor.
*
* @author roberto
* Last reviewed by JRA on Oct 5, 2025.
*/
@ApplicationScoped
public class CacheTTL {
private static final Logger LOG = LogManager.getLogger(CacheTTL.class);
/** Default TTL (seconds) for entries when not specified. */
private static final int DEFAULT_CACHE_DURATION = 24 * 60 * 60;
/** Backing store: key → cached object + expiration. */
private final Map
* Construct a cache and start the background cleaner that removes expired
* entries every 60 seconds.
*/
@Inject
public CacheTTL() {
cleaningThread = new Thread(() -> {
while (true) {
try {
// Check for expired objects every 60 seconds
Thread.sleep(60_000);
} catch (InterruptedException e) {
LOG.warn("Cache cleaner interrupted. Clearing cache and stopping.");
data.clear();
return;
}
Date now = new Date();
List
* Store a value with an explicit TTL.
*
* @param key cache key
* @param obj value to store (may be any object, including collections)
* @param ttl TTL in seconds
*/
public void set(String key, Object obj, int ttl) {
Date expirationDate = new Date(System.currentTimeMillis() + (long) ttl * 1000L);
data.put(key, new CachedObject(expirationDate, obj));
}
/**
* set
* Store a value with the default TTL.
*
* @param key cache key
* @param obj value to store
*/
public void set(String key, Object obj) {
set(key, obj, DEFAULT_CACHE_DURATION);
}
// ---------------------------------------------------------------------
// Getters
// ---------------------------------------------------------------------
/**
* get
* Retrieve a value as {@code Object}. Returns {@code null} if not present
* or expired (expired entries are eagerly removed by the cleaner).
*
* @param key cache key
* @return cached value or null
*/
public Object get(String key) {
CachedObject co = data.get(key);
return co == null ? null : co.getObject();
}
/**
* get
* Retrieve a value and cast it to the requested type. The cast is unchecked
* due to type erasure, but localized within the cache implementation.
*
* @param key cache key
* @param type expected value type
* @param
* Retrieve a {@code Set
* Remove and return a value typed.
*
* @param key cache key
* @param type expected type
* @return removed value or null
*/
public
* Remove and return a value as {@code Object}.
*
* @param key cache key
* @return removed value or null
*/
public Object remove(String key) {
CachedObject co = data.remove(key);
return co == null ? null : co.getObject();
}
/**
* clear
* Remove all entries from the cache.
*/
public void clear() {
data.clear();
}
// ---------------------------------------------------------------------
// Internal structure
// ---------------------------------------------------------------------
/**
* CachedObject
*
* Internal wrapper that pairs an arbitrary object with its expiration date.
*/
private static class CachedObject {
private final Date expireAt;
private final Object object;
/**
* Constructor
* Set expiration and payload.
*
* @param date
* @param object
*/
public CachedObject(Date date, Object obj) {
this.expireAt = date;
this.object = obj;
}
/**
* getExpireAt
* Return expiration date.
*
* @return date
*/
public Date getExpireAt() {
return expireAt;
}
/**
* getObject
* Return payload as {@code Object}.
*
* @return object
*/
public Object getObject() {
return object;
}
/**
* getObject
* Return payload cast to the requested type. Cast is localized here.
*
* @param type requested type
* @param