001: /*
002: * $Id: ExceptionHelper.java 10852 2008-02-18 14:31:26Z dirk.olmes $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.config;
012:
013: import org.mule.api.MuleException;
014: import org.mule.api.MuleRuntimeException;
015: import org.mule.api.config.ExceptionReader;
016: import org.mule.config.i18n.CoreMessages;
017: import org.mule.util.ClassUtils;
018: import org.mule.util.MapUtils;
019: import org.mule.util.SpiUtils;
020: import org.mule.util.StringUtils;
021:
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Map;
029: import java.util.Properties;
030:
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033:
034: /**
035: * <code>ExceptionHelper</code> provides a number of helper functions that can be
036: * useful for dealing with Mule exceptions. This class has 3 core functions - <p/> 1.
037: * ErrorCode lookup. A corresponding Mule error code can be found using for a given
038: * Mule exception 2. Addtional Error information such as Java doc url for a given
039: * exception can be resolved using this class 3. Error code mappings can be looked up
040: * by providing the the protocol to map to and the Mule exception.
041: */
042:
043: public final class ExceptionHelper {
044: /**
045: * This is the property to set the error code to no the message it is the
046: * property name the Transport provider uses set the set the error code on the
047: * underlying message
048: */
049: public static final String ERROR_CODE_PROPERTY = "error.code.property";
050:
051: /**
052: * a comma-separated list of other protocols the mappings in a file can be
053: * applied to
054: */
055: public static final String APPLY_TO_PROPERTY = "apply.to";
056:
057: /**
058: * logger used by this class
059: */
060: protected static final Log logger = LogFactory
061: .getLog(ExceptionHelper.class);
062:
063: private static String J2SE_VERSION = "";
064:
065: /** todo How do you get the j2ee version?? */
066: private static final String J2EE_VERSION = "1.3ee";
067:
068: private static Properties errorDocs = new Properties();
069: private static Properties errorCodes = new Properties();
070: private static Map reverseErrorCodes = null;
071: private static Map errorMappings = new HashMap();
072:
073: private static int exceptionThreshold = 0;
074: private static boolean verbose = true;
075:
076: private static boolean initialised = false;
077:
078: /**
079: * A list of the exception readers to use for different types of exceptions
080: */
081: private static List exceptionReaders = new ArrayList();
082:
083: /**
084: * The default ExceptionReader which will be used for most types of exceptions
085: */
086: private static ExceptionReader defaultExceptionReader = new DefaultExceptionReader();
087:
088: static {
089: initialise();
090: }
091:
092: /** Do not instanciate. */
093: private ExceptionHelper() {
094: // no-op
095: }
096:
097: private static void initialise() {
098: try {
099: if (initialised) {
100: return;
101: }
102:
103: registerExceptionReader(new MuleExceptionReader());
104: registerExceptionReader(new NamingExceptionReader());
105: J2SE_VERSION = System
106: .getProperty("java.specification.version");
107:
108: String name = SpiUtils.SERVICE_ROOT
109: + SpiUtils.EXCEPTION_SERVICE_PATH
110: + "mule-exception-codes.properties";
111: InputStream in = ExceptionHelper.class.getClassLoader()
112: .getResourceAsStream(name);
113: if (in == null) {
114: throw new IllegalArgumentException(
115: "Failed to load resource: " + name);
116: }
117: errorCodes.load(in);
118: in.close();
119:
120: reverseErrorCodes = MapUtils.invertMap(errorCodes);
121:
122: name = SpiUtils.SERVICE_ROOT
123: + SpiUtils.EXCEPTION_SERVICE_PATH
124: + "mule-exception-config.properties";
125: in = ExceptionHelper.class.getClassLoader()
126: .getResourceAsStream(name);
127: if (in == null) {
128: throw new IllegalArgumentException(
129: "Failed to load resource: " + name);
130: }
131: errorDocs.load(in);
132: in.close();
133:
134: initialised = true;
135: } catch (Exception e) {
136: throw new MuleRuntimeException(CoreMessages
137: .failedToLoad("Exception resources"), e);
138: }
139: }
140:
141: public static int getErrorCode(Class exception) {
142: String code = errorCodes.getProperty(exception.getName(), "-1");
143: return Integer.parseInt(code);
144: }
145:
146: public static Class getErrorClass(int code) {
147: String key = String.valueOf(code);
148: Object clazz = reverseErrorCodes.get(key);
149: if (clazz == null) {
150: return null;
151: } else if (clazz instanceof Class) {
152: return (Class) clazz;
153: } else {
154: try {
155: clazz = ClassUtils.loadClass(clazz.toString(),
156: ExceptionHelper.class);
157: } catch (ClassNotFoundException e) {
158: logger.error(e.getMessage(), e);
159: return null;
160: }
161: reverseErrorCodes.put(key, clazz);
162: return (Class) clazz;
163: }
164: }
165:
166: private static Properties getErrorMappings(String protocol) {
167: Object m = errorMappings.get(protocol);
168: if (m != null) {
169: if (m instanceof Properties) {
170: return (Properties) m;
171: } else {
172: return null;
173: }
174: } else {
175: String name = SpiUtils.SERVICE_ROOT
176: + SpiUtils.EXCEPTION_SERVICE_PATH + protocol
177: + "-exception-mappings.properties";
178: InputStream in = ExceptionHelper.class.getClassLoader()
179: .getResourceAsStream(name);
180: if (in == null) {
181: errorMappings.put(protocol, "not found");
182: logger
183: .warn("Failed to load error mappings from: "
184: + name
185: + " This may be because there are no error code mappings for protocol: "
186: + protocol);
187: return null;
188: }
189:
190: Properties p = new Properties();
191: try {
192: p.load(in);
193: in.close();
194: } catch (IOException iox) {
195: throw new IllegalArgumentException(
196: "Failed to load resource: " + name);
197: }
198:
199: errorMappings.put(protocol, p);
200: String applyTo = p.getProperty(APPLY_TO_PROPERTY, null);
201: if (applyTo != null) {
202: String[] protocols = StringUtils.splitAndTrim(applyTo,
203: ",");
204: for (int i = 0; i < protocols.length; i++) {
205: errorMappings.put(protocols[i], p);
206: }
207: }
208: return p;
209: }
210: }
211:
212: public static String getErrorCodePropertyName(String protocol) {
213: protocol = protocol.toLowerCase();
214: Properties mappings = getErrorMappings(protocol);
215: if (mappings == null) {
216: return null;
217: }
218: return mappings.getProperty(ERROR_CODE_PROPERTY);
219: }
220:
221: public static String getErrorMapping(String protocol,
222: Class exception) {
223: protocol = protocol.toLowerCase();
224: Properties mappings = getErrorMappings(protocol);
225: if (mappings == null) {
226: logger.info("No mappings found for protocol: " + protocol);
227: return String.valueOf(getErrorCode(exception));
228: }
229:
230: Class clazz = exception;
231: String code = null;
232: while (!clazz.equals(Object.class)) {
233: code = mappings.getProperty(clazz.getName());
234: if (code == null) {
235: clazz = clazz.getSuperclass();
236: } else {
237: return code;
238: }
239: }
240: code = String.valueOf(getErrorCode(exception));
241: // Finally lookup mapping based on error code and return the Mule error
242: // code if a match is not found
243: return mappings.getProperty(code, code);
244: }
245:
246: public static String getJavaDocUrl(Class exception) {
247: return getDocUrl("javadoc.", exception.getName());
248: }
249:
250: public static String getDocUrl(Class exception) {
251: return getDocUrl("doc.", exception.getName());
252: }
253:
254: private static String getDocUrl(String prefix, String packageName) {
255: String key = prefix;
256: if (packageName.startsWith("java.")
257: || packageName.startsWith("javax.")) {
258: key += J2SE_VERSION;
259: }
260: String url = getUrl(key, packageName);
261: if (url == null
262: && (packageName.startsWith("java.") || packageName
263: .startsWith("javax."))) {
264: key = prefix + J2EE_VERSION;
265: url = getUrl(key, packageName);
266: }
267: if (url != null) {
268: if (!url.endsWith("/")) {
269: url += "/";
270: }
271: String s = packageName.replaceAll("[.]", "/");
272: s += ".html";
273: url += s;
274: }
275: return url;
276: }
277:
278: private static String getUrl(String key, String packageName) {
279: String url = null;
280: if (!key.endsWith(".")) {
281: key += ".";
282: }
283: while (packageName.length() > 0) {
284: url = errorDocs.getProperty(key + packageName, null);
285: if (url == null) {
286: int i = packageName.lastIndexOf(".");
287: if (i == -1) {
288: packageName = "";
289: } else {
290: packageName = packageName.substring(0, i);
291: }
292: } else {
293: break;
294: }
295: }
296: return url;
297: }
298:
299: public static Throwable getRootException(Throwable t) {
300: Throwable cause = t;
301: Throwable root = null;
302: while (cause != null) {
303: root = cause;
304: cause = getExceptionReader(cause).getCause(cause);
305: // address some misbehaving exceptions, avoid endless loop
306: if (t == cause) {
307: break;
308: }
309: }
310: return root;
311: }
312:
313: public static Throwable getRootParentException(Throwable t) {
314: Throwable cause = t;
315: Throwable parent = t;
316: while (cause != null) {
317: if (cause.getCause() == null) {
318: return parent;
319: }
320: parent = cause;
321: cause = getExceptionReader(cause).getCause(cause);
322: // address some misbehaving exceptions, avoid endless loop
323: if (t == cause) {
324: break;
325: }
326: }
327: return t;
328: }
329:
330: public static MuleException getRootMuleException(Throwable t) {
331: Throwable cause = t;
332: MuleException umoException = null;
333: while (cause != null) {
334: if (cause instanceof MuleException) {
335: umoException = (MuleException) cause;
336: }
337: cause = getExceptionReader(cause).getCause(cause);
338: // address some misbehaving exceptions, avoid endless loop
339: if (t == cause) {
340: break;
341: }
342: }
343: return umoException;
344: }
345:
346: public static List getExceptionsAsList(Throwable t) {
347: List exceptions = new ArrayList();
348: Throwable cause = t;
349: while (cause != null) {
350: exceptions.add(0, cause);
351: cause = getExceptionReader(cause).getCause(cause);
352: // address some misbehaving exceptions, avoid endless loop
353: if (t == cause) {
354: break;
355: }
356: }
357: return exceptions;
358: }
359:
360: public static Map getExceptionInfo(Throwable t) {
361: Map info = new HashMap();
362: Throwable cause = t;
363: while (cause != null) {
364: info.putAll(getExceptionReader(cause).getInfo(cause));
365: cause = getExceptionReader(cause).getCause(cause);
366: // address some misbehaving exceptions, avoid endless loop
367: if (t == cause) {
368: break;
369: }
370: }
371: return info;
372: }
373:
374: public static String getExceptionStack(Throwable t) {
375: StringBuffer buf = new StringBuffer();
376: // get exception stack
377: List exceptions = getExceptionsAsList(t);
378:
379: int i = 1;
380: for (Iterator iterator = exceptions.iterator(); iterator
381: .hasNext(); i++) {
382: if (i > exceptionThreshold && exceptionThreshold > 0) {
383: buf.append("(").append(exceptions.size() - i + 1)
384: .append(" more...)");
385: break;
386: }
387: Throwable throwable = (Throwable) iterator.next();
388: ExceptionReader er = getExceptionReader(throwable);
389: buf.append(i).append(". ").append(er.getMessage(throwable))
390: .append(" (");
391: buf.append(throwable.getClass().getName()).append(")\n");
392: if (verbose && throwable.getStackTrace().length > 0) {
393: StackTraceElement e = throwable.getStackTrace()[0];
394: buf.append(" ").append(e.getClassName()).append(":")
395: .append(e.getLineNumber()).append(" (").append(
396: getJavaDocUrl(throwable.getClass()))
397: .append(")\n");
398: }
399: }
400: return buf.toString();
401: }
402:
403: /**
404: * Registers an exception reader with Mule
405: *
406: * @param reader the reader to register.
407: */
408: public static void registerExceptionReader(ExceptionReader reader) {
409: exceptionReaders.add(reader);
410: }
411:
412: /**
413: * Gets an exception reader for the exception
414: *
415: * @param t the exception to get a reader for
416: * @return either a specific reader or an instance of DefaultExceptionReader.
417: * This method never returns null;
418: */
419: public static ExceptionReader getExceptionReader(Throwable t) {
420: for (Iterator iterator = exceptionReaders.iterator(); iterator
421: .hasNext();) {
422: ExceptionReader exceptionReader = (ExceptionReader) iterator
423: .next();
424: if (exceptionReader.getExceptionType().isInstance(t)) {
425: return exceptionReader;
426: }
427: }
428: return defaultExceptionReader;
429: }
430:
431: public static String writeException(Throwable t) {
432: ExceptionReader er = getExceptionReader(t);
433: StringBuffer msg = new StringBuffer();
434: msg.append(er.getMessage(t)).append(". Type: ").append(
435: t.getClass());
436: return msg.toString();
437: }
438: }
|