abbiamo già visto cos’è Infinispan e come si configura una cache con JBoss AS 7. In vista degli ultimi ritocchi a Java EE 7, Infinispan tende la mano a CDI con il nuovo modulo infinispan-cdi, fornendo al momento una parziale implementazione alla specifica JCache (JSR-107) che, come suggerisce il nome, regolamenterà la gestione della cache nelle prossime applicazioni Java EE tramite semplici annotazioni (uno po’ come fa da poco Spring 3.1).
JCache: lavori in corso
Le annotazioni più importanti introdotte dalla specifica e implementate nel modulo infinispan-cdi sono:
- @CacheResult: permette di salvare nella cache il risultato di un metodo;
- @CachePut: salva il parametro di un metodo;
- @CacheRemoveEntry: rimuove un elemento dalla cache;
- @CacheRemoveAll: rimuove tutti gli elementi dalla cache.
e si usano così:
public class UserDAO { @CacheResult(cacheName="user-cache") User getUser(long id){...}; @CachePut(cacheName="user-cache") void storeUser(long id, @CacheValue User user){...}; @CacheRemoveEntry(cacheName="user-cache") void removeUser(long id){...}; @CacheRemoveAll(cacheName="user-cache") void removeAllUser(){...}; }
In puro stile CDI, queste annotazioni corrispondono a degli Interceptors, da registrare nel nostro bean.xml per essere attivati:
org.infinispan.cdi.interceptor.CacheResultInterceptor org.infinispan.cdi.interceptor.CachePutInterceptor org.infinispan.cdi.interceptor.CacheRemoveEntryInterceptor org.infinispan.cdi.interceptor.CacheRemoveAllInterceptor
Sembra tutto perfetto…ma allora qual è il problema? Il fatto è che il modulo è ancora in fase di sviluppo e la specifica stessa di JCache non è ben definita: basti pensare che le interfacce del package javax.cache sono cambiate diverse volte negli ultimi mesi; è possibile farsi un’idea della vivacità dello sviluppo dando un’occhiata alla home del progetto su GitHub.
Un altro problema che ho riscontrato sono le innumerevoli dipendenze: anche utilizzando Maven, non sono riuscito a ottenere buoni risultati provando ad integrare il tutto in JBoss 7.1.1. Inoltre, 20 jar per far funzionare una cache mi sono sembrati un po’ troppi.
Alla fine mi son detto: a me interessa solo @CacheResult (e anche subito 😉 ) per cui rimango sintonizzato in attesa di nuovi sviluppi e intanto mi reinvento la ruota ( 🙁 ) prendendo spunto da quanto ho capito come dovrebbe funzionare l’implementazione di JCache.
@CacheResult fai-da-te
Preso quindi da un impeto demiurgico, ecco quello che serve:
- una cache configurata (abbiamo come);
- un interceptor che conterrà la cache;
- una annotazione per identificare l’interceptor, che chiameremo @DailyCacheResult, in onore alla cache creata nel post precedente.
La coppia Interceptor/Interceptor Binding sarà del tipo:
@InterceptorBinding @Inherited @Target( { TYPE, METHOD }) @Retention(RUNTIME) @Documented public @interface DailyCacheResult {}
@Interceptor @DailyCacheResult public class DailyCacheInterceptor { @Inject @DailyCache private Cachecache; @AroundInvoke public Object manage(InvocationContext ic) throws Exception { String cacheKey = createKey(ic); if (cache.containsKey(cacheKey) && (cacheKey != null)) { logger.info("Values from cache for key: " + cacheKey); return cache.get(cacheKey); } else { Object result = ic.proceed(); if (result != null) { cache.put(cacheKey, result); logger.info("Caching values for key: " + cacheKey); } return result; } } }
dove la chiave della cache, generata dal metodo createKey(ic)
, può essere costituita da:
nome package + nome classe + nome metodo + valori parametri del metodo
Applicando quindi l’annotazione @DailyCacheResult ad un metodo si ottiene il risultato sperato: la nostra cache giornaliera è stata correttamente iniettata tramite l’annotazione @DaiyCache. Non si poteva iniettare direttamente il CacheContainer
nell’interceptor? Il fatto è che, almeno in JBoss 7.1.1, è bene regolare il ciclo di vita della cache “manualmente”, altrimenti ad ogni ripubblicazione dell’applicazione, senza riavviare il server, otteniamo un sacco di eccezioni in console. Questo potrebbe esser un buon caso d’uso per un EJB Singleton:
@Singleton @LocalBean @Startup public class DailyCacheConfig { @Resource(lookup = "java:jboss/infinispan/container/expiration-cache") private CacheContainer cacheContainer; @PostConstruct void startUp() { logger.info("Starting daily-cache..."); getDailyCache().start(); } @PreDestroy void shutDown() { logger.info("Stopping daily-cache..."); getDailyCache().stop(); } @Produces @DailyCache public CachegetDailyCache() { return cacheContainer.getCache("daily-cache"); } }
All’avvio e alla chiusura dell’applicativo verranno chiamati rispettivamente i metodi annotati con @PostConstruct
e @PreDestroy
, permettendo di controllare lo stato di attività della cache. Infine, il metodo getDailyCache()
farà da “factory” della nostra specifica cache, grazie al semplice qualifier:
@Qualifier @Target( { TYPE, METHOD, PARAMETER, FIELD }) @Retention(RUNTIME) @Documented public @interface DailyCache {}
Conclusioni
Non è mai un bene reinventarsi la ruota, ma in certe situazioni ne siamo costretti. Questo è semplicemente un modo che ho trovato per sfruttare la cache di Infinispan senza rinunciare all’espressività che ci darà JCache spero a breve. Rimango quindi in attesa dei prossimi sviluppi per “tornare sulla retta via!”.
Pingback: ()