001 /*
002 * $Id: ErrorInfo.java 3100 2008-10-14 22:33:10Z rah003 $
003 *
004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020 */
021
022 package org.jdesktop.swingx.error;
023
024 import java.util.HashMap;
025 import java.util.Map;
026 import java.util.Properties;
027 import java.util.logging.Level;
028
029 import javax.swing.SwingUtilities;
030
031 /**
032 * <p>A simple class that encapsulates all the information needed
033 * to report a problem using the automated report/processing system.</p>
034 *
035 * <p>All HTML referred to in this API refers to version 3.2 of the HTML
036 * markup specification.</p>
037 *
038 * <p>Both basicErrorMessage and detailedErrorMessage may be specified with
039 * variable substitution. For example, this is a valid error message string:
040 * "${os.version} is not supported". Such variables are resovled using the
041 * <code>substituteVariables</code> method. This method will use properties in
042 * the "state" map to replace these variables.</p>
043 *
044 * <p>For example:
045 * <pre><code>
046 * String message = "An error occured on ${os.name} version ${os.version}";
047 * //auto creates the state map, populated with all System properties.
048 * //Sandboxed apps can't read System properties.
049 * ErrorInfo info = new ErrorInfo("Error", exception);
050 * message = info.substituteVariables(message);
051 *
052 * //prints out: "An error occured on Mac OS X version 10.4.7" on some systems
053 * log.info(message);
054 * </code></pre></p>
055 *
056 * @status REVIEWED
057 * @author Alexander Zuev
058 * @author rbair
059 */
060 public class ErrorInfo {
061 /**
062 * Short string that will be used as a error title
063 */
064 private String title;
065 /**
066 * Basic message that describes incident
067 */
068 private String basicErrorMessage;
069 /**
070 * Message that will fully describe the incident with all the
071 * available details
072 */
073 private String detailedErrorMessage;
074 /**
075 * A category name, indicating where in the application this incident
076 * occurred. It is recommended that this be the same value as you
077 * would use when logging.
078 */
079 private String category;
080 /**
081 * Optional Throwable that will be used as a possible source for
082 * additional information
083 */
084 private Throwable errorException;
085 /**
086 * Used to specify how bad this error was.
087 */
088 private Level errorLevel;
089 /**
090 * A Map which captures the state of the application
091 * at the time of an exception. This state is then available for error
092 * reports.
093 */
094 private Map<String,String> state;
095
096 /**
097 * Creates a new ErrorInfo based on the provided data.
098 *
099 * @param title used as a quick reference for the
100 * error (for example, it might be used as the
101 * title of an error dialog or as the subject of
102 * an email message). May be null.
103 *
104 * @param basicErrorMessage short description of the problem. May be null.
105 *
106 * @param detailedErrorMessage full description of the problem. It is recommended,
107 * though not required, that this String contain HTML
108 * to improve the look and layout of the detailed
109 * error message. May be null.
110 *
111 * @param category A category name, indicating where in the application
112 * this incident occurred. It is recommended that
113 * this be the same value as you would use when logging.
114 * May be null.
115 *
116 * @param errorException <code>Throwable</code> that can be used as a
117 * source for additional information such as call
118 * stack, thread name, etc. May be null.
119 *
120 * @param errorLevel any Level (Level.SEVERE, Level.WARNING, etc).
121 * If null, then the level will be set to SEVERE.
122 *
123 * @param state the state of the application at the time the incident occured.
124 * The standard System properties are automatically added to this
125 * state, and thus do not need to be included. This value may be null.
126 * If null, the resulting map will contain only the System properties.
127 * If there is a value in the map with a key that also occurs in the
128 * System properties (for example: sun.java2d.noddraw), then the
129 * developer supplied value will be used. In other words, defined
130 * parameters override standard ones. In addition, the keys
131 * "System.currentTimeMillis" and "isOnEDT" are both defined
132 * automatically.
133 */
134 public ErrorInfo(String title, String basicErrorMessage, String detailedErrorMessage,
135 String category, Throwable errorException, Level errorLevel, Map<String,String> state) {
136 this.title = title;
137 this.basicErrorMessage = basicErrorMessage;
138 this.detailedErrorMessage = detailedErrorMessage;
139 this.category = category;
140 this.errorException = errorException;
141 this.errorLevel = errorLevel == null ? Level.SEVERE : errorLevel;
142 this.state = new HashMap<String,String>();
143
144 //first add all the System properties
145 try {
146 //NOTE: This is not thread safe because System.getProperties() does not appear
147 //to create a copy of the map. Thus, another thread could be modifying the System
148 //properties and the "state" at the time of this exception may not be
149 //accurate!
150 Properties props = System.getProperties();
151 for (Map.Entry<Object, Object> entry : props.entrySet()) {
152 String key = entry.getKey() == null ? null : entry.getKey().toString();
153 String val = entry.getKey() == null ? null : entry.getValue().toString();
154 if (key != null) {
155 this.state.put(key, val);
156 }
157 }
158 } catch (SecurityException e) {
159 //probably running in a sandbox, don't worry about this
160 }
161
162 //add the automatically supported properties
163 this.state.put("System.currentTimeMillis", "" + System.currentTimeMillis());
164 this.state.put("isOnEDT", "" + SwingUtilities.isEventDispatchThread());
165
166 //now add all the data in the param "state". Thus, if somebody specified a key in the
167 //state map, it overrides whatever was in the System map
168 if (state != null) {
169 for (Map.Entry<String,String> entry : state.entrySet()) {
170 this.state.put(entry.getKey(), entry.getValue());
171 }
172 }
173 }
174
175 /**
176 * Gets the string to use for a dialog title or other quick reference. Used
177 * as a quick reference for the incident. For example, it might be used as the
178 * title of an error dialog or as the subject of an email message.
179 *
180 * @return quick reference String. May be null.
181 */
182 public String getTitle() {
183 return title;
184 }
185
186 /**
187 * <p>Gets the basic error message. This message should be clear and user oriented.
188 * This String may have HTML formatting, but any such formatting should be used
189 * sparingly. Generally, such formatting makes sense for making certain words bold,
190 * but should not be used for page layout or other such things.</p>
191 *
192 * <p>For example, the following are perfectly acceptable basic error messages:
193 * <pre>
194 * "Your camera cannot be located. Please make sure that it is powered on
195 * and that it is connected to this computer. Consult the instructions
196 * provided with your camera to make sure you are using the appropriate
197 * cable for attaching the camera to this computer"
198 *
199 * "<html>You are running on <b>reserver</b> battery
200 * power. Please plug into a power source immediately, or your work may
201 * be lost!</html>"
202 * </pre></p>
203 *
204 * @return basic error message or null
205 */
206 public String getBasicErrorMessage() {
207 return basicErrorMessage;
208 }
209
210 /**
211 * <p>Gets the detailed error message. Unlike {@link #getBasicErrorMessage},
212 * this method may return a more technical message to the user. However, it
213 * should still be user oriented. This String should be formatted using basic
214 * HTML to improve readability as necessary.</p>
215 *
216 * <p>This method may return null.</p>
217 *
218 * @return detailed error message or null
219 */
220 public String getDetailedErrorMessage() {
221 return detailedErrorMessage;
222 }
223
224 /**
225 * Gets the category name. This value indicates where in the application
226 * this incident occurred. It is recommended that this be the same value as
227 * you would use when logging. This may be null.
228 *
229 * @return the category. May be null.
230 */
231 public String getCategory() {
232 return category;
233 }
234
235 /**
236 * Gets the actual exception that generated the error. If this returns a
237 * non null value, then {@link #getBasicErrorMessage} may return a null value.
238 * If this returns a non null value and {@link #getDetailedErrorMessage} returns
239 * a null value, then this returned <code>Throwable</code> may be used as the
240 * basis for the detailed error message (generally by showing the stack trace).
241 *
242 * @return exception or null
243 */
244 public Throwable getErrorException() {
245 return errorException;
246 }
247
248 /**
249 * Gets the severity of the error. The default level is <code>Level.SEVERE</code>,
250 * but any {@link Level} may be specified when constructing an
251 * <code>ErrorInfo</code>.
252 *
253 * @return the error level. This will never be null
254 */
255 public Level getErrorLevel() {
256 return errorLevel;
257 }
258
259 /**
260 * <p>Gets a copy of the application state at the time that the incident occured.
261 * This map will never be null. If running with appropriate permissions the
262 * map will contain all the System properties. In addition, it contains two
263 * keys, "System.currentTimeMillis" and "isOnEDT".</p>
264 *
265 * <p>Warning: The System.properties <em>may not</em> contain the exact set
266 * of System properties at the time the exception occured. This is due to the
267 * nature of System.getProperties() and the Properties collection. While they
268 * are property synchronized, it is possible that while iterating the set of
269 * properties in the ErrorInfo constructor that some other code can change
270 * the properties on another thread. This is unlikely to occur, but in some
271 * applications <em>may</em> occur.</p>
272 *
273 * @return a copy of the application state. This will never be null.
274 */
275 public Map<String,String> getState() {
276 return new HashMap<String,String>(state);
277 }
278 }