Tidy up contexted exception classes, better Javadoc, final variables, etc.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@829400 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Colebourne 2009-10-24 16:27:30 +00:00
parent f61c229c74
commit 926ed9ea0e
4 changed files with 239 additions and 183 deletions

View File

@ -20,31 +20,25 @@
import java.util.Set;
/**
* Provides an easier and safer way for developers to provide context when
* generating checked exceptions. Often, additional information, besides what's
* embedded in the exception cause, is needed for developers to debug and correct
* a bug. Often, this additional information can reduce the time it takes
* to replicate and fix a bug.
*
* <p>ContextedException is easier as developers don't need to be concerned
* with formatting the exception message to include additional information
* with the exception. Additional information is automatically included
* in the message and printed stack trace. This often thins out exception
* handling code.</p>
*
* <p>ContextedException is safer as the additional code needed to embed additional
* information in a normal exception tends to be tested less and is more vulnerable
* to errors such as null pointer exceptions.</p>
*
* <p>An unchecked version of this exception is provided by ContextedRuntimeException.</p>
*
* <p>To use this class write code as follows:</p>
*
* <p>
* An exception that provides an easy and safe way to add contextual information.
* </p><p>
* An exception trace itself is often insufficient to provide rapid diagnosis of the issue.
* Frequently what is needed is a select few pieces of local contextual data.
* Providing this data is tricky however, due to concerns over formatting and nulls.
* </p><p>
* The contexted exception approach allows the exception to be created together with a
* map of context values. This additional information is automatically included in the
* message and printed stack trace.
* </p><p>
* An unchecked version of this exception is provided by ContextedRuntimeException.
* </p>
* <p>
* To use this class write code as follows:
* </p>
* <pre>
* try {
*
* ...
*
* ...
* } catch (Throwable e) {
* throw new ContextedException("Error posting account transaction", e)
* .addLabeledValue("accountNumber", accountNumber)
@ -53,8 +47,8 @@
* }
* }
* </pre>
*
* <p>The output in a printStacktrace() (which often is written to a log) would look something like the following:
* </p><p>
* The output in a printStacktrace() (which often is written to a log) would look something like the following:
* <pre>
* org.apache.commons.lang.exception.ContextedException: java.lang.Exception: Error posting account transaction
* Exception Context:
@ -66,109 +60,124 @@
* at org.apache.commons.lang.exception.ContextedExceptionTest.testAddLabeledValue(ContextedExceptionTest.java:88)
* ..... (rest of trace)
* </pre>
* </p>
*
* @see ContextedRuntimeException
* @author D. Ashmore
* @since 3.0
*
*/
public class ContextedException extends Exception implements ExceptionContext {
/** The serialization version. */
private static final long serialVersionUID = 8940917952810290164L;
private ExceptionContext exceptionContext = new DefaultExceptionContext();
/** The context where the data is stored. */
private final ExceptionContext exceptionContext;
/**
* Instantiates ContextedException without message or cause.
* <p>DefaultExceptionContext used to store and format context information.</p>
* <p>
* The context information is stored using a default implementation.
*/
public ContextedException() {
super();
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedException with message, but without cause.
* <p>DefaultExceptionContext used to store and format context information.</p>
* @param message The exception message
* <p>
* The context information is stored using a default implementation.
*
* @param message the exception message, may be null
*/
public ContextedException(String message) {
super(message);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedException with cause, but without message.
* <p>DefaultExceptionContext used to store and format context information.</p>
* @param cause Exception creating need for ContextedException
* <p>
* The context information is stored using a default implementation.
*
* @param cause the underlying cause of the exception, may be null
*/
public ContextedException(Throwable cause) {
super(cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedException with cause and message.
* <p>DefaultExceptionContext used to store and format context information.</p>
* @param message The exception message
* @param cause Exception creating need for ContextedException
* <p>
* The context information is stored using a default implementation.
*
* @param message the exception message, may be null
* @param cause the underlying cause of the exception, may be null
*/
public ContextedException(String message, Throwable cause) {
super(message, cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedException with cause, message, and ExceptionContext.
* @param message The exception message
* @param cause Exception creating need for ContextedException
* @param context Context used to store additional information
* @since 3.0
*
* @param message the exception message, may be null
* @param cause the underlying cause of the exception, may be null
* @param context the context used to store the additional information, null uses default implementation
*/
public ContextedException(String message, Throwable cause, ExceptionContext context) {
super(message, cause);
if (context != null) {
this.exceptionContext = context;
if (context == null) {
context = new DefaultExceptionContext();
}
exceptionContext = context;
}
//-----------------------------------------------------------------------
/**
* Adds information helpful to a developer in diagnosing and correcting
* the problem. For the information to be meaningful, the value passed
* should have a reasonable toString() implementation.
*
* <p>Note: If the value provided isn't Serializable, one solution would be
* <p>
* Note: If the value provided isn't Serializable, one solution would be
* to provide its toString() if it has a meaningful implementation or
* individual properties of the value object instead.</p>
* @param label a textual label associated with information
* @param value information needed to understand exception. May be <code>null</code>.
* @return this
* @since 3.0
* individual properties of the value object instead.
*
* @param label a textual label associated with information, null not recommended
* @param value information needed to understand exception, may be null
* @return this, for method chaining
*/
public ContextedException addLabeledValue(String label, Serializable value) {
this.exceptionContext.addLabeledValue(label, value);
exceptionContext.addLabeledValue(label, value);
return this;
}
/**
* Convenience method to retrieve a value from the underlying ExceptionContext.
* @param label a textual label associated with information
* @return value information needed to understand exception. May be <code>null</code>.
* @since 3.0
* Retrieves a contextual data value associated with the label.
*
* @param label the label to get the contextual value for, may be null
* @return the contextual value associated with the label, may be null
*/
public Serializable getLabeledValue(String label) {
return this.exceptionContext.getLabeledValue(label);
return exceptionContext.getLabeledValue(label);
}
/**
* Convenience method to retrieve currently defined labels from the underlying ExceptionContext.
* @return labelSet
* @since 3.0
* Retrieves the labels defined in the contextual data.
*
* @return the set of labels, never null
*/
public Set<String> getLabelSet() {
return this.exceptionContext.getLabelSet();
return exceptionContext.getLabelSet();
}
/**
* Provides message pertaining to exception.
* Provides the message explaining the exception, including the contextual data.
*
* @see java.lang.Throwable#getMessage()
* @return message
* @since 3.0
* @return the message, never null
*/
@Override
public String getMessage(){
@ -179,6 +188,6 @@ public String getMessage(){
* {@inheritDoc}
*/
public String getFormattedExceptionMessage(String baseMessage) {
return this.exceptionContext.getFormattedExceptionMessage(baseMessage);
return exceptionContext.getFormattedExceptionMessage(baseMessage);
}
}

View File

@ -20,109 +20,165 @@
import java.util.Set;
/**
* Provides an unchecked version of ContextedException
* <p>
* A runtime exception that provides an easy and safe way to add contextual information.
* </p><p>
* An exception trace itself is often insufficient to provide rapid diagnosis of the issue.
* Frequently what is needed is a select few pieces of local contextual data.
* Providing this data is tricky however, due to concerns over formatting and nulls.
* </p><p>
* The contexted exception approach allows the exception to be created together with a
* map of context values. This additional information is automatically included in the
* message and printed stack trace.
* </p><p>
* An checked version of this exception is provided by ContextedException.
* </p>
* <p>
* To use this class write code as follows:
* </p>
* <pre>
* try {
* ...
* } catch (Throwable e) {
* throw new ContextedException("Error posting account transaction", e)
* .addLabeledValue("accountNumber", accountNumber)
* .addLabeledValue("amountPosted", amountPosted)
* .addLabeledValue("previousBalance", previousBalance)
* }
* }
* </pre>
* </p><p>
* The output in a printStacktrace() (which often is written to a log) would look something like the following:
* <pre>
* org.apache.commons.lang.exception.ContextedRuntimeException: java.lang.Exception: Error posting account transaction
* Exception Context:
* [accountNumber=null]
* [amountPosted=100.00]
* [previousBalance=-2.17]
*
* ---------------------------------
* at org.apache.commons.lang.exception.ContextedRuntimeExceptionTest.testAddLabeledValue(ContextedExceptionTest.java:88)
* ..... (rest of trace)
* </pre>
* </p>
*
* @see ContextedException
* @author D. Ashmore
* @author J&ouml;rg Schaible
* @since 3.0
*
*/
public class ContextedRuntimeException extends RuntimeException implements ExceptionContext {
public class ContextedRuntimeException extends Exception implements ExceptionContext {
/** The serialization version. */
private static final long serialVersionUID = 1459691936045811817L;
private ExceptionContext exceptionContext = new DefaultExceptionContext();
/** The context where the data is stored. */
private final ExceptionContext exceptionContext;
/**
* Instantiates ContextedRuntimeException without message or cause.
* <p>DefaultExceptionContext used to store and format context information.</p>
*
* <p>
* The context information is stored using a default implementation.
*/
public ContextedRuntimeException() {
super();
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedRuntimeException with message, but without cause.
* <p>DefaultExceptionContext used to store and format context information.</p>
* @param message The exception message
* @since 3.0
* <p>
* The context information is stored using a default implementation.
*
* @param message the exception message, may be null
*/
public ContextedRuntimeException(String message) {
super(message);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedRuntimeException with cause, but without message.
* <p>DefaultExceptionContext used to store and format context information.</p>
* @param cause Exception creating need for ContextedRuntimeException
* @since 3.0
* <p>
* The context information is stored using a default implementation.
*
* @param cause the underlying cause of the exception, may be null
*/
public ContextedRuntimeException(Throwable cause) {
super(cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedRuntimeException with cause and message.
* <p>DefaultExceptionContext used to store and format context information.</p>
* @param message The exception message
* @param cause Exception creating need for ContextedException
* @since 3.0
* <p>
* The context information is stored using a default implementation.
*
* @param message the exception message, may be null
* @param cause the underlying cause of the exception, may be null
*/
public ContextedRuntimeException(String message, Throwable cause) {
this(message, cause, cause instanceof ExceptionContext ? (ExceptionContext)cause : null);
super(message, cause);
exceptionContext = new DefaultExceptionContext();
}
/**
* Instantiates ContextedRuntimeException with cause, message, and ExceptionContext.
* @param message The exception message
* @param cause Exception creating need for ContextedRuntimeException
* @param context Context used to store additional information
* @since 3.0
*
* @param message the exception message, may be null
* @param cause the underlying cause of the exception, may be null
* @param context the context used to store the additional information, null uses default implementation
*/
public ContextedRuntimeException(String message, Throwable cause, ExceptionContext context) {
super(message, cause);
if (context != null) {
this.exceptionContext = context;
if (context == null) {
context = new DefaultExceptionContext();
}
exceptionContext = context;
}
//-----------------------------------------------------------------------
/**
* Adds information helpful to a developer in diagnosing and correcting
* the problem.
* @see ContextedException#addLabeledValue(String, Serializable)
* @param label a textual label associated with information
* @param value information needed to understand exception. May be <code>null</code>.
* @return this
* @since 3.0
* the problem. For the information to be meaningful, the value passed
* should have a reasonable toString() implementation.
* <p>
* Note: If the value provided isn't Serializable, one solution would be
* to provide its toString() if it has a meaningful implementation or
* individual properties of the value object instead.
*
* @param label a textual label associated with information, null not recommended
* @param value information needed to understand exception, may be null
* @return this, for method chaining
*/
public ContextedRuntimeException addLabeledValue(String label, Serializable value) {
this.exceptionContext.addLabeledValue(label, value);
exceptionContext.addLabeledValue(label, value);
return this;
}
/**
* Convenience method to retrieve a value from the underlying ExceptionContext.
* @param label a textual label associated with information
* @return value information needed to understand exception. May be <code>null</code>.
* @since 3.0
* Retrieves a contextual data value associated with the label.
*
* @param label the label to get the contextual value for, may be null
* @return the contextual value associated with the label, may be null
*/
public Serializable getLabeledValue(String label) {
return this.exceptionContext.getLabeledValue(label);
return exceptionContext.getLabeledValue(label);
}
/**
* Convenience method to retrieve currently defined labels from the underlying ExceptionContext.
* @return labelSet
* @since 3.0
* Retrieves the labels defined in the contextual data.
*
* @return the set of labels, never null
*/
public Set<String> getLabelSet() {
return this.exceptionContext.getLabelSet();
return exceptionContext.getLabelSet();
}
/**
* Provides message pertaining to exception.
* Provides the message explaining the exception, including the contextual data.
*
* @see java.lang.Throwable#getMessage()
* @return message
* @since 3.0
* @return the message, never null
*/
@Override
public String getMessage(){
@ -133,6 +189,6 @@ public String getMessage(){
* {@inheritDoc}
*/
public String getFormattedExceptionMessage(String baseMessage) {
return this.exceptionContext.getFormattedExceptionMessage(baseMessage);
return exceptionContext.getFormattedExceptionMessage(baseMessage);
}
}

View File

@ -17,74 +17,63 @@
package org.apache.commons.lang.exception;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.SystemUtils;
/**
* Provides context feature for exceptions. Used by both checked and unchecked version of the contexted exceptions.
* @see ContextedRuntimeException
* Default implementation of the context storing the label-value pairs for contexted exceptions.
*
* @author D. Ashmore
* @since 3.0
*/
public class DefaultExceptionContext implements ExceptionContext {
class DefaultExceptionContext implements ExceptionContext {
/** The serialization version. */
private static final long serialVersionUID = 293747957535772807L;
/*
* This value list could really be obtained from the Map, however, some
* callers want to control the order of the list as it appears in the
* Message. The list allows that. name/value pairs will appear in
* the order that they're provided. D. Ashmore
*/
private List<String> contextKeyList = new ArrayList<String>();
private Map<String, Serializable> contextValueMap = new HashMap<String, Serializable>();
/** The ordered map storing the label-data pairs. */
private Map<String, Serializable> contextValueMap = new LinkedHashMap<String, Serializable>();
/**
* Adds information helpful to a developer in diagnosing and correcting
* the problem.
* @see ContextedException#addLabeledValue(String, Serializable)
* @param label a textual label associated with information
* @param value information needed to understand exception. May be null.
* @return this
* @since 3.0
* Adds a contextual label-value pair into this context.
* <p>
* This label-value pair provides information useful for debugging.
*
* @param label the label of the item to add, null not recommended
* @param value the value of item to add, may be null
* @return this, for method chaining
*/
public ExceptionContext addLabeledValue(String label, Serializable value) {
this.contextKeyList.add(label);
this.contextValueMap.put(label, value);
contextValueMap.put(label, value);
return this;
}
/**
* Retrieves the value for a given label.
* @param label a textual label associated with information
* @return value information needed to understand exception. May be null.
* @since 3.0
* Retrieves a contextual data value associated with the label.
*
* @param label the label to get the contextual value for, may be null
* @return the contextual value associated with the label, may be null
*/
public Serializable getLabeledValue(String label) {
return this.contextValueMap.get(label);
return contextValueMap.get(label);
}
/**
* Retrieves currently defined labels.
* @return labelSet
* @since 3.0
* Retrieves the labels defined in the contextual data.
*
* @return the set of labels, never null
*/
public Set<String> getLabelSet() {
return this.contextValueMap.keySet();
return contextValueMap.keySet();
}
/**
* Centralized message logic for both checked and unchecked version of
* context exceptions
* @param baseMessage message retained by super class
* @return message -- exception message
* @since 3.0
* Builds the message containing the contextual information.
*
* @param baseMessage the base exception message <b>without</b> context information appended
* @return the exception message <b>with</b> context information appended, never null
*/
public String getFormattedExceptionMessage(String baseMessage){
StringBuilder buffer = new StringBuilder(256);
@ -92,7 +81,7 @@ public String getFormattedExceptionMessage(String baseMessage){
buffer.append(baseMessage);
}
if (contextKeyList.size() > 0) {
if (contextValueMap.size() > 0) {
if (buffer.length() > 0l) {
buffer.append(SystemUtils.LINE_SEPARATOR);
}
@ -102,7 +91,7 @@ public String getFormattedExceptionMessage(String baseMessage){
Object value;
String valueStr;
for (String label: this.contextKeyList) {
for (String label : contextValueMap.keySet()) {
buffer.append("[");
buffer.append(label);
buffer.append("=");
@ -126,5 +115,5 @@ public String getFormattedExceptionMessage(String baseMessage){
}
return buffer.toString();
}
}

View File

@ -31,38 +31,40 @@
* @since 3.0
*/
public interface ExceptionContext extends Serializable {
/**
* Adds a context item along with a label.
* @param label label of item
* @param value value of item
* Adds a contextual label-value pair into this context.
* <p>
* This label-value pair provides information useful for debugging.
*
* @param label the label of the item to add, null not recommended
* @param value the value of item to add, may be null
* @return context itself to allow method chaining
* @since 3.0
*/
public ExceptionContext addLabeledValue(String label, Serializable value);
/**
* Provides context information associated with the given label.
* @param label label of item
* @return value value associated with label
* @since 3.0
* Retrieves a contextual data value associated with the label.
*
* @param label the label to get the contextual value for, may be null
* @return the contextual value associated with the label, may be null
*/
public Serializable getLabeledValue(String label);
/**
* Provides a set of labels that are currently in the context.
* @return labelSet labels currently used by the context
* @since 3.0
* Retrieves the labels defined in the contextual data.
*
* @return the set of labels, never null
*/
public Set<String> getLabelSet();
/**
* Implementors provide the given base message with context label/value item
* information appended.
* @param baseMessage exception message <b>without</b> context information appended
* @return formattedMessage exception message <b>with</b> context information appended
* @since 3.0
*
* @param baseMessage the base exception message <b>without</b> context information appended
* @return the exception message <b>with</b> context information appended, never null
*/
public String getFormattedExceptionMessage(String baseMessage);
}
}