View Javadoc

1   /*
2    * $Id: ExceptionHelper.java 7976 2007-08-21 14:26:13Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.config;
12  
13  import org.mule.MuleRuntimeException;
14  import org.mule.config.i18n.MessageFactory;
15  import org.mule.umo.UMOException;
16  import org.mule.util.ClassUtils;
17  import org.mule.util.SpiUtils;
18  import org.mule.util.StringUtils;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Properties;
28  
29  import org.apache.commons.collections.MapUtils;
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  
33  /**
34   * <code>ExceptionHelper</code> provides a number of helper functions that can be
35   * useful for dealing with Mule exceptions. This class has 3 core functions - <p/>
36   * 1. ErrorCode lookup. A corresponding Mule error code can be found using for a
37   * given Mule exception 2. Addtional Error information such as Java doc url for a
38   * given exception can be resolved using this class 3. Error code mappings can be
39   * looked up by providing the the protocol to map to and the Mule exception.
40   */
41  
42  public final class ExceptionHelper
43  {
44      /**
45       * This is the property to set the error code to no the message it is the
46       * property name the Transport provider uses set the set the error code on the
47       * underlying message
48       */
49      public static final String ERROR_CODE_PROPERTY = "error.code.property";
50  
51      /**
52       * a comma-separated list of other protocols the mappings in a file can be
53       * applied to
54       */
55      public static final String APPLY_TO_PROPERTY = "apply.to";
56  
57      /**
58       * logger used by this class
59       */
60      protected static final Log logger = LogFactory.getLog(ExceptionHelper.class);
61  
62      private static String J2SE_VERSION = "";
63  
64      /** todo How do you get the j2ee version?? */
65      private static final String J2EE_VERSION = "1.3ee";
66  
67      private static Properties errorDocs = new Properties();
68      private static Properties errorCodes = new Properties();
69      private static Map reverseErrorCodes = null;
70      private static Map errorMappings = new HashMap();
71  
72      private static int exceptionThreshold = 0;
73      private static boolean verbose = true;
74  
75      private static boolean initialised = false;
76  
77      /**
78       * A list of the exception readers to use for different types of exceptions
79       */
80      private static List exceptionReaders = new ArrayList();
81  
82      /**
83       * The default ExceptionReader which will be used for most types of exceptions
84       */
85      private static ExceptionReader defaultExceptionReader = new DefaultExceptionReader();
86  
87      static
88      {
89          initialise();
90      }
91  
92      /** Do not instanciate. */
93      private ExceptionHelper ()
94      {
95          // no-op
96      }
97  
98      public static void initialise()
99      {
100         try
101         {
102             if (initialised)
103             {
104                 return;
105             }
106 
107             registerExceptionReader(new UMOExceptionReader());
108             registerExceptionReader(new NamingExceptionReader());
109             J2SE_VERSION = System.getProperty("java.specification.version");
110 
111             InputStream is = SpiUtils.findServiceDescriptor("org/mule/config",
112                 "mule-exception-codes.properties", ExceptionHelper.class);
113             if (is == null)
114             {
115                 throw new IllegalArgumentException(
116                     "Failed to load resource: META_INF/services/org/mule/config/mule-exception-codes.properties");
117             }
118             errorCodes.load(is);
119             reverseErrorCodes = MapUtils.invertMap(errorCodes);
120             is = SpiUtils.findServiceDescriptor("org/mule/config", "mule-exception-config.properties",
121                 ExceptionHelper.class);
122             if (is == null)
123             {
124                 throw new IllegalArgumentException(
125                     "Failed to load resource: META_INF/services/org/mule/config/mule-exception-config.properties");
126             }
127             errorDocs.load(is);
128 
129             initialised = true;
130         }
131         catch (IOException e)
132         {
133             throw new MuleRuntimeException(
134                 MessageFactory.createStaticMessage("Failed to load Exception resources"),
135                 e);
136         }
137     }
138 
139     public static int getErrorCode(Class exception)
140     {
141         String code = errorCodes.getProperty(exception.getName(), "-1");
142         return Integer.parseInt(code);
143     }
144 
145     public static Class getErrorClass(int code)
146     {
147         String key = String.valueOf(code);
148         Object clazz = reverseErrorCodes.get(key);
149         if (clazz == null)
150         {
151             return null;
152         }
153         else if (clazz instanceof Class)
154         {
155             return (Class) clazz;
156         }
157         else
158         {
159             try
160             {
161                 clazz = ClassUtils.loadClass(clazz.toString(), ExceptionHelper.class);
162             }
163             catch (ClassNotFoundException e)
164             {
165                 // TODO MULE-863: Either we handle this, or we don't
166                 logger.error(e.getMessage(), e);
167                 return null;
168             }
169             reverseErrorCodes.put(key, clazz);
170             return (Class) clazz;
171         }
172     }
173 
174     public static String getErrorMapping(String protocol, int code)
175     {
176         Class c = getErrorClass(code);
177         if (c != null)
178         {
179             return getErrorMapping(protocol, c);
180         }
181         else
182         {
183             logger.error("Class not known for code: " + code);
184             return "-1";
185         }
186     }
187 
188     private static Properties getErrorMappings(String protocol)
189     {
190         Object m = errorMappings.get(protocol);
191         if (m != null)
192         {
193             if (m instanceof Properties)
194             {
195                 return (Properties) m;
196             }
197             else
198             {
199                 return null;
200             }
201         }
202         else
203         {
204             InputStream is = SpiUtils.findServiceDescriptor("org/mule/config",
205                 protocol + "-exception-mappings.properties", ExceptionHelper.class);
206             if (is == null)
207             {
208                 errorMappings.put(protocol, "not found");
209                 logger.warn("Failed to load error mappings from: META-INF/services/org/mule/config/"
210                             + protocol
211                             + "-exception-mappings.properties. This may be because there are no error code mappings for protocol: "
212                             + protocol);
213                 return null;
214             }
215             Properties p = new Properties();
216             try
217             {
218                 p.load(is);
219             }
220             catch (IOException e)
221             {
222                 throw new MuleRuntimeException(
223                     MessageFactory.createStaticMessage("Failed to load Exception resources"), e);
224             }
225             errorMappings.put(protocol, p);
226             String applyTo = p.getProperty(APPLY_TO_PROPERTY, null);
227             if (applyTo != null)
228             {
229                 String[] protocols = StringUtils.splitAndTrim(applyTo, ",");
230                 for (int i = 0; i < protocols.length; i++)
231                 {
232                     errorMappings.put(protocols[i], p);
233                 }
234             }
235             return p;
236         }
237     }
238 
239     public static String getErrorCodePropertyName(String protocol)
240     {
241         protocol = protocol.toLowerCase();
242         Properties mappings = getErrorMappings(protocol);
243         if (mappings == null)
244         {
245             return null;
246         }
247         return mappings.getProperty(ERROR_CODE_PROPERTY);
248     }
249 
250     public static String getErrorMapping(String protocol, Class exception)
251     {
252         protocol = protocol.toLowerCase();
253         Properties mappings = getErrorMappings(protocol);
254         if (mappings == null)
255         {
256             logger.info("No mappings found for protocol: " + protocol);
257             return String.valueOf(getErrorCode(exception));
258         }
259 
260         Class clazz = exception;
261         String code = null;
262         while (!clazz.equals(Object.class))
263         {
264             code = mappings.getProperty(clazz.getName());
265             if (code == null)
266             {
267                 clazz = clazz.getSuperclass();
268             }
269             else
270             {
271                 return code;
272             }
273         }
274         code = String.valueOf(getErrorCode(exception));
275         // Finally lookup mapping based on error code and return the Mule error
276         // code if a match is not found
277         return mappings.getProperty(code, code);
278     }
279 
280     public static String getJavaDocUrl(Class exception)
281     {
282         return getDocUrl("javadoc.", exception.getName());
283     }
284 
285     public static String getDocUrl(Class exception)
286     {
287         return getDocUrl("doc.", exception.getName());
288     }
289 
290     private static String getDocUrl(String prefix, String packageName)
291     {
292         String key = prefix;
293         if (packageName.startsWith("java.") || packageName.startsWith("javax."))
294         {
295             key += J2SE_VERSION;
296         }
297         String url = getUrl(key, packageName);
298         if (url == null && (packageName.startsWith("java.") || packageName.startsWith("javax.")))
299         {
300             key = prefix + J2EE_VERSION;
301             url = getUrl(key, packageName);
302         }
303         if (url != null)
304         {
305             if (!url.endsWith("/"))
306             {
307                 url += "/";
308             }
309             String s = packageName.replaceAll("[.]", "/");
310             s += ".html";
311             url += s;
312         }
313 
314         /*
315          * If the exception is actually expected to occur by the calling code, the
316          * following output becomes misleading when reading the logs. if
317          * (logger.isDebugEnabled()) { if ("javadoc".equalsIgnoreCase(prefix)) {
318          * logger.debug("Javadoc Url for package '" + packageName + "' is: " + url); }
319          * else if ("doc".equalsIgnoreCase(prefix)) { logger.debug("Online Doc Url
320          * for package '" + packageName + "' is: " + url); } else {
321          * logger.debug(prefix + " Url for package '" + packageName + "' is: " +
322          * url); } }
323          */
324         return url;
325     }
326 
327     private static String getUrl(String key, String packageName)
328     {
329         String url = null;
330         if (!key.endsWith("."))
331         {
332             key += ".";
333         }
334         while (packageName.length() > 0)
335         {
336             url = errorDocs.getProperty(key + packageName, null);
337             if (url == null)
338             {
339                 int i = packageName.lastIndexOf(".");
340                 if (i == -1)
341                 {
342                     packageName = "";
343                 }
344                 else
345                 {
346                     packageName = packageName.substring(0, i);
347                 }
348             }
349             else
350             {
351                 break;
352             }
353         }
354         return url;
355     }
356 
357     public static Throwable getRootException(Throwable t)
358     {
359         Throwable cause = t;
360         Throwable root = null;
361         while (cause != null)
362         {
363             root = cause;
364             cause = getExceptionReader(cause).getCause(cause);
365             // address some misbehaving exceptions, avoid endless loop
366             if (t == cause)
367             {
368                 break;
369             }
370         }
371         return root;
372     }
373 
374     public static Throwable getRootParentException(Throwable t)
375     {
376         Throwable cause = t;
377         Throwable parent = t;
378         while (cause != null)
379         {
380             if (cause.getCause() == null)
381             {
382                 return parent;
383             }
384             parent = cause;
385             cause = getExceptionReader(cause).getCause(cause);
386             // address some misbehaving exceptions, avoid endless loop
387             if (t == cause)
388             {
389                 break;
390             }
391         }
392         return t;
393     }
394 
395     public static UMOException getRootMuleException(Throwable t)
396     {
397         Throwable cause = t;
398         UMOException umoException = null;
399         while (cause != null)
400         {
401             if (cause instanceof UMOException)
402             {
403                 umoException = (UMOException) cause;
404             }
405             cause = getExceptionReader(cause).getCause(cause);
406             // address some misbehaving exceptions, avoid endless loop
407             if (t == cause)
408             {
409                 break;
410             }
411         }
412         return umoException;
413     }
414 
415     public static List getExceptionsAsList(Throwable t)
416     {
417         List exceptions = new ArrayList();
418         Throwable cause = t;
419         while (cause != null)
420         {
421             exceptions.add(0, cause);
422             cause = getExceptionReader(cause).getCause(cause);
423             // address some misbehaving exceptions, avoid endless loop
424             if (t == cause)
425             {
426                 break;
427             }
428         }
429         return exceptions;
430     }
431 
432     public static Map getExceptionInfo(Throwable t)
433     {
434         Map info = new HashMap();
435         Throwable cause = t;
436         while (cause != null)
437         {
438             info.putAll(getExceptionReader(cause).getInfo(cause));
439             cause = getExceptionReader(cause).getCause(cause);
440             // address some misbehaving exceptions, avoid endless loop
441             if (t == cause)
442             {
443                 break;
444             }
445         }
446         return info;
447     }
448 
449     public static String getExceptionStack(Throwable t)
450     {
451         StringBuffer buf = new StringBuffer();
452         // get exception stack
453         List exceptions = getExceptionsAsList(t);
454 
455         int i = 1;
456         for (Iterator iterator = exceptions.iterator(); iterator.hasNext(); i++)
457         {
458             if (i > exceptionThreshold && exceptionThreshold > 0)
459             {
460                 buf.append("(").append(exceptions.size() - i + 1).append(" more...)");
461                 break;
462             }
463             Throwable throwable = (Throwable) iterator.next();
464             ExceptionReader er = getExceptionReader(throwable);
465             buf.append(i).append(". ").append(er.getMessage(throwable)).append(" (");
466             buf.append(throwable.getClass().getName()).append(")\n");
467             if (verbose && throwable.getStackTrace().length > 0)
468             {
469                 StackTraceElement e = throwable.getStackTrace()[0];
470                 buf.append("  ")
471                     .append(e.getClassName())
472                     .append(":")
473                     .append(e.getLineNumber())
474                     .append(" (")
475                     .append(getJavaDocUrl(throwable.getClass()))
476                     .append(")\n");
477             }
478         }
479         return buf.toString();
480     }
481 
482     /**
483      * Registers an exception reader with Mule
484      *
485      * @param reader the reader to register.
486      */
487     public static void registerExceptionReader(ExceptionReader reader)
488     {
489         exceptionReaders.add(reader);
490     }
491 
492     /**
493      * Gets an exception reader for the exception
494      *
495      * @param t the exception to get a reader for
496      * @return either a specific reader or an instance of DefaultExceptionReader.
497      *         This method never returns null;
498      */
499     public static ExceptionReader getExceptionReader(Throwable t)
500     {
501         for (Iterator iterator = exceptionReaders.iterator(); iterator.hasNext();)
502         {
503             ExceptionReader exceptionReader = (ExceptionReader) iterator.next();
504             if (exceptionReader.getExceptionType().isInstance(t))
505             {
506                 return exceptionReader;
507             }
508         }
509         return defaultExceptionReader;
510     }
511 
512     public static String writeException(Throwable t)
513     {
514         ExceptionReader er = getExceptionReader(t);
515         StringBuffer msg = new StringBuffer();
516         msg.append(er.getMessage(t)).append(". Type: ").append(t.getClass());
517         return msg.toString();
518     }
519 }