Este código fuente de ejemplo muestra cómo escribir un observador (volcador) para manejar actualizaciones de grabación diferida anómalas.
//
//Este programa de ejemplo se proporciona TAL CUAL y se puede utilizar, ejecutar, copiar y
//modificar sin que el cliente tenga que pagar derechos (a) para su propia formación,
//(b) para desarrollar aplicaciones diseñadas para ejecutarse con un producto IBM
//WebSphere, ya sea para uso interno propio del cliente o para su redistribución
//por parte del cliente, como parte de una aplicación de este tipo, en los productos propios del cliente. "
//
//5724-J34 (C) COPYRIGHT International Business Machines Corp. 2009
//Reservados todos los derechos * Material bajo licencia - Propiedad de IBM
//
package utils;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import com.ibm.websphere.objectgrid.BackingMap;
import com.ibm.websphere.objectgrid.ObjectGrid;
import com.ibm.websphere.objectgrid.ObjectGridException;
import com.ibm.websphere.objectgrid.ObjectGridRuntimeException;
import com.ibm.websphere.objectgrid.ObjectMap;
import com.ibm.websphere.objectgrid.Session;
import com.ibm.websphere.objectgrid.UndefinedMapException;
import com.ibm.websphere.objectgrid.plugins.ObjectGridEventGroup;
import com.ibm.websphere.objectgrid.plugins.ObjectGridEventListener;
import com.ibm.websphere.objectgrid.writebehind.FailedUpdateElement;
import com.ibm.websphere.objectgrid.writebehind.WriteBehindLoaderConstants;
/**
* La grabación diferida espera que las transacciones para Loader sean satisfactorias. Si una
* transacción para una clave falla, insertará una entrada en una correlación denominada
* PREFIJO + nombreCorrelación. La aplicación debe comprobar si en esta correlación hay
* entradas para volcar anomalías de transacciones de grabación anticipada. La aplicación es
* responsable de analizar y luego eliminar estas entradas. Estas entradas pueden ser de gran
* tamaño porque incluyen la clave, las imágenes del valor de antes y después, y la propia
* excepción. Las excepciones pueden ocupar fácilmente 20k.
*
* La clase se registra con la cuadrícula y se crea una instancia por fragmento
* primario en una JVM.
* Crea una única hebra una única hebra y dicha hebra comprobará cada correlación
* cada correlación de errores de grabación diferida para el fragmento, imprimirá
* el problema y eliminará la entrada.
*
* Esto significa que habrá una hebra por fragmento. Si el fragmento se traslada a otra JVM, el
* método deactivate detiene la hebra.
* @author bnewport
*
*/
public class WriteBehindDumper implements ObjectGridEventListener, ObjectGridEventGroup.ShardEvents,
Callable<Boolean>
{
static Logger logger = Logger.getLogger(WriteBehindDumper.class.getName());
ObjectGrid grid;
/**
* Agrupación de hebras para manejar verificadores de tablas. Si la aplicación tiene una
* agrupación propia, cámbiela para reutilizar la agrupación existente
*/
static ScheduledExecutorService pool = new ScheduledThreadPoolExecutor(2); // dos hebras para volcar registros
// el futuro para este fragmento
ScheduledFuture<Boolean> future;
// true si este fragmento está activo
volatile boolean isShardActive;
/**
* Tiempo normal entre las comprobaciones de correlaciones para ver si hay errores de grabación
* diferida
*/
final long BLOCKTIME_SECS = 20L;
/**
* Una sesión asignada para este fragmento. No tiene sentido en asignarla una y otra vez
*/
Session session;
/**
* Cuando un fragmento primario se activa, planificar las comprobaciones de forma periódica
* para comprobar las correlaciones de errores de grabación diferida e imprimir problemas
*/
public void shardActivated(ObjectGrid grid)
{
try
{
this.grid = grid;
session = grid.getSession();
isShardActive = true;
future = pool.schedule(this, BLOCKTIME_SECS, TimeUnit.SECONDS); // comprobar cada BLOCKTIME_SECS segundos inicialmente
}
catch(ObjectGridException e)
{
throw new ObjectGridRuntimeException("Exception activating write dumper", e);
}
}
/**
* Marcar fragmento como inactivo y luego cancelar el verificador
*/
public void shardDeactivate(ObjectGrid arg0)
{
isShardActive = false;
// si se cancela, la cancelación devuelve true
if(future.cancel(false) == false)
{
// si no, bloquear hasta que se complete el verificador
while(future.isDone() == false) // esperar a que la tarea finalice de una forma u otra
{
try
{
Thread.sleep(1000L); // comprobar cada segundo
}
catch(InterruptedException e)
{
}
}
}
}
/**
* Prueba simple para ver si la correlación está habilitada para la grabación diferida, y si lo
* está, devolver el nombre de la correlación de errores para la misma.
* @param mapName La correlación que se va a probar
* @return El nombre de la correlación de errores de grabación diferida si existe, si no nulo
*/
static public String getWriteBehindNameIfPossible(ObjectGrid grid, String mapName)
{
BackingMap map = grid.getMap(mapName);
if(map != null && map.getWriteBehind() != null)
{
return WriteBehindLoaderConstants.WRITE_BEHIND_FAILED_UPDATES_MAP_PREFIX + mapName;
}
else
return null;
}
/**
* Se ejecuta para cada fragmento. Comprueba si cada correlación tiene habilitada la grabación
* diferida y a continuación imprime cualquier error de transacción de grabación
* y, a continuación, elimina el registro.
*/
public Boolean call()
{
logger.fine("Called for " + grid.toString());
try
{
// mientras el fragmento primario está presente en esta JVM
// aquí sólo se devuelven las correlaciones definidas por el usuario, en esta lista no hay
// ningún correlaciones del sistema como correlaciones de grabación diferida
Iterator<String> iter = grid.getListOfMapNames().iterator();
boolean foundErrors = false;
// iterar en todas las correlaciones actuales
while(iter.hasNext() && isShardActive)
{
String origName = iter.next();
// si es una correlación de errores de grabación diferida
String name = getWriteBehindNameIfPossible(grid, origName);
if(name != null)
{
// intentar eliminar bloques de N errores cada vez
ObjectMap errorMap = null;
try
{
errorMap = session.getMap(name);
}
catch(UndefinedMapException e)
{
// durante el inicio, las correlaciones de errores pueden todavía no existir, paciencia...
continue;
}
// intentar volcar N registros a la vez
session.begin();
for(int counter = 0; counter < 100; ++counter)
{
Integer seqKey = (Integer)errorMap.getNextKey(1L);
if(seqKey != null)
{
foundErrors = true;
FailedUpdateElement elem = (FailedUpdateElement)errorMap.get(seqKey);
//
// La aplicación debe anotar el problema aquí
logger.info("WriteBehindDumper ( " + origName + ") for key (" + elem.getKey() + ") Exception: " +
elem.getThrowable().toString());
//
//
errorMap.remove(seqKey);
}
else
break;
}
session.commit();
}
} // ejecutar correlación siguiente
// realice un bucle más rápido si hay errores
if(isShardActive)
{
// volver a planificar después de un segundo si había registro anómalos
// de lo contrario, espere 20 segundos.
if(foundErrors)
future = pool.schedule(this, 1L, TimeUnit.SECONDS);
else
future = pool.schedule(this, BLOCKTIME_SECS, TimeUnit.SECONDS);
}
}
catch(ObjectGridException e)
{
logger.fine("Exception in WriteBehindDumper" + e.toString());
e.printStackTrace();
//no dejar una transacción en la sesión.
if(session.isTransactionActive())
{
try { session.rollback(); } catch(Exception e2) {}
}
}
return true;
}
public void destroy() {
// Apéndice de método generado automáticamente TODO
}
public void initialize(Session arg0) {
// Apéndice de método generado automáticamente TODO
}
public void transactionBegin(String arg0, boolean arg1) {
// Apéndice de método generado automáticamente TODO
}
public void transactionEnd(String arg0, boolean arg1, boolean arg2,
Collection arg3) {
// Apéndice de método generado automáticamente TODO
}
}