View Javadoc

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