1+ package io .warmup .framework .cache ;
2+
3+ import java .io .Serializable ;
4+ import java .security .MessageDigest ;
5+ import java .util .concurrent .ConcurrentHashMap ;
6+ import java .util .concurrent .ConcurrentMap ;
7+ import java .util .logging .Level ;
8+ import java .util .logging .Logger ;
9+
10+ /**
11+ * ASM Cache Manager para el framework Warmup.
12+ * Gestiona el cache de bytecode generado por ASM para optimizar el rendimiento JIT.
13+ *
14+ * @author MiniMax Agent
15+ * @since 2.0
16+ */
17+ public class ASMCacheManager implements Serializable {
18+
19+ private static final Logger log = Logger .getLogger (ASMCacheManager .class .getName ());
20+ private static final long serialVersionUID = 1L ;
21+
22+ private static ASMCacheManager instance ;
23+
24+ // Cache principal: cacheKey -> CacheEntry
25+ private final ConcurrentMap <String , CacheEntry > bytecodeCache ;
26+
27+ // Estadísticas del cache
28+ private long hits = 0 ;
29+ private long misses = 0 ;
30+ private long entries = 0 ;
31+
32+ /**
33+ * Constructor privado para patrón Singleton
34+ */
35+ private ASMCacheManager () {
36+ this .bytecodeCache = new ConcurrentHashMap <>();
37+ log .log (Level .INFO , "ASMCacheManager inicializado" );
38+ }
39+
40+ /**
41+ * Obtiene la instancia singleton del ASMCacheManager
42+ */
43+ public static synchronized ASMCacheManager getInstance () {
44+ if (instance == null ) {
45+ instance = new ASMCacheManager ();
46+ }
47+ return instance ;
48+ }
49+
50+ /**
51+ * Calcula el hash del código fuente original
52+ */
53+ public String calculateSourceHash (byte [] originalClassData ) {
54+ try {
55+ MessageDigest digest = MessageDigest .getInstance ("SHA-256" );
56+ byte [] hash = digest .digest (originalClassData );
57+
58+ // Convertir a hexadecimal
59+ StringBuilder hexString = new StringBuilder ();
60+ for (byte b : hash ) {
61+ String hex = Integer .toHexString (0xff & b );
62+ if (hex .length () == 1 ) {
63+ hexString .append ('0' );
64+ }
65+ hexString .append (hex );
66+ }
67+ return hexString .toString ();
68+ } catch (Exception e ) {
69+ log .log (Level .WARNING , "Error calculando hash de fuente: {0}" , e .getMessage ());
70+ return "error-hash" ;
71+ }
72+ }
73+
74+ /**
75+ * Obtiene el bytecode cacheado para una clave específica
76+ */
77+ public byte [] getCachedBytecode (String cacheKey , String sourceHash ) {
78+ CacheEntry entry = bytecodeCache .get (cacheKey );
79+ if (entry == null ) {
80+ misses ++;
81+ return null ;
82+ }
83+
84+ // Verificar si el hash coincide (invalidación por cambios en el código fuente)
85+ if (!entry .getSourceHash ().equals (sourceHash )) {
86+ bytecodeCache .remove (cacheKey );
87+ entries --;
88+ misses ++;
89+ return null ;
90+ }
91+
92+ hits ++;
93+ log .log (Level .FINE , "Cache HIT para clave: {0}" , cacheKey );
94+ return entry .getBytecode ();
95+ }
96+
97+ /**
98+ * Guarda bytecode en el cache
99+ */
100+ public void cacheBytecode (String cacheKey , String sourceHash , byte [] bytecode ) {
101+ CacheEntry entry = new CacheEntry (sourceHash , bytecode );
102+ CacheEntry previous = bytecodeCache .put (cacheKey , entry );
103+ if (previous == null ) {
104+ entries ++;
105+ }
106+
107+ log .log (Level .FINE , "Cache guardado para clave: {0}, entradas totales: {1}" ,
108+ new Object []{cacheKey , entries });
109+ }
110+
111+ /**
112+ * Invalida una entrada específica del cache
113+ */
114+ public void invalidate (String cacheKey ) {
115+ CacheEntry removed = bytecodeCache .remove (cacheKey );
116+ if (removed != null ) {
117+ entries --;
118+ log .log (Level .FINE , "Entrada invalidada del cache: {0}" , cacheKey );
119+ }
120+ }
121+
122+ /**
123+ * Limpia todo el cache
124+ */
125+ public void clearCache () {
126+ int cleared = bytecodeCache .size ();
127+ bytecodeCache .clear ();
128+ entries = 0 ;
129+ log .log (Level .INFO , "Cache limpiado completamente. Entradas eliminadas: {0}" , cleared );
130+ }
131+
132+ /**
133+ * Obtiene las estadísticas del cache
134+ */
135+ public void getStats () {
136+ long total = hits + misses ;
137+ double hitRate = total > 0 ? (double ) hits / total * 100 : 0 ;
138+
139+ String stats = String .format (
140+ "ASMCacheManager Stats - Entries: %d, Hits: %d, Misses: %d, Hit Rate: %.2f%%" ,
141+ entries , hits , misses , hitRate
142+ );
143+
144+ log .log (Level .INFO , stats );
145+ }
146+
147+ /**
148+ * Obtiene estadísticas como objeto para uso programático
149+ */
150+ public CacheStats getCacheStats () {
151+ long total = hits + misses ;
152+ double hitRate = total > 0 ? (double ) hits / total * 100 : 0 ;
153+
154+ return new CacheStats (entries , hits , misses , hitRate );
155+ }
156+
157+ /**
158+ * Clase interna para representar una entrada del cache
159+ */
160+ private static class CacheEntry implements Serializable {
161+ private static final long serialVersionUID = 1L ;
162+
163+ private final String sourceHash ;
164+ private final byte [] bytecode ;
165+ private final long timestamp ;
166+
167+ public CacheEntry (String sourceHash , byte [] bytecode ) {
168+ this .sourceHash = sourceHash ;
169+ this .bytecode = bytecode ;
170+ this .timestamp = System .currentTimeMillis ();
171+ }
172+
173+ public String getSourceHash () {
174+ return sourceHash ;
175+ }
176+
177+ public byte [] getBytecode () {
178+ return bytecode ;
179+ }
180+
181+ public long getTimestamp () {
182+ return timestamp ;
183+ }
184+ }
185+
186+ /**
187+ * Clase para representar estadísticas del cache
188+ */
189+ public static class CacheStats implements Serializable {
190+ private static final long serialVersionUID = 1L ;
191+
192+ private final long entries ;
193+ private final long hits ;
194+ private final long misses ;
195+ private final double hitRate ;
196+
197+ public CacheStats (long entries , long hits , long misses , double hitRate ) {
198+ this .entries = entries ;
199+ this .hits = hits ;
200+ this .misses = misses ;
201+ this .hitRate = hitRate ;
202+ }
203+
204+ public long getEntries () {
205+ return entries ;
206+ }
207+
208+ public long getHits () {
209+ return hits ;
210+ }
211+
212+ public long getMisses () {
213+ return misses ;
214+ }
215+
216+ public double getHitRate () {
217+ return hitRate ;
218+ }
219+
220+ @ Override
221+ public String toString () {
222+ return String .format (
223+ "CacheStats{entries=%d, hits=%d, misses=%d, hitRate=%.2f%%}" ,
224+ entries , hits , misses , hitRate
225+ );
226+ }
227+ }
228+ }
0 commit comments