Lorsqu'une implémentation de plug-in génère une exception, eXtreme Scale vérifie certaines exceptions définies dans le contrat throws. Toutefois, il arrive qu'une exception non vérifiée contienne un exception de contrat ou que l'exception n'observe pas correctement le contrat. Par conséquent, un mécanisme, tel que ExceptionMapper, est nécessaire pour mapper l'exception à l'exception de contrat, si possible.
Considérez qu'un chargeur JPALoaderdoit émettre une exception LoaderNotAvailableException lorsque le serveur de base de données ou le réseau ne sont pas fonctionnels ou que la base de données manque de ressources. Toutefois, l'implémentation du fournisseur JPA envoie simplement normalement une exception générique PersistenceException avec une exception SQLException, l'état SQL ou le code d'erreur SQL qui peut indiquer le problème de serveur de base de données ou réseau. Pour compliquer la situation, l'état SQL ou les codes d'erreur SQL varient fonction des bases de données. Par conséquent, le mécanisme de mappage d'exception doit être spécifique de la base de données.
L'interface ExceptionMapper permet de résoudre le problème. Elle dispose d'une d'une méthode Throwable map(Throwable original) pour mapper l'exception d'origine à l'exception consommable.
Par exemple,pour résoudre le problème indiqué, la classe d'implémentation peut introspecter l'état SQL ou le code d'erreur SQL de l'exception java.sql.SQLException chaînée dans l'exception JPA. Ensuite, elle peut générer une exception LoaderNotAvailableException si l'état ou le code d'erreur SQL indique que le serveur de base de données ou le réseau ne sont pas fonctionnels ou que la base de données manque de ressources.
De même, le bean ExceptionMapper peut être configuré uniquement dans les beans JPATxCallback ObjectGrid. Il permet de mapper toutes les exceptions reçues des beans JPATxCallback et JPALoader ou du bean JPAEntityLoader.
Pour configurer un mappeur ExceptionMapper, vous devez utiliser une configuration de type Spring pour le bean ExceptionMapper dans le bean JPATxCallback.
Voir Configuration des chargeurs JPA pour plus d'informations sur l'utilisation d'une configuration de type Spring pour JPALoader.
Voici un exemple de mappage des exceptions JPA à une exception aderNotAvailableException si elle indique un problème de serveur de base de données ou de réseau. Cette implémentation ExceptionMapper est destinée à utiliser un fournisseur JPA avec une base de données MSSQL. En premier lieu, définissez un groupe de codes d'erreur SQL et des états SQL qui indiquent le problème réseau ou de serveur de base de données. Dans la méthode de mappage, vérifiez d'abord le code d'erreur SQL par rapport à une liste de codes d'erreur connues, puis les états SQL et enfin le message. Si l'un de ces éléments correspond, générez une exception LoaderNotAvaliableException.
public class JPAMSSQLExceptionMapper implements ExceptionMapper {
static Set<Integer> loaderNotAvailableSQLErrorSet = new HashSet<Integer>();
static Set<String> loaderNotAvailableSQLStateSet = new HashSet<String>();
static {
addInitialMaps();
}
public C3P0MSSQLExceptionMapper() {
System.out.println("C3P0MSSQLExceptionMapper is constructed");
}
/**
* @internal
* Cette méthode permet d'ajouter des mappes initiales au hachage utilisé
* en interne et elle n'est pas exposée à l'utilisateur
*/
private static void addInitialMaps() {
// http://msdn.microsoft.com/en-us/library/cc645603.aspx
loaderNotAvailableSQLErrorSet.add(new Integer(230));
loaderNotAvailableSQLErrorSet.add(new Integer(6002));
// http://white-box.us/2009/03/08/sql-92-sqlstate-codes/
/*
* 08001 SQL client unable to establish SQL connection
* 08002 connection name in use
* 08003 connection does not exist
* 08004 SQL server rejected SQL connection
* 08006 connection failure
* 08007 transaction resolution unknown
*/
loaderNotAvailableSQLStateSet.add("08000");
loaderNotAvailableSQLStateSet.add("08001");
loaderNotAvailableSQLStateSet.add("08002");
loaderNotAvailableSQLStateSet.add("08003");
loaderNotAvailableSQLStateSet.add("08004");
loaderNotAvailableSQLStateSet.add("08006");
loaderNotAvailableSQLStateSet.add("08007");
// http://msdn.microsoft.com/en-us/library/ms714687.aspx
loaderNotAvailableSQLStateSet.add("08S01");
loaderNotAvailableSQLStateSet.add("HY000");
}
private static Pattern[] sqlServerMessagePatterns = new Pattern[] {
Pattern.compile("The TCP/IP connection to the host .* has failed.*"), Pattern.compile(".*Connection reset.*") };
private static Pattern[] sqlExceptionMessagePatterns = new Pattern[] { Pattern
.compile(".*Connections could not be acquired from the underlying database.*") };
private static String connection_reset = "Connection reset";
public Throwable map(Throwable originalEx) {
Throwable cause = originalEx;
while (cause != null) {
// Poursuite de l'exécution de la boucle pour vérifier l'exception chaînée suivante
if (cause instanceof SQLException) {
// Vérifier uniquement s'il s'agit d'une exception SQLException
SQLException sqle = (SQLException) cause;
// Si le groupe d'états SQL de chargeur indisponible contient cet état,
// une exception LoaderNotAvailableException contenant l'exception chaînée d'origine est retournée
if (loaderNotAvailableSQLStateSet.contains(sqle.getSQLState())) {
return new LoaderNotAvailableException(originalEx);
}
// Si le groupe de codes d'erreur SQL de chargeur indisponible contient ce code d'erreur,
// l'exception LoaderNotAvailableException contenant l'exception chaînées d'origine est générée
if (loaderNotAvailableSQLErrorSet.contains(new Integer(sqle.getErrorCode()))) {
return new LoaderNotAvailableException(originalEx);
}
String msg = sqle.getMessage();
for (int i=0; i<sqlExceptionMessagePatterns.length; i++) {
if (sqlExceptionMessagePatterns[i].matcher(msg).matches()) {
return new LoaderNotAvailableException(originalEx);
}
}
} else if (cause.getClass().getName().equals("com.microsoft.sqlserver.jdbc.SQLServerException")) {
String msg = cause.getMessage();
for (int i=0; i<sqlServerMessagePatterns.length; i++) {
if (sqlServerMessagePatterns[i].matcher(msg).matches()) {
return new LoaderNotAvailableException(originalEx);
}
}
System.out.println("msg " + msg + " does not match");
}
// Obtention de l'exception chaînée suivante
Throwable newCause = cause.getCause();
// Vérification de sécurité pour éviter une boucle infinie si l'exception se chaîne elle-même
if (newCause == cause) {
// Retourne toujours l'exception d'origine si elle ne peut pas être mappée.
return originalEx;
} else {
cause = newCause;
}
}
// Retourne toujours l'exception d'origine si elle ne peut pas être mappée.
return originalEx;
}