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 * A runtime 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 checked version of this exception is provided by ContextedException. 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.ContextedRuntimeException: 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.ContextedRuntimeExceptionTest.testAddValue(ContextedExceptionTest.java:88) 060 * ..... (rest of trace) 061 * </pre> 062 * </p> 063 * 064 * @see ContextedException 065 * @author Apache Software Foundation 066 * @author D. Ashmore 067 * @since 3.0 068 */ 069 public class ContextedRuntimeException extends RuntimeException implements ExceptionContext { 070 071 /** The serialization version. */ 072 private static final long serialVersionUID = 1459691936045811817L; 073 /** The context where the data is stored. */ 074 private final ExceptionContext exceptionContext; 075 076 /** 077 * Instantiates ContextedRuntimeException without message or cause. 078 * <p> 079 * The context information is stored using a default implementation. 080 */ 081 public ContextedRuntimeException() { 082 super(); 083 exceptionContext = new DefaultExceptionContext(); 084 } 085 086 /** 087 * Instantiates ContextedRuntimeException 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 ContextedRuntimeException(String message) { 094 super(message); 095 exceptionContext = new DefaultExceptionContext(); 096 } 097 098 /** 099 * Instantiates ContextedRuntimeException 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 ContextedRuntimeException(Throwable cause) { 106 super(cause); 107 exceptionContext = new DefaultExceptionContext(); 108 } 109 110 /** 111 * Instantiates ContextedRuntimeException 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 ContextedRuntimeException(String message, Throwable cause) { 119 super(message, cause); 120 exceptionContext = new DefaultExceptionContext(); 121 } 122 123 /** 124 * Instantiates ContextedRuntimeException 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 ContextedRuntimeException(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 ContextedRuntimeException 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 ContextedRuntimeException 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 }