1package com.mskinstitute.capstone3;
2
3import java.util.HashMap;
4import java.util.Map;
5import java.util.Optional;
6import java.util.concurrent.*;
7import java.util.concurrent.locks.ReentrantReadWriteLock;
8
9public class ConcurrentTtlCache<K, V> implements AutoCloseable {
10
11 // Internal wrapper for cached entries
12 private static class CacheEntry<V> {
13 final V value;
14 final long expiryTimestamp;
15
16 CacheEntry(V value, long ttlMillis) {
17 this.value = value;
18 this.expiryTimestamp = System.currentTimeMillis() + ttlMillis;
19 }
20
21 boolean isExpired() {
22 return System.currentTimeMillis() > expiryTimestamp;
23 }
24 }
25
26 private final Map<K, CacheEntry<V>> cacheMap = new HashMap<>();
27 private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
28 private final ScheduledExecutorService cleanerService = Executors.newSingleThreadScheduledExecutor(r -> {
29 Thread t = new Thread(r, "Cache-Eviction-Daemon");
30 t.setDaemon(true); // Daemon thread exits with JVM
31 return t;
32 });
33
34 public ConcurrentTtlCache(long cleanupIntervalSeconds) {
35 cleanerService.scheduleAtFixedRate(this::evictExpiredEntries,
36 cleanupIntervalSeconds, cleanupIntervalSeconds, TimeUnit.SECONDS);
37 }
38
39 /**
40 * Puts a key-value pair into the cache with a specific TTL.
41 */
42 public void put(K key, V value, long ttlMillis) {
43 rwLock.writeLock().lock();
44 try {
45 cacheMap.put(key, new CacheEntry<>(value, ttlMillis));
46 } finally {
47 rwLock.writeLock().unlock();
48 }
49 }
50
51 /**
52 * Retrieves an item from the cache if present and unexpired.
53 */
54 public Optional<V> get(K key) {
55 rwLock.readLock().lock();
56 try {
57 CacheEntry<V> entry = cacheMap.get(key);
58 if (entry == null || entry.isExpired()) {
59 return Optional.empty();
60 }
61 return Optional.of(entry.value);
62 } finally {
63 rwLock.readLock().unlock();
64 }
65 }
66
67 /**
68 * Purges expired keys from memory.
69 */
70 private void evictExpiredEntries() {
71 rwLock.writeLock().lock();
72 try {
73 cacheMap.entrySet().removeIf(e -> e.getValue().isExpired());
74 } finally {
75 rwLock.writeLock().unlock();
76 }
77 }
78
79 @Override
80 public void close() {
81 cleanerService.shutdownNow();
82 }
83
84 public static void main(String[] args) throws Exception {
85 try (ConcurrentTtlCache<String, String> sessionCache = new ConcurrentTtlCache<>(2)) {
86 System.out.println("🚀 Initializing Concurrent TTL Cache...");
87
88 // Store user session with 2 second TTL
89 sessionCache.put("SESSION_USR_101", "Aarav Sharma [ADMIN]", 2000);
90 System.out.println("Saved: SESSION_USR_101 (TTL: 2s)");
91
92 // Immediate read
93 Optional<String> session = sessionCache.get("SESSION_USR_101");
94 System.out.println("Immediate Read: " + session.orElse("EXPIRED"));
95
96 // Wait 2.5 seconds for TTL expiry
97 System.out.println("Sleeping 2500ms...");
98 Thread.sleep(2500);
99
100 // Read after expiration
101 Optional<String> expiredSession = sessionCache.get("SESSION_USR_101");
102 System.out.println("Post-Expiry Read: " + expiredSession.orElse("EXPIRED"));
103 }
104 }
105}