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