Exemple : écriture d'une classe dumper en écriture différée

Cet exemple de code source montre comment écrire un programme de surveillance (dumper) pour gérer les mises à jour d'écriture différée ayant échoué.

//
//Cet exemple de programme est fourni TEL QUEL et peut être utilisé, exécuté, copié et
//modifiés sans acquittement de droits par le client (a) pour sa propre formation et
//analyse, (b) pour développer des applications destinées à s'exécuter avec un produit IBM
//WebSphere pour une utilisation interne par le client ou pour redistribution
//par le client, dans le cadre d'une telle application, dans les produits du client. "
//
//5724-J34 (C) COPYRIGHT International Business Machines Corp. 2009
//  All Rights Reserved *  Eléments sous licence - Propriété d'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;

/**
 * L'écriture différée s'attend à ce que les transactions vers le chargeur aboutissent. Si une
 * transaction échoue pour une clé, elle insère une entrée dans une mappe appelée PREFIXE + nomMappe.
 * L'application doit rechercher dans cette mappe les entrées de vidage des incidents de
 * transaction d'écriture différée. L'application est chargée d'analyser, puis de supprimer
 * ces entrées. Ces dernières peuvent être assez importantes car elles incluent la clé, les
 * images avant et après de la valeur et l'exception elle-même. Les exceptions peuvent facilement
 * atteindre 20k.
 * 
 * La classe est enregistrée avec la grille et une instance est créée pour
 * chaque fragment primaire d'une machine virtuelle Java. Une unique unité
 * d'exécution est créée et cette dernière recherche le fragment dans chaque
 * mappe d'erreurs à écriture différée, imprime le problème, puis supprime
 * l'entrée.
 * 
 * Cela signifie qu'il existera une unité d'exécution par fragment. Si le
 * fragment est transféré sur une autre machine virtuelle Java, la méthode
 * deactivate arrête l'unité d'exécution.
 * @author bnewport
 *
 */
public class WriteBehindDumper implements ObjectGridEventListener, ObjectGridEventGroup.ShardEvents,
 Callable<Boolean>
{
	static Logger logger = Logger.getLogger(WriteBehindDumper.class.getName());
	
	ObjectGrid grid;
	
	/**
	 * Pool d'unités d'exécution chargé de gérer les vérificateurs de table. Si l'application possède
	 * son propre pool, modifiez la valeur pour réutiliser le pool existant
	 */
	static ScheduledExecutorService pool = new ScheduledThreadPoolExecutor(2); // deux unités d'exécution pour vider les enregistrements

	// futur de ce fragment
	ScheduledFuture<Boolean> future;
	
	// true si ce fragment est actif
	volatile boolean isShardActive;

	/**
	 * Durée normale entre les recherches dans les mappes d'erreurs d'écriture différée
	 */
	final long BLOCKTIME_SECS = 20L;
	
	/**
	 * Une session allouée pour ce fragment. Il est inutile de les allouer encore et encore
	 */
	Session session;
	/**
	 * Si un fragment primaire est activé, planifiez les vérifications de sorte à vérifier périodiquement
	 * les mappes d'erreurs d'écriture différée et imprimer les éventuels problèmes
	 */
	public void shardActivated(ObjectGrid grid) 
	{
		try
		{
			this.grid = grid;
			session = grid.getSession();
	
			isShardActive = true;
			future = pool.schedule(this, BLOCKTIME_SECS, TimeUnit.SECONDS); // vérification initiale toutes les BLOCKTIME_SECS secondes
		}
		catch(ObjectGridException e)
		{
			throw new ObjectGridRuntimeException("Exception activating write dumper", e);
		}
	}

	/**
	 * Marquez le fragment comme inactif, puis annulez le vérificateur
	 */
	public void shardDeactivate(ObjectGrid arg0) 
	{
		isShardActive = false;
		// s'il est annulé, l'annulation renvoie la valeur true
		if(future.cancel(false) == false)
		{
			// sinon, la tâche est bloquée jusqu'à ce que le vérificateur ait terminé son exécution
			while(future.isDone() == false) // attendez que la tâche se termine d'une manière ou d'une autre
			{
				try
				{
					Thread.sleep(1000L); // vérifiez à chaque seconde
				}
				catch (InterruptedException e)
				{
				}
			}
		}
	}

	/**
	 * Test simple permettant de vérifier si l'écriture différée est activée
  * pour la mappe ; si c'est le cas, renvoie le nom de la mappe d'erreurs
  * associée.
	 * @param mapName Mappe à tester
	 * @return Nom de la mappe d'erreurs à écriture différée si elle existe ;
  * sinon null
	 */
	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;
	}

	/**
	 * Exécuté pour chaque fragment. Vérifie si l'écriture différée est activée pour chaque mappe et
  * si c'est le cas, imprime les éventuelles erreurs de transaction d'écriture différée
	 * avant de supprimer l'enregistrement.
	 */
	public Boolean call()
	{
		logger.fine("Called for " + grid.toString());
		try
		{
			// tant que le fragment primaire est présent sur cette machine virtuelle Java,
			// seules les mappes définies par l'utilisateur sont renvoyées ici ; aucune mappe système,
			// telle que les mappes d'écriture différée, ne figure dans cette liste.
			Iterator<String> iter = grid.getListOfMapNames().iterator();
			boolean foundErrors = false;
			// effectuez une itération sur toutes les mappes actuelles
			while(iter.hasNext() && isShardActive)
			{
				String origName = iter.next();
				
				// s'il s'agit d'une mappe d'erreurs à écriture différée
				String name = getWriteBehindNameIfPossible(grid, origName);
				if (name != null) 
				{
					// essayez de supprimer des blocs de N erreurs à la fois
					ObjectMap errorMap = null;
					try
					{
						errorMap = session.getMap(name);
					}
					catch(UndefinedMapException e)
					{
						// au démarrage les mappes d'erreurs risquent de ne pas encore exister ; patience...
						continue;
					}
					// essayez de vider jusqu'à N enregistrements à la fois
					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);
							//
							// Votre application doit consigner le problème ici
							logger.info("WriteBehindDumper ( " + origName + ") for key (" + elem.getKey() + ") Exception: " + 
								elem.getThrowable().toString());
							//
							//
							errorMap.remove(seqKey);
						}
						else
							break;
					}
					session.commit();
				}
			} // passez à la mappe suivante
			// bouclez plus rapidement en cas d'erreurs
			if(isShardActive)
			{
				// replanifiez après une seconde en cas d'enregistrements incorrects ;
				// sinon, attendez 20 secondes.
				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();
			
			//ne laissez pas de transaction sur la session.
			if(session.isTransactionActive())
			{
				try { session.rollback(); } catch(Exception e2) {}
			}
		}
		return true;
	}
	
	public void destroy() {
		// TODO Module de remplacement de méthode généré automatiquement

	}

	public void initialize(Session arg0) {
		// TODO Module de remplacement de méthode généré automatiquement

	}

	public void transactionBegin(String arg0, boolean arg1) {
		// TODO Module de remplacement de méthode généré automatiquement

	}

	public void transactionEnd(String arg0, boolean arg1, boolean arg2,
			Collection arg3) {
		// TODO Module de remplacement de méthode généré automatiquement

	}
}