/*
* $Id: ErrorInfo.java 3100 2008-10-14 22:33:10Z rah003 $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.hdesktop.swingx.error;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import javax.swing.SwingUtilities;
/**
* <p>A simple class that encapsulates all the information needed
* to report a problem using the automated report/processing system.</p>
*
* <p>All HTML referred to in this API refers to version 3.2 of the HTML
* markup specification.</p>
*
* <p>Both basicErrorMessage and detailedErrorMessage may be specified with
* variable substitution. For example, this is a valid error message string:
* "${os.version} is not supported". Such variables are resovled using the
* <code>substituteVariables</code> method. This method will use properties in
* the "state" map to replace these variables.</p>
*
* <p>For example:
* <pre><code>
* String message = "An error occured on ${os.name} version ${os.version}";
* //auto creates the state map, populated with all System properties.
* //Sandboxed apps can't read System properties.
* ErrorInfo info = new ErrorInfo("Error", exception);
* message = info.substituteVariables(message);
*
* //prints out: "An error occured on Mac OS X version 10.4.7" on some systems
* log.info(message);
* </code></pre></p>
*
* @status REVIEWED
* @author Alexander Zuev
* @author rbair
*/
public class ErrorInfo {
/**
* Short string that will be used as a error title
*/
private String title;
/**
* Basic message that describes incident
*/
private String basicErrorMessage;
/**
* Message that will fully describe the incident with all the
* available details
*/
private String detailedErrorMessage;
/**
* A category name, indicating where in the application this incident
* occurred. It is recommended that this be the same value as you
* would use when logging.
*/
private String category;
/**
* Optional Throwable that will be used as a possible source for
* additional information
*/
private Throwable errorException;
/**
* Used to specify how bad this error was.
*/
private Level errorLevel;
/**
* A Map which captures the state of the application
* at the time of an exception. This state is then available for error
* reports.
*/
private Map<String,String> state;
/**
* Creates a new ErrorInfo based on the provided data.
*
* @param title used as a quick reference for the
* error (for example, it might be used as the
* title of an error dialog or as the subject of
* an email message). May be null.
*
* @param basicErrorMessage short description of the problem. May be null.
*
* @param detailedErrorMessage full description of the problem. It is recommended,
* though not required, that this String contain HTML
* to improve the look and layout of the detailed
* error message. May be null.
*
* @param category A category name, indicating where in the application
* this incident occurred. It is recommended that
* this be the same value as you would use when logging.
* May be null.
*
* @param errorException <code>Throwable</code> that can be used as a
* source for additional information such as call
* stack, thread name, etc. May be null.
*
* @param errorLevel any Level (Level.SEVERE, Level.WARNING, etc).
* If null, then the level will be set to SEVERE.
*
* @param state the state of the application at the time the incident occured.
* The standard System properties are automatically added to this
* state, and thus do not need to be included. This value may be null.
* If null, the resulting map will contain only the System properties.
* If there is a value in the map with a key that also occurs in the
* System properties (for example: sun.java2d.noddraw), then the
* developer supplied value will be used. In other words, defined
* parameters override standard ones. In addition, the keys
* "System.currentTimeMillis" and "isOnEDT" are both defined
* automatically.
*/
public ErrorInfo(String title, String basicErrorMessage, String detailedErrorMessage,
String category, Throwable errorException, Level errorLevel, Map<String,String> state) {
this.title = title;
this.basicErrorMessage = basicErrorMessage;
this.detailedErrorMessage = detailedErrorMessage;
this.category = category;
this.errorException = errorException;
this.errorLevel = errorLevel == null ? Level.SEVERE : errorLevel;
this.state = new HashMap<String,String>();
//first add all the System properties
try {
//NOTE: This is not thread safe because System.getProperties() does not appear
//to create a copy of the map. Thus, another thread could be modifying the System
//properties and the "state" at the time of this exception may not be
//accurate!
Properties props = System.getProperties();
for (Map.Entry<Object, Object> entry : props.entrySet()) {
String key = entry.getKey() == null ? null : entry.getKey().toString();
String val = entry.getKey() == null ? null : entry.getValue().toString();
if (key != null) {
this.state.put(key, val);
}
}
} catch (SecurityException e) {
//probably running in a sandbox, don't worry about this
}
//add the automatically supported properties
this.state.put("System.currentTimeMillis", "" + System.currentTimeMillis());
this.state.put("isOnEDT", "" + SwingUtilities.isEventDispatchThread());
//now add all the data in the param "state". Thus, if somebody specified a key in the
//state map, it overrides whatever was in the System map
if (state != null) {
for (Map.Entry<String,String> entry : state.entrySet()) {
this.state.put(entry.getKey(), entry.getValue());
}
}
}
/**
* Gets the string to use for a dialog title or other quick reference. Used
* as a quick reference for the incident. For example, it might be used as the
* title of an error dialog or as the subject of an email message.
*
* @return quick reference String. May be null.
*/
public String getTitle() {
return title;
}
/**
* <p>Gets the basic error message. This message should be clear and user oriented.
* This String may have HTML formatting, but any such formatting should be used
* sparingly. Generally, such formatting makes sense for making certain words bold,
* but should not be used for page layout or other such things.</p>
*
* <p>For example, the following are perfectly acceptable basic error messages:
* <pre>
* "Your camera cannot be located. Please make sure that it is powered on
* and that it is connected to this computer. Consult the instructions
* provided with your camera to make sure you are using the appropriate
* cable for attaching the camera to this computer"
*
* "<html>You are running on <b>reserver</b> battery
* power. Please plug into a power source immediately, or your work may
* be lost!</html>"
* </pre></p>
*
* @return basic error message or null
*/
public String getBasicErrorMessage() {
return basicErrorMessage;
}
/**
* <p>Gets the detailed error message. Unlike {@link #getBasicErrorMessage},
* this method may return a more technical message to the user. However, it
* should still be user oriented. This String should be formatted using basic
* HTML to improve readability as necessary.</p>
*
* <p>This method may return null.</p>
*
* @return detailed error message or null
*/
public String getDetailedErrorMessage() {
return detailedErrorMessage;
}
/**
* Gets the category name. This value indicates where in the application
* this incident occurred. It is recommended that this be the same value as
* you would use when logging. This may be null.
*
* @return the category. May be null.
*/
public String getCategory() {
return category;
}
/**
* Gets the actual exception that generated the error. If this returns a
* non null value, then {@link #getBasicErrorMessage} may return a null value.
* If this returns a non null value and {@link #getDetailedErrorMessage} returns
* a null value, then this returned <code>Throwable</code> may be used as the
* basis for the detailed error message (generally by showing the stack trace).
*
* @return exception or null
*/
public Throwable getErrorException() {
return errorException;
}
/**
* Gets the severity of the error. The default level is <code>Level.SEVERE</code>,
* but any {@link Level} may be specified when constructing an
* <code>ErrorInfo</code>.
*
* @return the error level. This will never be null
*/
public Level getErrorLevel() {
return errorLevel;
}
/**
* <p>Gets a copy of the application state at the time that the incident occured.
* This map will never be null. If running with appropriate permissions the
* map will contain all the System properties. In addition, it contains two
* keys, "System.currentTimeMillis" and "isOnEDT".</p>
*
* <p>Warning: The System.properties <em>may not</em> contain the exact set
* of System properties at the time the exception occured. This is due to the
* nature of System.getProperties() and the Properties collection. While they
* are property synchronized, it is possible that while iterating the set of
* properties in the ErrorInfo constructor that some other code can change
* the properties on another thread. This is unlikely to occur, but in some
* applications <em>may</em> occur.</p>
*
* @return a copy of the application state. This will never be null.
*/
public Map<String,String> getState() {
return new HashMap<String,String>(state);
}
}