Home | History | Annotate | Download | only in exception
      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: AbstractRuntimeException.java,v 1.1.1.1 2004/05/09 16:57:57 vlad_r Exp $
      8  */
      9 package com.vladium.util.exception;
     10 
     11 import java.io.IOException;
     12 import java.io.ObjectOutputStream;
     13 import java.io.PrintStream;
     14 import 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 unchecked exception class is designed as a base/expansion point for the
     21  * entire hierarchy of unchecked 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 AbstractException} for a checked 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 AbstractRuntimeException ("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  */
     88 public
     89 abstract class AbstractRuntimeException extends RuntimeException implements ICodedException, IThrowableWrapper
     90 {
     91     // public: ................................................................
     92 
     93     /**
     94      * Constructs an exception with null message and null cause.
     95      */
     96     public AbstractRuntimeException ()
     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 AbstractRuntimeException (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 AbstractRuntimeException (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 AbstractRuntimeException (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 AbstractRuntimeException (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 AbstractRuntimeException (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