001 /*
002 * Copyright 2008-2013 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package griffon.util;
017
018 import griffon.core.GriffonApplication;
019 import org.codehaus.groovy.runtime.StackTraceUtils;
020 import org.slf4j.Logger;
021 import org.slf4j.LoggerFactory;
022
023 import java.util.Map;
024
025 import static java.util.Arrays.asList;
026
027 /**
028 * Catches and sanitizes all uncaught exceptions.
029 *
030 * @author Danno Ferrin
031 * @author Andres Almiray
032 */
033 public class GriffonExceptionHandler implements Thread.UncaughtExceptionHandler {
034 /**
035 * "griffon.full.stacktrace"
036 */
037 public static final String GRIFFON_FULL_STACKTRACE = "griffon.full.stacktrace";
038 /**
039 * "griffon.exception.output"
040 */
041 public static final String GRIFFON_EXCEPTION_OUTPUT = "griffon.exception.output";
042
043 private static final Logger LOG = LoggerFactory.getLogger(GriffonExceptionHandler.class);
044 private static final String[] CONFIG_OPTIONS = {
045 GRIFFON_FULL_STACKTRACE,
046 GRIFFON_EXCEPTION_OUTPUT
047 };
048
049 public void uncaughtException(Thread t, Throwable e) {
050 handle(e);
051 }
052
053 public void handle(Throwable throwable) {
054 try {
055 sanitize(throwable);
056 if (isOutputEnabled()) throwable.printStackTrace(System.err);
057 GriffonApplication app = ApplicationHolder.getApplication();
058 if (app != null) {
059 LOG.error("Uncaught Exception", throwable);
060 app.event("Uncaught" + GriffonNameUtils.getShortName(throwable.getClass()), asList(throwable));
061 app.event(GriffonApplication.Event.UNCAUGHT_EXCEPTION_THROWN.getName(), asList(throwable));
062 }
063 } catch (Throwable t) {
064 sanitize(t);
065 if (isOutputEnabled()) t.printStackTrace(System.err);
066 LOG.error("An error occurred while handling uncaught exception " + throwable, t);
067 }
068 }
069
070 public static Throwable sanitize(Throwable throwable) {
071 try {
072 if (!Boolean.getBoolean(GRIFFON_FULL_STACKTRACE)) StackTraceUtils.deepSanitize(throwable);
073 } catch (Throwable t) {
074 // don't let the exception get thrown out, will cause infinite looping!
075 }
076 return throwable;
077 }
078
079 public static StackTraceElement[] sanitize(StackTraceElement[] stackTrace) {
080 try {
081 if (!Boolean.getBoolean(GRIFFON_FULL_STACKTRACE)) {
082 Throwable t = new Throwable();
083 t.setStackTrace(stackTrace);
084 sanitize(t);
085 stackTrace = t.getStackTrace();
086 }
087 } catch (Throwable o) {
088 // don't let the exception get thrown out, will cause infinite looping!
089 }
090 return stackTrace;
091 }
092
093 public static boolean isOutputEnabled() {
094 return Boolean.getBoolean(GRIFFON_EXCEPTION_OUTPUT);
095 }
096
097 public static void configure(Map config) {
098 for (String option : CONFIG_OPTIONS) {
099 if (config.containsKey(option)) {
100 System.setProperty(option, String.valueOf(config.get(option)));
101 }
102 }
103 }
104
105 public static void registerExceptionHandler() {
106 Thread.setDefaultUncaughtExceptionHandler(new GriffonExceptionHandler());
107 System.setProperty("sun.awt.exception.handler", GriffonExceptionHandler.class.getName());
108 }
109
110 public static void handleThrowable(Throwable t) {
111 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(
112 Thread.currentThread(),
113 t
114 );
115 }
116 }
|