Ejemplo: Escribir una clase de volcador de grabación diferida

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

	}
}