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