001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (C) 2012 Paul Ferraro 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Lesser General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package net.sf.hajdbc.sql; 019 020import java.sql.BatchUpdateException; 021import java.sql.ClientInfoStatus; 022import java.sql.DataTruncation; 023import java.sql.SQLClientInfoException; 024import java.sql.SQLException; 025import java.util.Arrays; 026import java.util.Map; 027 028import net.sf.hajdbc.AbstractExceptionFactory; 029import net.sf.hajdbc.ExceptionType; 030import net.sf.hajdbc.dialect.Dialect; 031 032/** 033 * @author Paul Ferraro 034 */ 035public class SQLExceptionFactory extends AbstractExceptionFactory<SQLException> 036{ 037 private static final long serialVersionUID = -7352436527312370925L; 038 039 public SQLExceptionFactory() 040 { 041 super(SQLException.class); 042 } 043 044 /** 045 * {@inheritDoc} 046 * @see net.sf.hajdbc.ExceptionFactory#createException(java.lang.String) 047 */ 048 @Override 049 public SQLException createException(String message) 050 { 051 return new SQLException(message); 052 } 053 054 /** 055 * {@inheritDoc} 056 * @see net.sf.hajdbc.ExceptionFactory#equals(java.lang.Exception, java.lang.Exception) 057 */ 058 @Override 059 public boolean equals(SQLException exception1, SQLException exception2) 060 { 061 // Terminator for exception chain recursion 062 if ((exception1 == null) || (exception2 == null)) 063 { 064 return exception1 == exception2; 065 } 066 067 // Fast-fail for mismatched Java 1.6 SQLException subclasses 068 if (!exception1.getClass().equals(exception2.getClass())) 069 { 070 return false; 071 } 072 073 // Ensure BatchUpdateExceptions have matching update counts 074 if ((exception1 instanceof BatchUpdateException) && (exception2 instanceof BatchUpdateException)) 075 { 076 BatchUpdateException e1 = (BatchUpdateException) exception1; 077 BatchUpdateException e2 = (BatchUpdateException) exception2; 078 079 int[] counts1 = e1.getUpdateCounts(); 080 int[] counts2 = e2.getUpdateCounts(); 081 082 if ((counts1 != null) && (counts2 != null) ? !Arrays.equals(counts1, counts2) : (counts1 != counts2)) 083 { 084 return false; 085 } 086 } 087 else if ((exception1 instanceof SQLClientInfoException) && (exception2 instanceof SQLClientInfoException)) 088 { 089 SQLClientInfoException e1 = (SQLClientInfoException) exception1; 090 SQLClientInfoException e2 = (SQLClientInfoException) exception2; 091 092 Map<String, ClientInfoStatus> map1 = e1.getFailedProperties(); 093 Map<String, ClientInfoStatus> map2 = e2.getFailedProperties(); 094 095 return (map1 != null) && (map2 != null) ? map1.equals(map2) : (map1 != map2); 096 } 097 else if ((exception1 instanceof DataTruncation) && (exception2 instanceof DataTruncation)) 098 { 099 DataTruncation e1 = (DataTruncation) exception1; 100 DataTruncation e2 = (DataTruncation) exception2; 101 102 return (e1.getDataSize() == e2.getDataSize()) && (e1.getIndex() == e2.getIndex()) && (e1.getParameter() == e2.getParameter()) && (e1.getRead() == e2.getRead()) && (e1.getTransferSize() == e2.getTransferSize()); 103 } 104 105 SQLException nextException1 = exception1.getNextException(); 106 SQLException nextException2 = exception2.getNextException(); 107 108 int code1 = exception1.getErrorCode(); 109 int code2 = exception2.getErrorCode(); 110 111 // Match by vendor code, if defined 112 if ((code1 != 0) || (code2 != 0)) 113 { 114 return (code1 == code2) ? this.equals(nextException1, nextException2) : false; 115 } 116 117 String state1 = exception1.getSQLState(); 118 String state2 = exception2.getSQLState(); 119 120 boolean hasState1 = (state1 != null); 121 boolean hasState2 = (state2 != null); 122 123 // Match by SQLState, if defined 124 if (hasState1 || hasState2) 125 { 126 return (state1 == state2) || (hasState1 && hasState2 && state1.equals(state2)) ? this.equals(nextException1, nextException2) : false; 127 } 128 129 // Fallback to match by reason 130 String reason1 = exception1.getMessage(); 131 String reason2 = exception2.getMessage(); 132 133 return ((reason1 == reason2) || ((reason1 != null) && (reason2 != null) && reason1.equals(reason2))) ? this.equals(nextException1, nextException2) : false; 134 } 135 136 /** 137 * {@inheritDoc} 138 * @see net.sf.hajdbc.ExceptionFactory#indicatesFailure(java.lang.Exception, net.sf.hajdbc.dialect.Dialect) 139 */ 140 @Override 141 public boolean indicatesFailure(SQLException exception, Dialect dialect) 142 { 143 SQLException nextException = exception.getNextException(); 144 145 return dialect.indicatesFailure(exception) || ((nextException != null) && this.indicatesFailure(nextException, dialect)); 146 } 147 148 /** 149 * {@inheritDoc} 150 * @see net.sf.hajdbc.ExceptionFactory#getType() 151 */ 152 @Override 153 public ExceptionType getType() 154 { 155 return ExceptionType.SQL; 156 } 157}