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         *      "&lt;html&gt;You are running on &lt;b&gt;reserver&lt;/b&gt; battery
200         *       power. Please plug into a power source immediately, or your work may
201         *       be lost!&lt;/html&gt;"
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    }