001    /*
002     * 
003     * $Revision: 13085 $ $Date: 2008-02-06 18:27:24 +0100 (Mi, 06 Feb 2008) $
004     *
005     * This file is part of ***  M y C o R e  ***
006     * See http://www.mycore.de/ for details.
007     *
008     * This program is free software; you can use it, redistribute it
009     * and / or modify it under the terms of the GNU General Public License
010     * (GPL) as published by the Free Software Foundation; either version 2
011     * of the License or (at your option) any later version.
012     *
013     * This program is distributed in the hope that it will be useful, but
014     * WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program, in a file called gpl.txt or license.txt.
020     * If not, write to the Free Software Foundation Inc.,
021     * 59 Temple Place - Suite 330, Boston, MA  02111-1307 USA
022     */
023    
024    package org.mycore.common;
025    
026    import java.io.ByteArrayOutputStream;
027    import java.io.PrintStream;
028    
029    /**
030     * Instances of this class represent a general exception thrown by any part of
031     * the MyCoRe implementation classes.
032     * 
033     * @author Jens Kupferschmidt
034     * @author Frank Lützenkirchen
035     * @version $Revision: 13085 $ $Date: 2008-02-06 18:27:24 +0100 (Mi, 06 Feb 2008) $
036     * 
037     * @see RuntimeException
038     */
039    public class MCRException extends RuntimeException {
040        /** the embedded exception that was thrown by an underlying system */
041        protected Exception exception;
042    
043        /**
044         * Creates a new MCRException with an error message
045         * 
046         * @param message
047         *            the error message for this exception
048         */
049        public MCRException(String message) {
050            super(message);
051        }
052    
053        /**
054         * Creates a new MCRException with an error message and a reference to an
055         * exception thrown by an underlying system. Normally, this exception will
056         * be the cause why we would throw an MCRException, e. g. when something in
057         * the datastore goes wrong.
058         * 
059         * @param message
060         *            the error message for this exception
061         * @param exception
062         *            the exception that was thrown by an underlying system
063         */
064        public MCRException(String message, Exception exception) {
065            this(message);
066            this.exception = exception;
067        }
068    
069        /**
070         * Returns the exception thrown by an underlying system
071         * 
072         * @return the exception thrown by an underlying system
073         */
074        public Exception getException() {
075            return exception;
076        }
077    
078        /**
079         * Returns a String containing the invocation stack trace for this exception
080         * 
081         * @return a String containing the invocation stack trace for this exception
082         */
083        public String getStackTraceAsString() {
084            return getStackTraceAsString(this);
085        }
086    
087        /**
088         * Returns a String containing the invocation stack trace of an exception
089         * 
090         * @param ex
091         *            the exception you want the stack trace of
092         * @return the invocation stack trace of an exception
093         */
094        public static String getStackTraceAsString(Throwable ex) {
095            // We let Java print the stack trace to a buffer in memory to be able to
096            // get it as String:
097            ByteArrayOutputStream baos = new ByteArrayOutputStream();
098    
099            try {
100                PrintStream buffer = new PrintStream(baos);
101                ex.printStackTrace(buffer);
102                buffer.close();
103            } catch (Exception willNeverBeThrown) {
104            }
105    
106            return baos.toString();
107        }
108    
109        /** Counter to prevent a recursion between getStackTrace() and toString() */
110        private int toStringInvocationCounter = 0;
111    
112        /**
113         * Returns a String representation of this exception and all its properties
114         * 
115         * @return a String representation of this exception and all its properties
116         */
117        public synchronized String toString() {
118            // Use counter to prevent a recursion between getStackTrace() and
119            // toString()
120            toStringInvocationCounter++;
121    
122            if (toStringInvocationCounter > 1) {
123                return super.toString();
124            }
125    
126            StringBuffer sb = new StringBuffer();
127    
128            sb.append("MyCoRe Exception: ").append(getClass().getName());
129            sb.append("\n\n");
130            sb.append("Message:\n");
131            sb.append(getMessage()).append("\n\n");
132            sb.append("Stack trace:\n");
133            sb.append(getStackTraceAsString());
134    
135            if (exception != null) {
136                sb.append("\n");
137                sb.append("This exception was thrown because of the following underlying exception:\n\n");
138                sb.append(exception.getClass().getName());
139                sb.append("\n\n");
140    
141                String msg = exception.getLocalizedMessage();
142    
143                if (msg != null) {
144                    sb.append("Message:\n").append(msg).append("\n\n");
145                }
146    
147                sb.append("Stack trace:\n");
148                sb.append(getStackTraceAsString(exception));
149            }
150    
151            toStringInvocationCounter--;
152    
153            return sb.toString();
154        }
155    }