001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.lang3.exception;
018
019 import java.util.Set;
020
021 /**
022 * <p>
023 * An exception that provides an easy and safe way to add contextual information.
024 * </p><p>
025 * An exception trace itself is often insufficient to provide rapid diagnosis of the issue.
026 * Frequently what is needed is a select few pieces of local contextual data.
027 * Providing this data is tricky however, due to concerns over formatting and nulls.
028 * </p><p>
029 * The contexted exception approach allows the exception to be created together with a
030 * map of context values. This additional information is automatically included in the
031 * message and printed stack trace.
032 * </p><p>
033 * An unchecked version of this exception is provided by ContextedRuntimeException.
034 * </p>
035 * <p>
036 * To use this class write code as follows:
037 * </p>
038 * <pre>
039 * try {
040 * ...
041 * } catch (Exception e) {
042 * throw new ContextedException("Error posting account transaction", e)
043 * .addValue("accountNumber", accountNumber)
044 * .addValue("amountPosted", amountPosted)
045 * .addValue("previousBalance", previousBalance)
046 * }
047 * }
048 * </pre>
049 * </p><p>
050 * The output in a printStacktrace() (which often is written to a log) would look something like the following:
051 * <pre>
052 * org.apache.commons.lang3.exception.ContextedException: java.lang.Exception: Error posting account transaction
053 * Exception Context:
054 * [accountNumber=null]
055 * [amountPosted=100.00]
056 * [previousBalance=-2.17]
057 *
058 * ---------------------------------
059 * at org.apache.commons.lang3.exception.ContextedExceptionTest.testAddValue(ContextedExceptionTest.java:88)
060 * ..... (rest of trace)
061 * </pre>
062 * </p>
063 *
064 * @see ContextedRuntimeException
065 * @author Apache Software Foundation
066 * @author D. Ashmore
067 * @since 3.0
068 */
069 public class ContextedException extends Exception implements ExceptionContext {
070
071 /** The serialization version. */
072 private static final long serialVersionUID = 8940917952810290164L;
073 /** The context where the data is stored. */
074 private final ExceptionContext exceptionContext;
075
076 /**
077 * Instantiates ContextedException without message or cause.
078 * <p>
079 * The context information is stored using a default implementation.
080 */
081 public ContextedException() {
082 super();
083 exceptionContext = new DefaultExceptionContext();
084 }
085
086 /**
087 * Instantiates ContextedException with message, but without cause.
088 * <p>
089 * The context information is stored using a default implementation.
090 *
091 * @param message the exception message, may be null
092 */
093 public ContextedException(String message) {
094 super(message);
095 exceptionContext = new DefaultExceptionContext();
096 }
097
098 /**
099 * Instantiates ContextedException with cause, but without message.
100 * <p>
101 * The context information is stored using a default implementation.
102 *
103 * @param cause the underlying cause of the exception, may be null
104 */
105 public ContextedException(Throwable cause) {
106 super(cause);
107 exceptionContext = new DefaultExceptionContext();
108 }
109
110 /**
111 * Instantiates ContextedException with cause and message.
112 * <p>
113 * The context information is stored using a default implementation.
114 *
115 * @param message the exception message, may be null
116 * @param cause the underlying cause of the exception, may be null
117 */
118 public ContextedException(String message, Throwable cause) {
119 super(message, cause);
120 exceptionContext = new DefaultExceptionContext();
121 }
122
123 /**
124 * Instantiates ContextedException with cause, message, and ExceptionContext.
125 *
126 * @param message the exception message, may be null
127 * @param cause the underlying cause of the exception, may be null
128 * @param context the context used to store the additional information, null uses default implementation
129 */
130 public ContextedException(String message, Throwable cause, ExceptionContext context) {
131 super(message, cause);
132 if (context == null) {
133 context = new DefaultExceptionContext();
134 }
135 exceptionContext = context;
136 }
137
138 //-----------------------------------------------------------------------
139 /**
140 * Adds information helpful to a developer in diagnosing and correcting
141 * the problem. For the information to be meaningful, the value passed
142 * should have a reasonable toString() implementation. If the added label
143 * is already available, the label is appended with an index.
144 * <p>
145 * Note: This exception is only serializable if the object added is serializable.
146 * </p>
147 *
148 * @param label a textual label associated with information, null not recommended
149 * @param value information needed to understand exception, may be null
150 * @return this, for method chaining
151 */
152 public ContextedException addValue(String label, Object value) {
153 exceptionContext.addValue(label, value);
154 return this;
155 }
156
157 /**
158 * Replaces information helpful to a developer in diagnosing and correcting
159 * the problem. For the information to be meaningful, the value passed
160 * should have a reasonable toString() implementation. If the replaced
161 * label does not yet exist, it is simply added.
162 * <p>
163 * Note: This exception is only serializable if the object added is serializable.
164 * </p>
165 *
166 * @param label a textual label associated with information, null not recommended
167 * @param value information needed to understand exception, may be null
168 * @return this, for method chaining
169 */
170 public ContextedException replaceValue(String label, Object value) {
171 exceptionContext.replaceValue(label, value);
172 return this;
173 }
174
175 /**
176 * Retrieves a contextual data value associated with the label.
177 *
178 * @param label the label to get the contextual value for, may be null
179 * @return the contextual value associated with the label, may be null
180 */
181 public Object getValue(String label) {
182 return exceptionContext.getValue(label);
183 }
184
185 /**
186 * Retrieves the labels defined in the contextual data.
187 *
188 * @return the set of labels, never null
189 */
190 public Set<String> getLabelSet() {
191 return exceptionContext.getLabelSet();
192 }
193
194 /**
195 * Provides the message explaining the exception, including the contextual data.
196 *
197 * @see java.lang.Throwable#getMessage()
198 * @return the message, never null
199 */
200 @Override
201 public String getMessage(){
202 return getFormattedExceptionMessage(super.getMessage());
203 }
204
205 /**
206 * {@inheritDoc}
207 */
208 public String getFormattedExceptionMessage(String baseMessage) {
209 return exceptionContext.getFormattedExceptionMessage(baseMessage);
210 }
211 }