| Reporter.java |
1 /**
2 * Distribution License:
3 * JSword is free software; you can redistribute it and/or modify it under
4 * the terms of the GNU Lesser General Public License, version 2.1 as published by
5 * the Free Software Foundation. This program is distributed in the hope
6 * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8 * See the GNU Lesser General Public License for more details.
9 *
10 * The License is available on the internet at:
11 * http://www.gnu.org/copyleft/lgpl.html
12 * or by writing to:
13 * Free Software Foundation, Inc.
14 * 59 Temple Place - Suite 330
15 * Boston, MA 02111-1307, USA
16 *
17 * Copyright: 2005
18 * The copyright to this program is held by it's authors.
19 *
20 * ID: $Id: Reporter.java 2099 2011-03-07 17:13:00Z dmsmith $
21 */
22 package org.crosswire.common.util;
23
24 import java.util.Properties;
25
26 import org.crosswire.jsword.JSMsg;
27
28 /**
29 * This package looks after Exceptions and messages as they happen. It would be
30 * nice not to need this class - the principle being that any library that
31 * encounters an error can throw an exception to indicate that there is a
32 * problem. However this is not always the case. For example:
33 * <ul>
34 * <li>static class constructors should not throw, unless the class really is of
35 * no use given the error, and yet we may want to tell the user that there was a
36 * (non-critical) error.</li>
37 * <li>Any library routine that works in a loop, applying some (potentially
38 * failing) functionality, may want to continue the work without throwing in
39 * response to a single error.</li>
40 * <li>The class being implemented may implement an interface that disallows
41 * nested exceptions and yet does not want to loose the root cause error
42 * information. (This is the weakest of the above arguments, but probably still
43 * valid.)</li> However in many of the times this class is used, this is the
44 * reason:
45 * <li>Within UI specific code - to throw up a dialog box (or whatever). Now
46 * this use is currently tolerated, however it is probably a poor idea to use
47 * GUI agnostic messaging in a GUI specific context. But I'm not bothered enough
48 * to change it now. Specifically this use is deprecated because it makes the
49 * app more susceptible to the configuration of the things that listen to
50 * reports.</li>
51 * </ul>
52 *
53 * @see gnu.lgpl.License for license details.<br>
54 * The copyright to this program is held by it's authors.
55 * @author Joe Walker [joe at eireneh dot com]
56 */
57 public final class Reporter {
58 /**
59 * Enforce Singleton
60 */
61 private Reporter() {
62 }
63
64 /**
65 * Something has gone wrong. We need to tell the user or someone, but we can
66 * carry on. In general having caught an exception and passed it to
67 * Reporter.informUser(), you should not throw another Exception. Called to
68 * fire a commandEntered event to all the Listeners
69 *
70 * @param source
71 * The cause of the problem, a Component if possible.
72 * @param prob
73 */
74 public static void informUser(Object source, Throwable prob) {
75 Class<?> cat = (source != null) ? source.getClass() : Reporter.class;
76 Logger templog = Logger.getLogger(cat);
77
78 templog.warn(prob.getMessage(), prob);
79
80 fireCapture(new ReporterEvent(source, prob));
81 }
82
83 /**
84 * Something has gone wrong. We need to tell the user or someone, but we can
85 * carry on. In general having caught an exception and passed it to
86 * Reporter.informUser(), you should not throw another Exception. Called to
87 * fire a commandEntered event to all the Listeners
88 *
89 * @param source
90 * The cause of the problem, a Component if possible.
91 * @param prob
92 * The Exception that was thrown
93 */
94 public static void informUser(Object source, LucidException prob) {
95 Class<?> cat = (source != null) ? source.getClass() : Reporter.class;
96 Logger templog = Logger.getLogger(cat);
97
98 templog.warn(prob.getMessage(), prob);
99
100 fireCapture(new ReporterEvent(source, prob));
101 }
102
103 /**
104 * Something has gone wrong. We need to tell the user or someone, but we can
105 * carry on. In general having caught an exception and passed it to
106 * Reporter.informUser(), you should not throw another Exception. Called to
107 * fire a commandEntered event to all the Listeners
108 *
109 * @param source
110 * The cause of the problem, a Component if possible.
111 * @param prob
112 * The Exception that was thrown
113 */
114 public static void informUser(Object source, LucidRuntimeException prob) {
115 Class<?> cat = (source != null) ? source.getClass() : Reporter.class;
116 Logger templog = Logger.getLogger(cat);
117
118 templog.warn(prob.getMessage(), prob);
119
120 fireCapture(new ReporterEvent(source, prob));
121 }
122
123 /**
124 * Something has happened. We need to tell the user or someone.
125 *
126 * @param source
127 * The cause of the message, a Component if possible.
128 * @param message
129 * The message to pass to the user
130 */
131 public static void informUser(Object source, String message) {
132 log.debug(message);
133
134 fireCapture(new ReporterEvent(source, message));
135 }
136
137 /**
138 * Add an Exception listener to the list of things wanting to know whenever
139 * we capture an Exception
140 */
141 public static void addReporterListener(ReporterListener li) {
142 LISTENERS.add(ReporterListener.class, li);
143 }
144
145 /**
146 * Remove an Exception listener from the list of things wanting to know
147 * whenever we capture an Exception
148 */
149 public static void removeReporterListener(ReporterListener li) {
150 LISTENERS.remove(ReporterListener.class, li);
151 }
152
153 /**
154 * Log a message.
155 */
156 protected static void fireCapture(ReporterEvent ev) {
157 // Guaranteed to return a non-null array
158 Object[] liArr = LISTENERS.getListenerList();
159
160 if (liArr.length == 0) {
161 log.warn("Nothing to listen to report: message=" + ev.getMessage(), ev.getException());
162 }
163
164 // Process the listeners last to first, notifying
165 // those that are interested in this event
166 for (int i = liArr.length - 2; i >= 0; i -= 2) {
167 if (liArr[i] == ReporterListener.class) {
168 ReporterListener li = (ReporterListener) liArr[i + 1];
169 if (ev.getException() != null) {
170 li.reportException(ev);
171 } else {
172 li.reportMessage(ev);
173 }
174 }
175 }
176 }
177
178 /**
179 * Sets the parent of any exception windows.
180 */
181 public static void grabAWTExecptions(boolean grab) {
182 if (grab) {
183 // register ourselves
184 System.setProperty(AWT_HANDLER_PROPERTY, OUR_NAME);
185 } else {
186 // deregister ourselves
187 String current = System.getProperty(AWT_HANDLER_PROPERTY);
188 if (current != null && current.equals(OUR_NAME)) {
189 Properties prop = System.getProperties();
190 prop.remove(AWT_HANDLER_PROPERTY);
191 }
192 }
193 }
194
195 /**
196 * The system property name for registering AWT exceptions
197 */
198 private static final String AWT_HANDLER_PROPERTY = "sun.awt.exception.handler";
199
200 /**
201 * The name of the class to register for AWT exceptions
202 */
203 private static final String OUR_NAME = CustomAWTExceptionHandler.class.getName();
204
205 /**
206 * The log stream
207 */
208 private static final Logger log = Logger.getLogger(Reporter.class);
209
210 /**
211 * The list of listeners
212 */
213 private static final EventListenerList LISTENERS = new EventListenerList();
214
215 /**
216 * A class to handle AWT caught Exceptions
217 */
218 public static final class CustomAWTExceptionHandler {
219 /**
220 * Its important that we have a no-arg ctor to make this work. So if we
221 * ever create an arged ctor then we need to add: public
222 * CustomAWTExceptionHandler() { }
223 */
224
225 /**
226 * Handle AWT exceptions
227 */
228 public void handle(Throwable ex) {
229 // Only allow one to be reported every so often.
230 // This interval control was needed because AWT exceptions
231 // were causing recursive AWT exceptions
232 // and way too many dialogs were being thrown up on the screen.
233 if (gate.open()) {
234 // TRANSLATOR: Very frequent error condition: The program has encountered a severe problem and it is likely that the program is unusable.
235 Reporter.informUser(this, new LucidException(JSMsg.gettext("Unexpected internal problem. You may need to restart."), ex));
236 }
237 }
238
239 private static TimeGate gate = new TimeGate(2000);
240 }
241 }
242