jueves, 17 de septiembre de 2009

Ciclo de Vida de JCS

En esta última parte acerca de JCS voy a explicar el ciclo de vida básico de un objeto guardado en cache y las diferentes clases que se ocupan para poder cumplir con cada tarea básica.

En el post anterior describí como establecer la configuración básica para tener nuestro cache listo para ser llenado con nuestros preciosos objetos.Un punto más que necesitamos dejar listo antes de comenzar nuestro ahorro de recursos es tomar en cuenta los manejadores de expiración o en inglés “Expiration Handlers”.

Un manejador de expiración es una clase encargada de ejecutar las respectivas acciones cuando un objeto termina su largo o corto periodo de vida (R.I.P.). Como creo que un código dice más que mil palabras, veamos un ejemplo.

Vamos a crear primero nuestra clase AbstractExpirationHandler la cual va ser extendida por nuestras clases de expiración “concreto”.

import java.util.HashSet;
import java.util.Set;

import org.apache.jcs.engine.CacheElement;
import org.apache.jcs.engine.control.event.ElementEvent;
import org.apache.jcs.engine.control.event.behavior.IElementEvent;
import org.apache.jcs.engine.control.event.behavior.IElementEventHandler;

public abstract class AbstractExpirationHandler implements IElementEventHandler {

private static final Set events = new HashSet();

{
events.add(ELEMENT_EVENT_EXCEEDED_IDLETIME_BACKGROUND);
events.add(ELEMENT_EVENT_EXCEEDED_IDLETIME_ONREQUEST);
events.add(ELEMENT_EVENT_EXCEEDED_MAXLIFE_BACKGROUND);
events.add(ELEMENT_EVENT_EXCEEDED_MAXLIFE_ONREQUEST);
}

public void handleElementEvent(IElementEvent elementEvent) {
ElementEvent event;
CacheElement element;

if (events.contains(elementEvent.getElementEvent())) {
event = (ElementEvent) elementEvent;
element = (CacheElement) event.getSource();
handleEvent(element);
}
}
/*To be implemented by concrete classes.*/
protected abstract void handleEvent(CacheElement element);
}

Nuestra clase concreto simplemente invocará las funciones necesarias para actualizar nuestro cache.

import org.apache.jcs.engine.CacheElement;

public class ConcreteExpirationHandler extends
AbstractExpirationHandler {


protected void handleEvent(CacheElement element) {
ConcreteKey key = (ConcreteKey) element.getKey();
/**
TODO: Update cache object identified by its key.
*/
}
}

La clase manejador concreto necesita ser asociada a una region especifica. Podemos hacer esto agregando el siguiente método a la clase CacheManager.

public static boolean addEventHandler(String region,
IElementEventHandler handler) {
boolean result = true;
JCS cache;
IElementAttributes attributes;
try {
cache = JCS.getInstance(region);
attributes = cache.getDefaultElementAttributes();
attributes.addElementEventHandler(handler);
cache.setDefaultElementAttributes(attributes);
} catch (CacheException ce) {
result = false;
}
return result;
}

Y usarla en neutro método init():

CacheManager.addEventHandler(“default”, new ConcreteExpirationHandler());

Si el lector está poniendo atención, habrá notado que se introdujo un nuevo concepto en el último pedazo de código, el elemento llave de cache (“Cache Key” en inglés). Como expliqué antes en la segunda parte de esta serie, un objeto de cache necesita ser identificado con una llave única. En un mundo simple, un objeto puede ser identificaso con una sola variable, probablemente un String, pero como vivimos en un mundo complejo, las llaves para nuestros objetos pueden ser un conjunto entero de pequeñas llaves, similar a una tabla de una base de datos que tiene una llave compuesta.

Así que para tener una llave necesitamos tener una clase con tres características básicas:
  1. Implemente la interface Serializabe
  2. Implemente equals()
  3. Implemente hascode()

import java.io.Serializable;


public class ConcreteKey implements Serializable {
private static final long serialVersionUID = 3599612490238955657L;

private String keyParam1;
private String keyParam2;

public String getKeyParam1() {
return keyParam1;
}

public void setKeyParam1(String keyParam1) {
this. keyParam1 = keyParam1;
}

public String getKeyParam2() {
return keyParam2;
}

public void setKeyParam1(String keyParam1) {
this. keyParam1 = keyParam1;
}

public boolean equals(Object obj) {
if (null == obj) {
return false;
}
if (this == obj) {
return true;
}
if (!(obj instanceof ConcreteKey)) {
return false;
}
ConcreteKey other = (ConcreteKey) obj;
boolean equals;
equals = (this.keyParam1.equals(other.keyParam1))
&& (this.keyParam2.equals(other.keyParam2));
return equals;
}

public int hashCode() {
StringBuilder builder = new StringBuilder();
builder.append(this.keyParam1).append(this.keyParam1);
String objectStr = builder.toString();
return objectStr.hashCode();
}
}

Ahora con nuestra llave podemos consultar y actualizar nuestro cache:

public class FooService {

public FooObject getFooObject(String keyParam1, String keyParam2) {
FooObject foo = null;
ConcreteKey key = new ConcreteKey();
key.setKeyParam1(keyParam1);
key.setKeyParam1(keyParam2);

JCS cache = JCS.getInstance("default");
foo = cache.get(key); // Query cache.

if (foo == null) { // Not available in cache right now?
// Get object from normal source.
foo = FooFactory.getFoo(keyParam1, keyParam2);
// Put it in cache to be available next time.
cache.put(key, foo);
}
}
}
Hasta aquí llegamos, terminé en este punto explicando los pasos básicos para configurar el cache, pero hay un montón de opciones más a explorar como auxiliares de cache, la consola de administración, etc. Si tienen alguna pregunta no duden en enviarme un correo o escribir un comentario en este blog.