001: /*
002: * Copyright (c) 2003 The Visigoth Software Society. All rights
003: * reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * 2. Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowledgement:
019: * "This product includes software developed by the
020: * Visigoth Software Society (http://www.visigoths.org/)."
021: * Alternately, this acknowledgement may appear in the software itself,
022: * if and wherever such third-party acknowledgements normally appear.
023: *
024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
025: * project contributors may be used to endorse or promote products derived
026: * from this software without prior written permission. For written
027: * permission, please contact visigoths@visigoths.org.
028: *
029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
030: * nor may "FreeMarker" or "Visigoth" appear in their names
031: * without prior written permission of the Visigoth Software Society.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of the Visigoth Software Society. For more
049: * information on the Visigoth Software Society, please see
050: * http://www.visigoths.org/
051: */
052:
053: package freemarker.template;
054:
055: import java.io.OutputStreamWriter;
056: import java.io.PrintWriter;
057: import java.io.StringWriter;
058: import java.lang.reflect.Method;
059:
060: import freemarker.core.Environment;
061:
062: /**
063: * The FreeMarker classes usually use this exception and its descendants to
064: * signal FreeMarker specific exceptions.
065: *
066: * @version $Id: TemplateException.java,v 1.26.2.1 2006/02/12 20:02:15 revusky Exp $
067: */
068: public class TemplateException extends Exception {
069:
070: private static final boolean BEFORE_1_4 = before14();
071:
072: private static boolean before14() {
073: Class ec = Exception.class;
074: try {
075: ec.getMethod("getCause", new Class[] {});
076: } catch (Throwable e) {
077: return true;
078: }
079: return false;
080: }
081:
082: private static final Class[] EMPTY_CLASS_ARRAY = new Class[] {};
083:
084: private static final Object[] EMPTY_OBJECT_ARRAY = new Object[] {};
085:
086: /** The underlying cause of this exception, if any */
087: private final Exception causeException;
088: private final Environment env;
089: private final String ftlInstructionStack;
090:
091: /**
092: * Constructs a TemplateException with no specified detail message
093: * or underlying cause.
094: */
095: public TemplateException(Environment env) {
096: this (null, null, env);
097: }
098:
099: /**
100: * Constructs a TemplateException with the given detail message,
101: * but no underlying cause exception.
102: *
103: * @param description the description of the error that occurred
104: */
105: public TemplateException(String description, Environment env) {
106: this (description, null, env);
107: }
108:
109: /**
110: * Constructs a TemplateException with the given underlying Exception,
111: * but no detail message.
112: *
113: * @param cause the underlying <code>Exception</code> that caused this
114: * exception to be raised
115: */
116: public TemplateException(Exception cause, Environment env) {
117: this (null, cause, env);
118: }
119:
120: /**
121: * Constructs a TemplateException with both a description of the error
122: * that occurred and the underlying Exception that caused this exception
123: * to be raised.
124: *
125: * @param description the description of the error that occurred
126: * @param cause the underlying <code>Exception</code> that caused this
127: * exception to be raised
128: */
129: public TemplateException(String description, Exception cause,
130: Environment env) {
131: super (getDescription(description, cause));
132: causeException = cause;
133: this .env = env;
134: if (env != null) {
135: StringWriter sw = new StringWriter();
136: PrintWriter pw = new PrintWriter(sw);
137: env.outputInstructionStack(pw);
138: pw.flush();
139: ftlInstructionStack = sw.toString();
140: } else {
141: ftlInstructionStack = "";
142: }
143: }
144:
145: private static String getDescription(String description,
146: Exception cause) {
147: if (description != null) {
148: return description;
149: }
150: if (cause != null) {
151: return cause.getClass().getName() + ": "
152: + cause.getMessage();
153: }
154: return "No error message";
155: }
156:
157: /**
158: * <p>Returns the underlying exception that caused this exception to be
159: * generated.</p>
160: * <p><b>Note:</b><br />
161: * avoided calling it <code>getCause</code> to avoid name clash with
162: * JDK 1.4 method. This would be problematic because the JDK 1.4 method
163: * returns a <code>Throwable</code> rather than an <code>Exception</code>.</p>
164: *
165: * @return the underlying <code>Exception</code>, if any, that caused this
166: * exception to be raised
167: */
168: public Exception getCauseException() {
169: return causeException;
170: }
171:
172: /**
173: * Returns the same exception as <code>getCauseException</code>. Provided
174: * to enable full JDK-generated stack traces when running under JDK 1.4.
175: *
176: * @see Throwable#getCause()
177: * @return the underlying <code>Exception</code>, if any, that caused this
178: * exception to be raised
179: */
180: public Throwable getCause() {
181: return causeException;
182: }
183:
184: /**
185: * Returns the quote of the problematic FTL instruction and the FTL stack strace.
186: */
187: public String getFTLInstructionStack() {
188: return ftlInstructionStack;
189: }
190:
191: /**
192: * @return the execution environment in which the exception occurred
193: */
194: public Environment getEnvironment() {
195: return env;
196: }
197:
198: public void printStackTrace(java.io.PrintStream ps) {
199: PrintWriter pw = new PrintWriter(new OutputStreamWriter(ps),
200: true);
201: printStackTrace(pw);
202: pw.flush();
203: }
204:
205: public void printStackTrace(PrintWriter pw) {
206: pw.println();
207: pw.println(getMessage());
208: if (ftlInstructionStack != null
209: && ftlInstructionStack.length() != 0) {
210: pw.println("The problematic instruction:");
211: pw.println(ftlInstructionStack);
212: }
213: pw.println("Java backtrace for programmers:");
214: pw.println("----------");
215: super .printStackTrace(pw);
216: if (BEFORE_1_4 && causeException != null) {
217: pw.println("Underlying cause: ");
218: causeException.printStackTrace(pw);
219: }
220:
221: // Dirty hack to fight with stupid ServletException class whose
222: // getCause() method doesn't work properly. Also an aid for pre-J2xE 1.4
223: // users.
224: try {
225: // Reflection is used to prevent dependency on Servlet classes.
226: Method m = causeException.getClass().getMethod(
227: "getRootCause", EMPTY_CLASS_ARRAY);
228: Throwable rootCause = (Throwable) m.invoke(causeException,
229: EMPTY_OBJECT_ARRAY);
230: if (rootCause != null) {
231: Throwable j14Cause = null;
232: if (!BEFORE_1_4 && causeException != null) {
233: m = causeException.getClass().getMethod("getCause",
234: EMPTY_CLASS_ARRAY);
235: j14Cause = (Throwable) m.invoke(causeException,
236: EMPTY_OBJECT_ARRAY);
237: }
238: if (j14Cause == null) {
239: pw.println("ServletException root cause: ");
240: rootCause.printStackTrace(pw);
241: }
242: }
243: } catch (Throwable exc) {
244: ; // ignore
245: }
246: }
247:
248: }
|