1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: AbstractException.java,v 1.1.1.1 2004/05/09 16:57:57 vlad_r Exp $ 8 */ 9package com.vladium.util.exception; 10 11import java.io.IOException; 12import java.io.ObjectOutputStream; 13import java.io.PrintStream; 14import java.io.PrintWriter; 15 16// ---------------------------------------------------------------------------- 17/** 18 * Based on code published by me in <a href="http://www.fawcette.com/javapro/2002_12/online/exception_vroubtsov_12_16_02/default_pf.asp">JavaPro, 2002</a>.<P> 19 * 20 * This checked exception class is designed as a base/expansion point for the 21 * entire hierarchy of checked exceptions in a project.<P> 22 * 23 * It provides the following features: 24 * <UL> 25 * <LI> ability to take in compact error codes that map to full text messages 26 * in a resource bundle loaded at this class' instantiation time. This avoids 27 * hardcoding the error messages in product code and allows for easy 28 * localization of such text if required. Additionally, these messages 29 * can be parametrized in the java.text.MessageFormat style; 30 * <LI> exception chaining in J2SE versions prior to 1.4 31 * </UL> 32 * 33 * See {@link AbstractRuntimeException} for an unchecked version of the same class.<P> 34 * 35 * TODO: javadoc 36 * 37 * Each constructor that accepts a String 'message' parameter accepts an error 38 * code as well. You are then responsible for ensuring that either the root 39 * <CODE>com.vladium.exception.exceptions</CODE> resource bundle 40 * or your project/exception class-specific resource bundle [see 41 * <A HREF="#details">below</A> for details] contains a mapping for this error 42 * code. When this lookup fails the passed String value itself will be used as 43 * the error message.<P> 44 * 45 * All constructors taking an 'arguments' parameter supply parameters to the error 46 * message used as a java.text.MessageFormat pattern.<P> 47 * 48 * Example: 49 * <PRE><CODE> 50 * File file = ... 51 * try 52 * ... 53 * catch (Exception e) 54 * { 55 * throw new AbstractException ("FILE_NOT_FOUND", new Object[] {file, e}, e); 56 * } 57 * </CODE></PRE> 58 * where <CODE>com.vladium.util.exception.exceptions</CODE> contains: 59 * <PRE><CODE> 60 * FILE_NOT_FOUND: file {0} could not be opened: {1} 61 * </CODE></PRE> 62 * 63 * To log exception data use {@link #getMessage} or <CODE>printStackTrace</CODE> 64 * family of methods. You should never have to use toString().<P> 65 * 66 * <A NAME="details"> It is also possible to use project- or exception 67 * subhierarchy-specific message resource bundles without maintaining all error 68 * codes in <CODE>com.vladium.exception.exceptions</CODE>. To do so, create a 69 * custom resource bundle and add the following static initializer code to your 70 * base exception class: 71 * <PRE><CODE> 72 * static 73 * { 74 * addExceptionResource (MyException.class, "my_custom_resource_bundle"); 75 * } 76 * </CODE></PRE> 77 * The bundle name is relative to MyException package. This step can omitted if 78 * the bundle name is "exceptions". 79 * 80 * Note that the implementation correctly resolves error code name collisions 81 * across independently developed exception families, as long as resource bundles 82 * use unique names. Specifically, error codes follow inheritance and hiding rules 83 * similar to Java class static methods. See {@link ExceptionCommon#addExceptionResource} 84 * for further details. 85 * 86 * @author Vlad Roubtsov, (C) 2002 87 */ 88public 89abstract class AbstractException extends Exception implements ICodedException, IThrowableWrapper 90{ 91 // public: ................................................................ 92 93 /** 94 * Constructs an exception with null message and null cause. 95 */ 96 public AbstractException () 97 { 98 m_cause = null; 99 m_arguments = null; 100 } 101 102 /** 103 * Constructs an exception with given error message/code and null cause. 104 * 105 * @param message the detail message [can be null] 106 */ 107 public AbstractException (final String message) 108 { 109 super (message); 110 m_cause = null; 111 m_arguments = null; 112 } 113 114 /** 115 * Constructs an exception with given error message/code and null cause. 116 * 117 * @param message the detail message [can be null] 118 * @param arguments message format parameters [can be null or empty] 119 * 120 * @see java.text.MessageFormat 121 */ 122 public AbstractException (final String message, final Object [] arguments) 123 { 124 super (message); 125 m_cause = null; 126 m_arguments = arguments == null ? null : (Object []) arguments.clone (); 127 } 128 129 /** 130 * Constructs an exception with null error message/code and given cause. 131 * 132 * @param cause the cause [nested exception] [can be null] 133 */ 134 public AbstractException (final Throwable cause) 135 { 136 super (); 137 m_cause = cause; 138 m_arguments = null; 139 } 140 141 /** 142 * Constructs an exception with given error message/code and given cause. 143 * 144 * @param message the detail message [can be null] 145 * @param cause the cause [nested exception] [can be null] 146 */ 147 public AbstractException (final String message, final Throwable cause) 148 { 149 super (message); 150 m_cause = cause; 151 m_arguments = null; 152 } 153 154 /** 155 * Constructs an exception with given error message/code and given cause. 156 * 157 * @param message the detail message [can be null] 158 * @param arguments message format parameters [can be null or empty] 159 * @param cause the cause [nested exception] [can be null] 160 * 161 * @see java.text.MessageFormat 162 */ 163 public AbstractException (final String message, final Object [] arguments, final Throwable cause) 164 { 165 super (message); 166 m_cause = cause; 167 m_arguments = arguments == null ? null : (Object []) arguments.clone (); 168 } 169 170 171 /** 172 * Overrides base method to support error code lookup and avoid returning nulls. 173 * Note that this does not recurse into any 'cause' for message lookup, it only 174 * uses the data passed into the constructor. Subclasses cannot override.<P> 175 * 176 * Equivalent to {@link #getLocalizedMessage}. 177 * 178 * @return String error message provided at construction time or the result 179 * of toString() if no/null message was provided [never null]. 180 */ 181 public final String getMessage () 182 { 183 if (m_message == null) // not synchronized by design 184 { 185 String msg; 186 final String supermsg = super.getMessage (); 187 final Class _class = getClass (); 188 189 if (m_arguments == null) 190 { 191 msg = ExceptionCommon.getMessage (_class, supermsg); 192 } 193 else 194 { 195 msg = ExceptionCommon.getMessage (_class, supermsg, m_arguments); 196 } 197 198 if (msg == null) 199 { 200 // this is the same as what's done in Throwable.toString() [copied here to be independent of future JDK changes] 201 final String className = _class.getName (); 202 203 msg = (supermsg != null) ? (className + ": " + supermsg) : className; 204 } 205 206 m_message = msg; 207 } 208 209 return m_message; 210 } 211 212 /** 213 * Overrides base method for the sole purpose of making it final.<P> 214 * 215 * Equivalent to {@link #getMessage}. 216 */ 217 public final String getLocalizedMessage () 218 { 219 // this is the same as what's done in Throwable 220 // [copied here to be independent of future JDK changes] 221 return getMessage (); 222 } 223 224 /** 225 * Overrides Exception.printStackTrace() to (a) force the output to go 226 * to System.out and (b) handle nested exceptions in JDKs prior to 1.4.<P> 227 * 228 * Subclasses cannot override. 229 */ 230 public final void printStackTrace () 231 { 232 // NOTE: unlike the JDK implementation, force the output to go to System.out: 233 ExceptionCommon.printStackTrace (this, System.out); 234 } 235 236 /** 237 * Overrides Exception.printStackTrace() to handle nested exceptions in JDKs prior to 1.4.<P> 238 * 239 * Subclasses cannot override. 240 */ 241 public final void printStackTrace (final PrintStream s) 242 { 243 ExceptionCommon.printStackTrace (this, s); 244 } 245 246 /** 247 * Overrides Exception.printStackTrace() to handle nested exceptions in JDKs prior to 1.4.<P> 248 * 249 * Subclasses cannot override. 250 */ 251 public final void printStackTrace (final PrintWriter s) 252 { 253 ExceptionCommon.printStackTrace (this, s); 254 } 255 256 // ICodedException: 257 258 /** 259 * Returns the String that was passed as 'message' constructor argument. 260 * Can be null. 261 * 262 * @return message code string [can be null] 263 */ 264 public final String getErrorCode () 265 { 266 return super.getMessage (); 267 } 268 269 // IThrowableWrapper: 270 271 /** 272 * This implements {@link IThrowableWrapper} 273 * and also overrides the base method in JDK 1.4+. 274 */ 275 public final Throwable getCause () 276 { 277 return m_cause; 278 } 279 280 public void __printStackTrace (final PrintStream ps) 281 { 282 super.printStackTrace (ps); 283 } 284 285 public void __printStackTrace (final PrintWriter pw) 286 { 287 super.printStackTrace (pw); 288 } 289 290 /** 291 * Equivalent to {@link ExceptionCommon#addExceptionResource}, repeated here for 292 * convenience. Subclasses should invoke from static initializers <I>only</I>. 293 * 'namespace' should be YourException.class. 294 */ 295 public static void addExceptionResource (final Class namespace, 296 final String messageResourceBundleName) 297 { 298 // note: 'namespace' will be the most derived class; it is possible to 299 // auto-detect that in a static method but that requires some security 300 // permissions 301 ExceptionCommon.addExceptionResource (namespace, messageResourceBundleName); 302 } 303 304 // protected: ............................................................. 305 306 // package: ............................................................... 307 308 // private: ............................................................... 309 310 311 /* 312 * Ensures that this instance can be serialized even if some message parameters 313 * are not serializable objects. 314 */ 315 private void writeObject (final ObjectOutputStream out) 316 throws IOException 317 { 318 getMessage (); // transform this instance to serializable form 319 out.defaultWriteObject (); 320 } 321 322 323 private String m_message; // marshalled/cached result of getMessage() 324 private transient final Object [] m_arguments; 325 // note: this field duplicates functionality available in stock Throwable in JRE 1.4+ 326 private final Throwable m_cause; 327 328} // end of class 329// ---------------------------------------------------------------------------- 330