View Javadoc

1   /*
2    * Copyright (c) 2001 - 2005 ivata limited.
3    * All rights reserved.
4    * -----------------------------------------------------------------------------
5    * ivata masks may be redistributed under the GNU General Public
6    * License as published by the Free Software Foundation;
7    * version 2 of the License.
8    *
9    * These programs are free software; you can redistribute them and/or
10   * modify them under the terms of the GNU General Public License
11   * as published by the Free Software Foundation; version 2 of the License.
12   *
13   * These programs are distributed in the hope that they will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16   *
17   * See the GNU General Public License in the file LICENSE.txt for more
18   * details.
19   *
20   * If you would like a copy of the GNU General Public License write to
21   *
22   * Free Software Foundation, Inc.
23   * 59 Temple Place - Suite 330
24   * Boston, MA 02111-1307, USA.
25   *
26   *
27   * To arrange commercial support and licensing, contact ivata at
28   *                  http://www.ivata.com/contact.jsp
29   * -----------------------------------------------------------------------------
30   * $Log: ThrowableHandling.java,v $
31   * Revision 1.9  2005/10/03 21:32:22  colinmacleod
32   * Cosmetic (line length) fix.
33   *
34   * Revision 1.8  2005/10/03 21:31:29  colinmacleod
35   * Fixed the comparison order or cause properties, so that rootCause is checked
36   * before cause.
37   *
38   * Revision 1.7  2005/10/03 10:17:25  colinmacleod
39   * Fixed some style and javadoc issues.
40   *
41   * Revision 1.6  2005/10/02 14:06:32  colinmacleod
42   * Added/improved log4j logging.
43   *
44   * Revision 1.5  2005/04/09 18:04:17  colinmacleod
45   * Changed copyright text to GPL v2 explicitly.
46   *
47   * Revision 1.4  2005/01/19 12:43:00  colinmacleod
48   * Fixed getCause: it was only returning the immediate cause.
49   *
50   * Revision 1.3  2005/01/11 10:51:02  colinmacleod
51   * Added log tracing.
52   *
53   * Revision 1.2  2005/01/06 22:21:45  colinmacleod
54   * Moved up a version number.
55   * Changed copyright notices to 2005.
56   * Updated the documentation:
57   *   - started working on multiproject:site docu.
58   *   - changed the logo.
59   * Added checkstyle and fixed LOADS of style issues.
60   * Added separate thirdparty subproject.
61   * Added struts (in web), util and webgui (in webtheme) from ivata op.
62   *
63   * Revision 1.1  2004/12/31 19:17:09  colinmacleod
64   * Renamed from ExceptionHandling to ThrowableHandling.
65   *
66   * Revision 1.1  2004/09/30 15:16:04  colinmacleod
67   * Split off addressbook elements into security subproject.
68   *
69   * Revision 1.2  2004/03/21 21:16:19  colinmacleod
70   * Shortened name to ivata op.
71   *
72   * Revision 1.1.1.1  2004/01/27 20:58:00  colinmacleod
73   * Moved ivata op to SourceForge.
74   *
75   * Revision 1.2  2003/10/17 12:36:13  jano
76   * fixing problems with building
77   * converting intranet -> portal
78   * Eclipse building
79   *
80   * Revision 1.1.1.1  2003/10/13 20:50:15  colin
81   * Restructured portal into subprojects
82   *
83   * Revision 1.7  2003/02/24 19:27:31  colin
84   * restructured file paths
85   *
86   * Revision 1.6  2003/02/04 17:43:52  colin
87   * copyright notice
88   *
89   * Revision 1.5  2002/11/12 10:16:10  colin
90   * added getRemoteException method with support for EJBException
91   *
92   * Revision 1.4  2002/09/16 14:18:15  colin
93   * improved the error messages for embedded exceptions
94   *
95   * Revision 1.3  2002/09/09 13:39:55  colin
96   * fixed bug in the displaying of the exception class
97   *
98   * Revision 1.2  2002/08/29 16:40:23  colin
99   * improved documentation
100  *
101  * Revision 1.1  2002/06/13 11:22:21  colin
102  * first version with rose model integration.
103  * -----------------------------------------------------------------------------
104  */
105 package com.ivata.mask.util;
106 import java.beans.PropertyDescriptor;
107 import java.lang.reflect.InvocationTargetException;
108 import org.apache.commons.beanutils.PropertyUtils;
109 import org.apache.log4j.Logger;
110 /***
111  * <p>
112  * Routines for handling exceptions.
113  * </p>
114  * <p>
115  * Don't create an instance of this class; use the static final methods.
116  * </p>
117  *
118  * @since ivata masks 0.4 (2002-05-20)
119  * @author Colin MacLeod
120  * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
121  * @version $Revision: 1.9 $
122  */
123 public final class ThrowableHandling {
124     /***
125      * <p>
126      * This array of string contains all the known methods which will return a
127      * cause exception/throwable. The order is the order they will be searched
128      * for.
129      * </p>
130      */
131     private static final String[] CAUSE_PROPERTIES = {
132                             "rootCause",
133                             "targetException",
134                             "undeclaredThrowable",
135                             "causedBy",
136                             "cause"
137     };
138     /***
139      * <p>
140      * Used to identify the preprended text in embedded exceptions.
141      * </p>
142      */
143     static final String[] EMBEDDED = {
144                         "Embedded Exception",
145                         "SystemException is:"
146     };
147     /***
148      * <copyDoc>Refer to {@link Logger}.</copyDoc>
149      */
150     private static final Logger logger
151         = Logger.getLogger(ThrowableHandling.class);
152     /***
153      * <p>
154      * If the parameter provided is a <code>RemoteException</code>, then
155      * provide the original exception, otherwise just return this exception.
156      * </p>
157      *
158      * @param throwable
159      *            throwable object to return the original exception for.
160      * @return the exception which originally was thrown.
161      */
162     public static Throwable getCause(final Throwable throwable) {
163         if (logger.isDebugEnabled()) {
164             logger.debug("getCause(Throwable throwable = " + throwable
165                     + ") - start");
166         }
167 
168         if (throwable == null) {
169             if (logger.isDebugEnabled()) {
170                 logger.debug("getCause(Throwable) - end - return value = "
171                         + null);
172             }
173             return null;
174         }
175         Throwable returnThrowable = throwable;
176         Throwable cause;
177         while (((cause = getCauseOrNull(returnThrowable)) != null)
178                 && (cause != returnThrowable)) {
179             returnThrowable = cause;
180         }
181 
182         if (logger.isDebugEnabled()) {
183             logger.debug("getCause(Throwable) - end - return value = "
184                     + returnThrowable);
185         }
186         return returnThrowable;
187     }
188     /***
189      * <p>
190      * If the given throwable was caused by another, get that cause.
191      * </p>
192      *
193      * @param throwable
194      *            the throwable to find the cause for
195      * @return cause of this throwable, or <code>null</code> if there is no
196      *         original cause.
197      */
198     private static Throwable getCauseOrNull(final Throwable throwable) {
199         if (logger.isDebugEnabled()) {
200             logger.debug("getCauseOrNull(Throwable throwable = " + throwable
201                     + ") - start");
202         }
203 
204         if (throwable == null) {
205             if (logger.isDebugEnabled()) {
206                 logger.debug("getCauseOrNull: throwable was null");
207             }
208             return null;
209         }
210         PropertyDescriptor[] descriptors = PropertyUtils
211                 .getPropertyDescriptors(throwable.getClass());
212         // find the descriptor which matches a known name
213         String causeProperty = null;
214         outerLoop:
215         for (int j = 0; j < ThrowableHandling.CAUSE_PROPERTIES.length; j++) {
216             String property = ThrowableHandling.CAUSE_PROPERTIES[j];
217             for (int i = 0; i < descriptors.length; i++) {
218                 PropertyDescriptor descriptor = descriptors[i];
219                 if (logger.isDebugEnabled()) {
220                     logger.debug("Comparing property '"
221                             + descriptor.getName()
222                             + "' to known cause property '"
223                             + property
224                             + "'");
225                 }
226                 if (property.equals(descriptor.getName())) {
227                     if (logger.isDebugEnabled()) {
228                         logger.debug("getCauseOrNull: using '"
229                                 + property
230                                 + "' as cause property");
231                     }
232                     causeProperty = property;
233                     break outerLoop;
234                 }
235             }
236         }
237         // if there is no property to find the cause, there is not much we can
238         // do...
239         if (causeProperty == null) {
240             if (logger.isDebugEnabled()) {
241                 logger.debug("getCauseOrNull: cause property is null");
242             }
243             return null;
244         }
245         try {
246             Throwable returnThrowable = (Throwable) PropertyUtils.getProperty(
247                     throwable, causeProperty);
248             if (logger.isDebugEnabled()) {
249                 logger.debug("getCauseOrNull(Throwable) - end - return value = "
250                                 + returnThrowable);
251             }
252             return returnThrowable;
253         } catch (IllegalAccessException e) {
254             logger.error("getCauseOrNull(Throwable)", e);
255 
256             if (logger.isDebugEnabled()) {
257                 logger.debug(e);
258             }
259             return null;
260         } catch (InvocationTargetException e) {
261             logger.error("getCauseOrNull(Throwable)", e);
262 
263             if (logger.isDebugEnabled()) {
264                 logger.debug(e);
265             }
266             return null;
267         } catch (NoSuchMethodException e) {
268             logger.error("getCauseOrNull(Throwable)", e);
269 
270             if (logger.isDebugEnabled()) {
271                 logger.debug(e);
272             }
273             return null;
274         }
275     }
276     /***
277      * <p>
278      * If the parameter provided is a <code>RemoteException</code>, then
279      * provide the message from the original exception, otherwise just return
280      * the message of this exception.
281      * </p>
282      *
283      * @param throwable
284      *            throwable object to return the original exception message for.
285      * @return the message of the exception which originally was thrown.
286      */
287     public static String getRemoteMessage(final Throwable throwable) {
288         if (logger.isDebugEnabled()) {
289             logger.debug("getRemoteMessage(Throwable throwable = " + throwable
290                     + ") - start");
291         }
292 
293         Throwable remoteException = getCause(throwable);
294         String messageText = getCause(remoteException).getMessage();
295         int index, textPosition;
296         // look for any embedded exception text and remove it
297         if (messageText == null) {
298             messageText = "An application exception occurred, "
299                     + remoteException.getClass() + ".";
300         } else {
301             // go thro' all of the texts which signify an embedded message
302             for (index = 0; index < EMBEDDED.length; ++index) {
303                 if ((textPosition = messageText.indexOf(EMBEDDED[index]))
304                         != -1) {
305                     messageText = messageText.substring(textPosition
306                             + EMBEDDED[index].length());
307                     break;
308                 }
309             }
310         }
311 
312         if (logger.isDebugEnabled()) {
313             logger.debug("getRemoteMessage(Throwable) - end - return value = "
314                     + messageText);
315         }
316         return messageText;
317     }
318     /***
319      * <p>
320      * Private default constructor ensures utility class functionality.
321      * </p>
322      */
323     private ThrowableHandling() {
324     }
325 }