Quando uma implementação de plug-in do usuário lança uma exceção, o eXtreme Scale verifica determinadas exceções definidas no contrato throws. No entanto, às vezes uma exceção não verificada contém uma exceção de contrato ou a exceção não observa o contrato apropriadamente. Portanto, um mecanismo é necessário para mapear a exceção para a exceção de contrato se possível, tal como ExceptionMapper.
Considere que um JPALoader deve lançar uma LoaderNotAvailableException quando o servidor de banco de dados ou de rede não está funcional ou o banco de dados é executado sem recursos. No entanto, a implementação do provedor JPA normalmente apenas lança uma PersistenceException genérica com uma SQLException, o estado de SQL ou o código de erro que poderia indicar o problema do servidor de banco de dados ou o problema de rede. Para complicar ainda mais a situação, bancos de dados diferentes possuem estado de SQL e códigos de erro diferentes para tais problemas. Portanto, o mecanismo de mapeamento de exceção deve ser específico do banco de dados.
A interface ExceptionMapper é usada para resolver o problema. Ela possui um método Throwable map(Throwable original) para mapear a exceção original para uma exceção consumível.
Por exemplo, para resolver o problema indicado, a classe de implementação pode examinar o estado de SQL e o código de erro da java.sql.SQLException encadeada na exceção de JPA. Em seguida, pode lançar uma LoaderNotAvailableException se o estado de SQL ou código de erro indica que o servidor de banco de dados ou a rede não está funcional ou o banco de dados é executado sem recursos.
Atualmente, o bean ExceptionMapper só pode ser configurado nos beans do ObjectGrid JPATxCallback. Ele é usado para mapear todas as exceções recebidas do JPATxCallback e dos beans JPALoader ou JPAEntityLoader.
Para configurar um ExceptionMapper, é necessário usar uma configuração no estilo Spring para o bean ExceptionMapper dentro do bean JPATxCallback.
Consulte Configurando Utilitários de Carga do JPA para obter informações sobre como usar a configuração no estilo Spring para um JPALoader.
A seguir há um exemplo no qual mapeamos as exceções de JPA para uma LoaderNotAvailableException se ela indica um problema no servidor de banco de dados ou um problema de rede. Esta implementação de ExceptionMapper é para usar um provedor de JPA com um banco de dados MSSQL. Primeiro, defina um conjunto de códigos de erro de SQL e estados de SQL que indicam o problema da rede ou o problema no servidor de banco de dados específico. No método de mapa, primeiro verifique o código de erro de SQL em uma lista de códigos de erro conhecidos, em seguida, os estados de SQL e, finalmente, a mensagem. Se um deles corresponder, lance uma 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
* This method is used to add intial maps to the hash, this is used
* internally, and it is not for public view
*/
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) {
// keep looping to check the next chained exception
if (cause instanceof SQLException) {
// Only check if the exception is an SQLException
SQLException sqle = (SQLException) cause;
// If the loader not available SQL state set contains this SQL state, then
// we return a LoaderNotAvailableException with the original exception chained in it.
if (loaderNotAvailableSQLStateSet.contains(sqle.getSQLState())) {
return new LoaderNotAvailableException(originalEx);
}
// If the loader not available SQL error code set contains this error code, then
// we return a LoaderNotAvailableException with the original exception chained in it
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");
}
// Get the next chained exception
Throwable newCause = cause.getCause();
// Safe-guard check to avoid indefinite loop if the exception chains itself
if (newCause == cause) {
// Always return the original exception if cannot map it.
return originalEx;
} else {
cause = newCause;
}
}
// Always retrun the original exception if cannot map it.
return originalEx;
}