1   /*
2    * Copyright 2002,2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.jelly.tags.junit;
17  
18  import org.apache.commons.jelly.JellyException;
19  import org.apache.commons.jelly.JellyTagException;
20  import org.apache.commons.jelly.XMLOutput;
21  import org.apache.commons.jelly.util.ClassLoaderUtils;
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  
25  /***
26   * Runs its body and asserts that an exception is thrown by it.  If no
27   * exception is thrown the tag fails.  By default all exceptions are caught.
28   * If however <code>expected</code> was specified the body must throw
29   * an exception of the given class, otherwise the assertion fails.  The
30   * exception thrown by the body can also be of any subtype of the specified
31   * exception class.  The optional <code>var</code> attribute can be specified if
32   * the caught exception is to be exported to a variable.
33   */
34  public class AssertThrowsTag extends AssertTagSupport {
35  
36      /*** The Log to which logging calls will be made. */
37      private static final Log log = LogFactory.getLog(AssertThrowsTag.class);
38  
39      /***
40       * The variable name to export the caught exception to.
41       */
42      private String var;
43  
44      /***
45       * The class name (fully qualified) of the exception expected to be thrown
46       * by the body.  Also a superclass of the expected exception can be given.
47       */
48      private String expected;
49  
50      /***
51       * Sets the ClassLoader to be used when loading an exception class
52       */
53      private ClassLoader classLoader;
54  
55      // Tag interface
56      //-------------------------------------------------------------------------
57      public void doTag(XMLOutput output) throws JellyTagException {
58          Class throwableClass = null;
59          try {
60              throwableClass = getThrowableClass();
61              invokeBody(output);
62          }
63          catch (Throwable t) {
64              if (t instanceof JellyException) {
65                  // unwrap Jelly exceptions which wrap other exceptions
66                  JellyException je = (JellyException) t;
67                  if (je.getCause() != null) {
68                      t = je.getCause();
69                  }
70              }
71              if (var != null) {
72                  context.setVariable(var, t);
73              }
74              if (throwableClass != null && !throwableClass.isAssignableFrom(t.getClass())) {
75                  fail("Unexpected exception: " + t);
76              }
77              else {
78                  return;
79              }
80          }
81          fail("No exception was thrown.");
82      }
83  
84      // Properties
85      //-------------------------------------------------------------------------
86      /***
87       * Sets the class name of exception expected to be thrown by the body.  The
88       * class name must be fully qualified and can either be the expected
89       * exception class itself or any supertype of it, but must be a subtype of
90       * <code>java.lang.Throwable</code>.
91       */
92      public void setExpected(String expected) {
93          this.expected = expected;
94      }
95  
96      /***
97       * Sets the variable name to define for this expression.
98       */
99      public void setVar(String var) {
100         this.var = var;
101     }
102 
103     /***
104      * Sets the class loader to be used to load the exception type
105      */
106     public void setClassLoader(ClassLoader classLoader) {
107         this.classLoader = classLoader;
108     }
109 
110     public ClassLoader getClassLoader() {
111         return ClassLoaderUtils.getClassLoader(classLoader, getClass());
112     }
113 
114     // Implementation methods
115     //-------------------------------------------------------------------------
116 
117     /***
118      * Returns the <code>Class</code> corresponding to the class
119      * specified by <code>expected</code>. If
120      * <code>expected</code> was either not specified then <code>java. lang.
121      * Throwable</code> is returned.
122      * Otherwise if the class couldn't be
123      * found or doesn't denote an exception class then an exception is thrown.
124      *
125      * @return Class The class of the exception to expect
126      */
127     protected Class getThrowableClass() throws ClassNotFoundException {
128         if (expected == null) {
129             return Throwable.class;
130         }
131 
132         Class throwableClass = null;
133         try {
134             throwableClass = getClassLoader().loadClass(expected);
135         }
136         catch (ClassNotFoundException e) {
137             try {
138                 throwableClass = Thread.currentThread().getContextClassLoader().loadClass(expected);
139             }
140             catch (ClassNotFoundException e2) {
141                 log.warn( "Could not find exception class: " + expected );
142                 throw e;
143             }
144         }
145 
146         if (!Throwable.class.isAssignableFrom(throwableClass)) {
147             log.warn( "The class: " + expected + " is not an Exception class.");
148             return null;
149         }
150         return throwableClass;
151     }
152 }