001: /*
002: * This software is released under a licence similar to the Apache Software Licence.
003: * See org.logicalcobwebs.proxool.package.html for details.
004: * The latest version is available at http://proxool.sourceforge.net
005: */
006: package org.logicalcobwebs.proxool;
007:
008: import org.apache.commons.logging.Log;
009: import org.apache.commons.logging.LogFactory;
010:
011: import java.lang.reflect.Constructor;
012: import java.lang.reflect.InvocationTargetException;
013: import java.lang.reflect.Method;
014: import java.sql.SQLException;
015: import java.util.Iterator;
016:
017: /**
018: * Will wrap up exceptions in another exception which can be defined at runtime.
019: * @version $Revision: 1.5 $, $Date: 2006/01/18 14:40:01 $
020: * @author billhorsman
021: * @author $Author: billhorsman $ (current maintainer)
022: */
023: class FatalSqlExceptionHelper {
024:
025: private static final Log LOG = LogFactory
026: .getLog(FatalSqlExceptionHelper.class);
027:
028: /**
029: * Throws a wrapped SQLException if a wrapper is defined
030: * @param className the classname of the wrapping exception (must be either a RuntimeException or
031: * an SQLException). If null, then the original exception is rethrown.
032: * @param originalException the orginal exception
033: * @throws ProxoolException if there is an unexpected error with wrapping the exception
034: * @throws SQLException either the original exception, or a wrapped version of it
035: * @throws RuntimeException a wrapped up version of the orginal
036: */
037: protected static void throwFatalSQLException(String className,
038: Throwable originalException) throws ProxoolException,
039: SQLException, RuntimeException {
040: if (className != null && className.trim().length() > 0) {
041: Class clazz = null;
042: try {
043: clazz = Class.forName(className);
044: } catch (ClassNotFoundException e) {
045: throw new ProxoolException("Couldn't find class "
046: + className);
047: }
048: if (SQLException.class.isAssignableFrom(clazz)) {
049: // That's OK
050: } else if (RuntimeException.class.isAssignableFrom(clazz)) {
051: // That's OK
052: } else {
053: throw new ProxoolException(
054: "Couldn't wrap up using "
055: + clazz.getName()
056: + " because it isn't either a RuntimeException or an SQLException");
057: }
058: Constructor toUse = null;
059: Object[] args = null;
060: String argDescription = "";
061: Constructor[] constructors = clazz.getConstructors();
062: for (int i = 0; i < constructors.length; i++) {
063: Constructor constructor = constructors[i];
064: Class[] parameterTypes = constructor
065: .getParameterTypes();
066: if (toUse == null && parameterTypes.length == 0) {
067: toUse = constructor;
068: }
069: if (parameterTypes.length == 1
070: && Exception.class
071: .isAssignableFrom(parameterTypes[0])) {
072: toUse = constructor;
073: args = new Object[] { originalException };
074: argDescription = "Exception";
075: break;
076: }
077: }
078: try {
079: Object exceptionToThrow = toUse.newInstance(args);
080: if (exceptionToThrow instanceof RuntimeException) {
081: LOG.debug("Wrapping up a fatal exception: "
082: + originalException.getMessage(),
083: originalException);
084: throw (RuntimeException) exceptionToThrow;
085: } else if (exceptionToThrow instanceof SQLException) {
086: throw (SQLException) exceptionToThrow;
087: } else {
088: throw new ProxoolException(
089: "Couldn't throw "
090: + clazz.getName()
091: + " because it isn't either a RuntimeException or an SQLException");
092: }
093: } catch (InstantiationException e) {
094: throw new ProxoolException("Couldn't create "
095: + clazz.getName() + "(" + argDescription + ")",
096: e);
097: } catch (IllegalAccessException e) {
098: throw new ProxoolException("Couldn't create "
099: + clazz.getName() + "(" + argDescription + ")",
100: e);
101: } catch (InvocationTargetException e) {
102: throw new ProxoolException("Couldn't create "
103: + clazz.getName() + "(" + argDescription + ")",
104: e);
105: }
106: } else {
107: if (originalException instanceof SQLException) {
108: throw (SQLException) originalException;
109: } else if (originalException instanceof RuntimeException) {
110: throw (RuntimeException) originalException;
111: } else {
112: throw new RuntimeException("Unexpected exception:"
113: + originalException.getMessage());
114: }
115: }
116: }
117:
118: /**
119: * Test to see if an exception is a fatal one
120: * @param cpd the definition so we can find out what a fatal exception looks like
121: * @param t the exception to test
122: * @return true if it is fatal
123: */
124: protected static boolean testException(
125: ConnectionPoolDefinitionIF cpd, Throwable t) {
126: return testException(cpd, t, 0);
127: }
128:
129: /**
130: * Test to see if an exception is a fatal one
131: * @param cpd the definition so we can find out what a fatal exception looks like
132: * @param t the exception to test
133: * @param level the recursion level (max 20)
134: * @return true if it is fatal
135: */
136: protected static boolean testException(
137: ConnectionPoolDefinitionIF cpd, Throwable t, int level) {
138: boolean fatalSqlExceptionDetected = false;
139: Iterator i = cpd.getFatalSqlExceptions().iterator();
140: while (i.hasNext()) {
141: if (t.getMessage() != null
142: && t.getMessage().indexOf((String) i.next()) > -1) {
143: // This SQL exception indicates a fatal problem with this connection.
144: fatalSqlExceptionDetected = true;
145: }
146: }
147:
148: // If it isn't fatal, then try testing the contained exception
149: if (!fatalSqlExceptionDetected && level < 20) {
150: Throwable cause = getCause(t);
151: if (cause != null) {
152: fatalSqlExceptionDetected = testException(cpd, cause,
153: level + 1);
154: }
155: }
156:
157: return fatalSqlExceptionDetected;
158: }
159:
160: /**
161: * Tries to drill down into an exception to find its cause. Only goes one level deep.
162: * Uses reflection to look at getCause(), getTargetException(), getRootCause() and
163: * finally getOriginalException() methods to see if it can find one. Doesn't throw
164: * an error - it will just log a warning and return a null if nothing was found.
165: * @param t the exception to look inside
166: * @return the original exception or null if none was found.
167: */
168: protected static Throwable getCause(Throwable t) {
169: Throwable cause = null;
170: Method causeMethod = null;
171:
172: try {
173: // Try a series of likely accessor methods
174: if (causeMethod == null) {
175: causeMethod = getMethod(t, "getCause");
176: }
177: if (causeMethod == null) {
178: causeMethod = getMethod(t, "getTargetException");
179: }
180: if (causeMethod == null) {
181: causeMethod = getMethod(t, "getRootCause");
182: }
183: if (causeMethod == null) {
184: causeMethod = getMethod(t, "getOriginalException");
185: }
186:
187: // If one was found, invoke it.
188: if (causeMethod != null) {
189: try {
190: cause = (Throwable) causeMethod.invoke(t, null);
191: } catch (IllegalAccessException e) {
192: LOG.warn("Problem invoking "
193: + t.getClass().getName() + "."
194: + causeMethod.getName() + ". Ignoring.", e);
195: } catch (IllegalArgumentException e) {
196: LOG.warn("Problem invoking "
197: + t.getClass().getName() + "."
198: + causeMethod.getName() + ". Ignoring.", e);
199: } catch (InvocationTargetException e) {
200: LOG.warn("Problem invoking "
201: + t.getClass().getName() + "."
202: + causeMethod.getName() + ". Ignoring.", e);
203: }
204: }
205: } catch (Exception e) {
206: LOG
207: .warn(
208: "Unexpected exception drilling into exception. Ignoring.",
209: e);
210: }
211: return cause;
212: }
213:
214: private static Method getMethod(Object o, String methodName) {
215: Method m = null;
216: try {
217: m = o.getClass().getMethod(methodName, null);
218: // Reject any method that doesn't return a throwable.
219: if (!Throwable.class.isAssignableFrom(m.getReturnType())) {
220: m = null;
221: }
222: } catch (NoSuchMethodException e) {
223: // That's OK
224: } catch (SecurityException e) {
225: LOG.warn("Problem finding method " + methodName, e);
226: }
227: return m;
228: }
229:
230: }
231:
232: /*
233: Revision history:
234: $Log: FatalSqlExceptionHelper.java,v $
235: Revision 1.5 2006/01/18 14:40:01 billhorsman
236: Unbundled Jakarta's Commons Logging.
237:
238: Revision 1.4 2005/07/01 08:02:50 billhorsman
239: Check for exception message being null
240:
241: Revision 1.3 2003/09/30 18:39:08 billhorsman
242: New test-before-use, test-after-use and fatal-sql-exception-wrapper-class properties.
243:
244: Revision 1.2 2003/09/29 18:12:33 billhorsman
245: Doc
246:
247: */
|