001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: UMSException.java,v 1.5 2009/01/28 05:34:51 ww203982 Exp $
026 *
027 */
028
029/**
030 * Portions Copyrighted [2011] [ForgeRock AS]
031 */
032package com.iplanet.ums;
033
034import java.io.PrintWriter;
035import java.io.StringWriter;
036import java.util.ResourceBundle;
037
038import com.sun.identity.shared.ldap.LDAPException;
039
040/**
041 * <PRE>
042 * 
043 * This class is the super-class for all UMS <B>checked</B> exceptions.
044 * 
045 * 
046 * Some Exception throwing guidelines: -------------------------------------
047 * 
048 * <B> Checked exceptions </B> are sub-classes of java.lang.Exception; methods
049 * throwing this type of exception are forced to define a throws clause in the
050 * method signature and client programmers need to catch and handle the
051 * exception with a try/catch block or declare the throws clause in their
052 * methods. <B> Unchecked exceptions </B> are sub-classes of
053 * java.lang.RuntimeException. Client programmers don't have to deal with the
054 * exception using a try/catch block and the method throwing it does not have to
055 * define it in its signature.
056 *  - If your method encounters an abnormal condition which causes it to be
057 * unable to fulfill its contract, or throw a checked or unchecked exception
058 * (either UMSException or RuntimeException).
059 *  - If your method discovers that a client has breached its contract, for
060 * example, passing a null as a parameter where a non-null value is required,
061 * throw an unchecked exception (RuntimeException).
062 *  - If your method is unable to fulfill its contract and you feel client
063 * programmers should consciously decide how to handle, throw checked exceptions
064 * (UMSException).
065 * 
066 * 
067 * Embedded/Nested Exceptions: --------------------------
068 * 
069 * An exception of type UMSException can embed any exception of type Throwable.
070 * Embedded exceptions ensure traceability of errors in a multi-tiered
071 * application. For example, in a simple 3- Tier model - presentation/client
072 * tier, middle/domain tier and database/persistence tier - the real cause of
073 * error might be lost by the time control, which is passed back from the
074 * persistence tier to the client tier. To ensure tracking info, the constructor
075 * UMSException(message,Throwable) should be used while throwing the exception.
076 * Normally, the first object at each tier/module will have generic exceptions
077 * defined, for example, LDAPException, RelationalDBException,
078 * ConfigManagerException. Client programs can then invoke the #getRootCause()
079 * method to get the underlying cause.
080 * 
081 * Exception hierarchy should be defined: -------------------------------------
082 * An exception for each abnormal cause should be created. For example,
083 * LDAPSearchException, LDAPArchiveException, etc. UMSException should probably
084 * be thrown only by external API's. Even these should have embedded exceptions
085 * from lower level tiers. For example, UMSException will have LDAPException
086 * embedded in it, LDAPException will have LDAPSearchException nested, and so
087 * on. Every package should define its own exception hierarchies specific to its
088 * context, for example, policy-related exceptions should be defined in the
089 * policy package.
090 * 
091 * Localizing Error Messages ------------------------- The java resource bundle
092 * mechanism is used to implement localization. The ResourceSet and
093 * ResourceSetManager classes are used to implement localization.
094 * 
095 * Steps for creating UMSException Sub-classes and messages
096 * ------------------------------------------------------
097 * 
098 * 1. Identify the package this exception will belong to. A policy-related
099 * exception, PolicyNotFoundException, should be part of the policy package.
100 * 
101 * 2. Each package should have its own properties file to store error messages.
102 * For example policy.properties in package policy #policy.properties #
103 * Resources for com.iplanet.ums.policy policy-nopolicyfound=Cannot find this
104 * Policy
105 * 
106 * 3. Create a sub-class of UMSException and override the constructors.
107 * 
108 * public class PolicyNotFoundException extends UMSException { public
109 * PolicyNotFoundException() { super(); } public PolicyNotFoundException(String
110 * msg) { super(msg); } public PolicyNotFoundExceptin(String msg, Throwable t) {
111 * super(msg,t); }
112 * 
113 * 
114 * Throwing/Catching Exception Examples: ------------------------------------
115 * 
116 * 1. Throwing a non-nested Exception <B>(not recommended, use Ex. 3 below)</B>
117 * UMSException ux = new UMSException("Some weird error!..."); throw ux;
118 * 
119 * 2. Throwing a nested Exception <B>(not recommended, use Ex. 3 below)</B> 
120 * try { ....... ....... } catch (LDAPException le) { UMSException ux = new
121 * UMSException("Some weird error!...", le); throw ux; }
122 * 
123 * 3. Throwing an Exception using the ResourceSetManager
124 * 
125 * ...... ...... public static final String PKG =
126 * "com.iplanet.ums.policy.policy"; public static final String PREFIX =
127 * "policy"; public static final String NO_POLICY_DOMAIN = "nopolicydomain";
128 * public static final String POLICY_NOT_FOUND = "nopolicyfound"; ...... ......
129 * if( policyDomainName == null || policyDomainName.length() == 0) { 
130 * String msg = ResourceSetManager.getString( PKG, PREFIX, NO_POLICY_DOMAIN ); 
131 * // RuntimeException 
132 * throw new IllegalArgumentException( msg ); } ...... ......
133 * if (policy not found ) { String msg = ResourceSetManager.getString( PKG,
134 * PREFIX, POLICY_NOT_FOUND); // RuntimeException throw new
135 * InvalidPolicyException(msg); }
136 * 
137 * 
138 * The properties file (com/iplanet/ums/policy/policy.properties) looks like
139 * this: # Resources for com.iplanet.ums.policy policy-nopolicydomain=Policy
140 * Domain name cannot be null or blank policy-nopolicyfound=Cannot find this
141 * Policy
142 * 
143 *  - Logging/Dealing with an Exception, inclunding all nested exceptions try {
144 * ....... ....... } catch (UMSException ux) {
145 * 
146 * if (ux.getRootCause() instanceof LDAPException) { PrintWriter pw = new
147 * PrintWriter(<some file stream>); ux.log(pw); } else {
148 * System.out.println(ux.getMessage()); }
149 *  }
150 * 
151 * </PRE>
152 * 
153 * @see #UMSException(String, Throwable)
154 * @see #getRootCause()
155 * @see java.lang.Exception
156 * @see java.lang.RuntimeException
157 * @supported.api
158 */
159public class UMSException extends java.lang.Exception {
160
161    private static final long serialVersionUID = -7043204896844472780L;
162
163    static ResourceBundle xcptMsgs = null;
164
165    protected String xcptMessage;
166
167    protected Throwable rootCause;
168
169    /**
170     * Constructs a UMSException with a detailed message.
171     * 
172     * @param message
173     *            Detailed message for this exception.
174     * @supported.api
175     */
176    public UMSException(String message) {
177        super(message);
178        xcptMessage = message;
179    }
180
181    /**
182     * Constructs a UMSException with a message and an embedded exception.
183     * 
184     * @param message
185     *            Detailed message for this exception.
186     * @param rootCause
187     *            An embedded exception
188     * @supported.api
189     */
190    public UMSException(String message, Throwable rootCause) {
191        super(message);
192        xcptMessage = message;
193        this.rootCause = rootCause;
194    }
195
196    /**
197     * Constructs a UMSException with no details.
198     */
199    protected UMSException() {
200        super();
201        xcptMessage = null;
202    }
203
204    /**
205     * Returns the detail message of this exception and all embedded exceptions.
206     * @supported.api
207     */
208    public String getMessage() {
209
210        // if there's no nested exception,
211        // return the main message
212        if (getRootCause() == null)
213            return xcptMessage;
214
215        StringBuilder theMsg = new StringBuilder();
216
217        // get the root cause message
218        String nestedMsg;
219        if (rootCause instanceof LDAPException) {
220            nestedMsg = ((LDAPException) rootCause).getLDAPErrorMessage();
221        } else {
222            nestedMsg = rootCause.getMessage();
223        }
224
225        if (xcptMessage != null)
226            theMsg.append(xcptMessage).append("::").append(nestedMsg);
227        else
228            theMsg.append(nestedMsg);
229
230        return theMsg.toString();
231    }
232
233    /**
234     * Returns the embedded exception.
235     * @supported.api
236     */
237    public Throwable getRootCause() {
238        return rootCause;
239    }
240
241    /**
242     * Format this UMSException to a PrintWriter.
243     * 
244     * @param out
245     *            PrintWriter to write exception to.
246     * 
247     * @return The out parameter passed in.
248     * @see java.io.PrintWriter
249     * @supported.api
250     */
251    public PrintWriter log(PrintWriter out) {
252        return log(this, out);
253    }
254
255    /**
256     * A utility method to format an Exception to a PrintWriter.
257     * 
258     * @param xcpt
259     *            Exception to log.
260     * @param out
261     *            PrintWriter to write exception to.
262     * 
263     * @return The out parameter passed in.
264     * @see java.io.PrintWriter
265     * @supported.api
266     */
267    static public PrintWriter log(Throwable xcpt, PrintWriter out) {
268
269        out.println("-----------");
270        out.println(xcpt.toString());
271        out.println("Stack Trace:");
272        out.print(getStackTrace(xcpt));
273        out.println("-----------");
274        out.flush();
275        return out;
276    }
277
278    /**
279     * Formats a UMSException exception message; includes embedded exceptions.
280     * @supported.api
281     */
282    public String toString() {
283
284        StringBuilder buf = new StringBuilder();
285        buf.append("--------------------------------------");
286        buf.append("Got UMS Exception\n");
287
288        String msg = getMessage();
289        if (msg != null && msg.length() > 0) {
290            buf.append("Message: ").append(getMessage());
291        }
292
293        // Invoke toString() of rootCause first
294        if (rootCause != null) {
295            buf.append("\nLower level exception: ");
296            buf.append(getRootCause());
297        }
298
299        return buf.toString();
300    }
301
302    /**
303     * Prints this exception's stack trace to <tt>System.err</tt>. If this
304     * exception has a root exception; the stack trace of the root exception is
305     * printed to <tt>System.err</tt> instead.
306     * @supported.api
307     */
308    public void printStackTrace() {
309        printStackTrace(System.err);
310    }
311
312    /**
313     * Prints this exception's stack trace to a print stream. If this exception
314     * has a root exception, the stack trace of the root exception is printed to
315     * the print stream instead.
316     * 
317     * @param ps
318     *            The non-null print stream to which to print.
319     * @supported.api
320     */
321    public void printStackTrace(java.io.PrintStream ps) {
322        if (rootCause != null) {
323            String superString = super.toString();
324            synchronized (ps) {
325                ps.print(superString + (superString.endsWith(".") ? "" : ".")
326                        + "  Root exception is ");
327                rootCause.printStackTrace(ps);
328            }
329        } else {
330            super.printStackTrace(ps);
331        }
332    }
333
334    /**
335     * Prints this exception's stack trace to a print writer. If this exception
336     * has a root exception; the stack trace of the root exception is printed to
337     * the print writer instead.
338     * 
339     * @param pw The non-null print writer to which to print.
340     * @supported.api
341     */
342    public void printStackTrace(java.io.PrintWriter pw) {
343        if (rootCause != null) {
344            String superString = super.toString();
345            synchronized (pw) {
346                pw.print(superString + (superString.endsWith(".") ? "" : ".")
347                        + "  Root exception is ");
348                rootCause.printStackTrace(pw);
349            }
350        } else {
351            super.printStackTrace(pw);
352        }
353    }
354
355    /**
356     * Get exception stack trace as a string.
357     * 
358     * java.lang.Throwable java.lang.Exception UMSException <name of exception
359     * being thrown>
360     */
361    static private String getStackTrace(Throwable xcpt) {
362        StringWriter sw = new StringWriter();
363        PrintWriter pw = new PrintWriter(sw);
364
365        xcpt.printStackTrace(pw);
366
367        return sw.toString();
368    }
369}