001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 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: FSRedirectException.java,v 1.2 2008/06/25 05:46:40 qcheng Exp $
026 *
027 */
028
029/*
030 * Portions Copyright 2013 ForgeRock AS
031 */
032package com.sun.identity.federation.common;
033
034import com.sun.identity.shared.locale.L10NMessageImpl;
035import java.io.PrintWriter;
036import java.io.StringWriter;
037
038/**
039 * <PRE>
040 * This class is the super-class for all Federation <B>checked</B> exceptions.
041 *
042 *
043 * Some Exception throwing guidelines:
044 * -------------------------------------
045 *
046 * <B> Checked exceptions </B> are sub-classes of java.lang.Exception; methods
047 * throwing this type of exception are forced to define a throws clause
048 * in the method signature and client programmers need to catch and
049 * handle the exception with a try/catch block or declare the throws clause
050 * in their methods.
051 * <B> Unchecked exceptions </B> are sub-classes of java.lang.RuntimeException.
052 * Client programmers don't have to deal with the exception using a
053 * try/catch block and the method throwing it does not have to define it
054 * in its signature.
055 *
056 * - If your method encounters an abnormal condition which causes it
057 *   to be unable to fulfill its contract, or throw a checked or
058 *   unchecked exception (either FSRedirectException or RuntimeException).
059 *
060 * - If your method discovers that a client has breached its contract,
061 *   for example, passing a null as a parameter where a non-null value is
062 *   required, throw an unchecked exception (RuntimeException).
063 *
064 * - If your method is unable to fulfill its contract and you feel client
065 *   programmers should consciously decide how to handle, throw checked
066 *   exceptions (FSRedirectException).
067 *
068 *
069 * Embedded/Nested Exceptions:
070 * --------------------------
071 *
072 *  An exception of type FSRedirectException can embed any
073 *  exception of type Throwable. Embedded exceptions ensure traceability
074 *  of errors in a multi-tiered application. For example, in a simple 3-
075 *  Tier model - presentation/client tier, middle/domain tier and
076 *  database/persistence tier - the real cause of error might be lost by the
077 *  time control, which is passed back from the persistence tier to the client
078 *  tier. To ensure tracking info, the constructor FSRedirectException(
079 *  message,Throwable) should be used while throwing the exception.
080 *  Client programs can then invoke the #getRootCause() method
081 *  to get the underlying cause.
082 *
083 * Exception hierarchy should be defined:
084 * -------------------------------------
085 * An exception for each abnormal cause should be created.
086 * FSRedirectException should probably be thrown only by external API's.
087 * Even these should have embedded exceptions from lower level tiers.
088 * Every package should define its own exception hierarchies specific
089 * to its context, for example, account management exceptions should be
090 * defined in the accountmgmt package.
091 *
092 * Localizing Error Messages
093 * -------------------------
094 * The java resource bundle mechanism is used to implement localization.
095 * The ResourceSet and ResourceSetManager classes are used to implement
096 * localization.
097 *
098 * Steps for creating FSRedirectException Sub-classes and messages
099 * ------------------------------------------------------
100 *
101 * 1. Identify the package this exception will belong to.
102 *      account management related exception
103 *      should be part of the accountmgmt package.
104 *
105 * 2. Each package should have its own properties file to store
106 *      error messages.
107 *      For example accountmgmt.properties in package accountmgmt
108 
109 * 3. Create a sub-class of FSRedirectException and override the constructors.
110 *
111 *    public class FSAccountManagementException extends FSRedirectException {
112 *        public FSAccountManagementException() {
113 *                super();
114 *        }
115 *        public FSAccountManagementException(String msg) {
116 *                super(msg);
117 *        }
118 *        public FSAccountManagementException(String msg, Throwable t) {
119 *                super(msg,t);
120 *        }
121 *
122 *
123 * Throwing/Catching Exception Examples:
124 * ------------------------------------
125 *
126 * 1. Throwing a non-nested Exception
127 *      <B>(not recommended, use Ex. 3 below)</B>
128 *       FSRedirectException ux = 
129 *           new FSRedirectException("Some weird error!...");
130 *       throw ux;
131 *
132 * 2. Throwing a nested Exception
133 *      <B>(not recommended, use Ex. 3 below)</B>
134 *       try {
135 *               .......
136 *               .......
137 *       } catch (UMSException umse) {
138 *         FSRedirectException fse =
139 *                       new FSRedirectException("Some weird error!...", le);
140 *        throw ux;
141 *       }
142 *
143 * 3. Throwing an Exception using the ResourceSetManager
144 * <TBD : write some eg & format for properties file>
145 * 
146 * - Logging/Dealing with an Exception, inclunding all nested exceptions
147 *       try {
148 *               .......
149 *               .......
150 *       } catch (FSRedirectException fse) {
151 *
152 *               if (fse.getRootCause() instanceof UMSException) {
153 *                       PrintWriter pw = new PrintWriter(<some file stream>);
154 *                       fse.log(pw);
155 *               } else {
156 *                      System.out.println(fse.getMessage());
157 *               }
158 *       }
159 *
160 * </PRE>
161 *
162 * @see #FSRedirectException(String, Object[], Throwable)
163 * @see #getRootCause()
164 * @see java.lang.Exception
165 * @see java.lang.RuntimeException
166 *
167 * @supported.api
168 */
169public class FSRedirectException extends L10NMessageImpl {
170    protected String _message = null;
171    protected Throwable rootCause = null;
172
173    /**
174     * Constructor
175     *
176     * @param errorCode Key of the error message in resource bundle.
177     * @param args Arguments to the message.
178     */
179    public FSRedirectException(String errorCode, Object[] args) {
180        super(FSUtils.BUNDLE_NAME, errorCode, args);
181    }
182
183    /**
184     * Constructor
185     *
186     * @param errorCode Key of the error message in resource bundle.
187     * @param args Arguments to the message.
188     * @param rootCause  An embedded exception
189     */
190    public FSRedirectException(String errorCode, Object[] args,
191        Throwable rootCause) {
192        super(FSUtils.BUNDLE_NAME, errorCode, args);
193        this.rootCause = rootCause;
194    }
195
196    /**
197     * Constructor 
198     * Constructs a <code>FSRedirectException</code> with a detailed message.
199     *
200     * @param message
201     * Detailed message for this exception.
202     * @supported.api
203     */
204    public FSRedirectException(String message) {
205        super(message);
206        _message = message;
207    }
208    
209    /**
210     * Constructor
211     * Constructs a <code>FSRedirectException</code> with a message and
212     * an embedded exception.
213     *
214     * @param message  Detailed message for this exception.
215     * @param rootCause  An embedded exception
216     * @supported.api
217     */
218    public FSRedirectException(Throwable rootCause, String message) {
219        super(message);
220        _message = message;
221        this.rootCause = rootCause;
222    }
223   
224    /**
225     * Constructor
226     * Constructs a <code>FSRedirectException</code> with an exception.
227     *
228     * @param ex  Exception
229     * @supported.api
230     */
231    public FSRedirectException(Exception ex) {
232       super(ex);
233    }
234    
235    /**
236     * Returns the embedded exception.
237     *
238     * @return the embedded exception.
239     * @supported.api
240     */
241    public Throwable getRootCause() {
242        return rootCause;
243    }
244    
245    /**
246     * Formats this <code>FSRedirectException</code> to a 
247     * <code>PrintWriter</code>.
248     *
249     * @param out <code>PrintWriter</code> to write exception to.
250     * @return The out parameter passed in.
251     * @see java.io.PrintWriter
252     * @supported.api
253     */
254    public PrintWriter log(PrintWriter out) {
255        return log(this, out);
256    }
257    
258    /**
259     * Formats an Exception to a <code>PrintWriter</code>.
260     *
261     * @param xcpt <code>Exception</code> to log.
262     * @param out <code>PrintWriter</code> to write exception to.
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     * Returns a formatted <code>FSRedirectException</code> exception message;
280     * includes embedded exceptions.
281     *
282     * @return a formatted <code>FSRedirectException</code> exception message.
283     * @supported.api
284     */
285    public String toString() {
286        
287        StringBuffer buf = new StringBuffer();
288        buf.append("--------------------------------------");
289        buf.append("Got Federation Exception\n");
290        
291        String msg = getMessage();
292        if(msg != null && msg.length() > 0) {
293            buf.append("Message: ").append(getMessage());
294        }
295        
296        // Invoke toString() of rootCause first
297        if (rootCause != null) {
298            buf.append("\nLower level exception: ");
299            buf.append(getRootCause());
300        }
301        
302        return buf.toString();
303    }
304    
305    /**
306     * Prints this exception's stack trace to <tt>System.err</tt>.
307     * If this exception has a root exception; the stack trace of the
308     * root exception is printed to <tt>System.err</tt> instead.
309     * @supported.api
310     */
311    public void printStackTrace() {
312        printStackTrace( System.err );
313    }
314    
315    /**
316     * Prints this exception's stack trace to a print stream.
317     * If this exception has a root exception, the stack trace of the
318     * root exception is printed to the print stream instead.
319     * @param ps The non-null print stream to which to print.
320     * @supported.api
321     */
322    public void printStackTrace(java.io.PrintStream ps) {
323        if ( rootCause != null ) {
324            String superString = super.toString();
325            synchronized ( ps ) {
326                ps.print(superString
327                + (superString.endsWith(".") ? "" : ".")
328                + "  Root exception is ");
329                rootCause.printStackTrace( ps );
330            }
331        } else {
332            super.printStackTrace( ps );
333        }
334    }
335    
336    /**
337     * Prints this exception's stack trace to a print writer.
338     * If this exception has a root exception; the stack trace of the
339     * root exception is printed to the print writer instead.
340     * @param pw The non-null print writer to which to print.
341     * @supported.api
342     */
343    public void printStackTrace(java.io.PrintWriter pw) {
344        if ( rootCause != null ) {
345            String superString = super.toString();
346            synchronized (pw) {
347                pw.print(superString
348                + (superString.endsWith(".") ? "" : ".")
349                + "  Root exception is ");
350                rootCause.printStackTrace( pw );
351            }
352        } else {
353            super.printStackTrace( pw );
354        }
355    }
356    
357    /**
358     * Gets exception stack trace as a string.
359     * <pre>
360     *   java.lang.Throwable
361     *   java.lang.Exception
362     *   FSRedirectException
363     *   <name of exception being thrown>
364     * </pre>
365     */
366    static private String getStackTrace(Throwable xcpt) {
367        StringWriter sw = new StringWriter();
368        PrintWriter pw = new PrintWriter(sw);
369        
370        xcpt.printStackTrace(pw);
371        
372        return sw.toString();
373    }
374}