2011/08/05 - Jakarta Cactus has been retired.

For more information, please explore the Attic.

View Javadoc

1   /* 
2    * ========================================================================
3    * 
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements.  See the NOTICE file distributed with
6    * this work for additional information regarding copyright ownership.
7    * The ASF licenses this file to You under the Apache License, Version 2.0
8    * (the "License"); you may not use this file except in compliance with
9    * the License.  You may obtain a copy of the License at
10   * 
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * 
19   * ========================================================================
20   */
21  package org.apache.cactus.server.runner;
22  
23  import java.io.InputStream;
24  import java.io.IOException;
25  import java.io.Reader;
26  import java.io.PrintWriter;
27  import java.io.StringReader;
28  import java.io.Writer;
29  import java.lang.reflect.Constructor;
30  import java.lang.reflect.Method;
31  
32  import javax.servlet.ServletException;
33  import javax.servlet.UnavailableException;
34  import javax.servlet.http.HttpServlet;
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  
38  import junit.framework.Test;
39  import junit.framework.TestResult;
40  
41  import org.apache.cactus.internal.configuration.BaseConfiguration;
42  import org.apache.cactus.internal.configuration.ConfigurationInitializer;
43  import org.apache.cactus.internal.server.runner.WebappTestRunner;
44  import org.apache.cactus.internal.server.runner.XMLFormatter;
45  
46  /**
47   * Helper servlet to start a JUnit Test Runner in a webapp.
48   * 
49   * <p>
50   *   This class currently does a couple of reflection tricks to avoid a direct 
51   *   dependancy on the TraX API (<code>javax.xml.transform.*</code>),
52   *   encapsulated in the 
53   *   {@link org.apache.cactus.internal.server.runner.XMLTransformer} class.
54   * </p>
55   * 
56   * @version $Id: ServletTestRunner.java 239016 2004-06-27 15:23:30Z vmassol $
57   */
58  public class ServletTestRunner extends HttpServlet
59  {
60      /**
61       * HTTP parameter containing name of test suite to execute.
62       */
63      private static final String HTTP_SUITE_PARAM = "suite";
64  
65      /**
66       * HTTP parameter that determines whether the XML test results should be 
67       * transformed using the XSLT stylesheet specified as initialization 
68       * parameter.
69       */
70      private static final String HTTP_TRANSFORM_PARAM = "transform";
71  
72      /**
73       * HTTP parameter containing name of the XSL stylesheet to put in the
74       * returned XML test result. It will only work if the browser supports
75       * this feature (IE does, I don't know about others).
76       */
77      private static final String HTTP_XSL_PARAM = "xsl";
78  
79      /**
80       * Name of the servlet initialization parameter that contains the path to
81       * the XSLT stylesheet for transforming the XML report into HTML.
82       */
83      private static final String XSL_STYLESHEET_PARAM = "xsl-stylesheet";
84  
85      /**
86       * Encoding to use for the returned XML.
87       */
88      private static final String ENCODING_PARAM = "encoding";
89      
90      /**
91       * The XML transformer. Avoid direct dependancy by using reflection.
92       */
93      private Object transformer = null;
94  
95      /**
96       * Indicates whether the servlet has sufficient permissions to set a
97       * system property, to be able to set the cactus.contentURL property. This
98       * is set to false if the first attempt to set the property throws a
99       * SecurityException.
100      */
101     private boolean canSetSystemProperty = true;
102 
103     /**
104      * Called by the container when the servlet is initialized.
105      * 
106      * @throws ServletException If an initialization parameter contains an
107      *         illegal value
108      */
109     public void init() throws ServletException
110     {
111         // Reset the Cactus initialization so that multiple web application can 
112         // work with different Cactus configurations. Otherwise, as the Cactus 
113         // initialization is JVM-wide, the config is not read again.
114         ConfigurationInitializer.initialize(true);
115 
116         // Check whether XSLT transformations should be done server-side and
117         // build the templates if an XSLT processor is available
118         String xslStylesheetParam = getInitParameter(XSL_STYLESHEET_PARAM);
119         if (xslStylesheetParam != null)
120         {
121             InputStream xslStylesheet =
122                 getServletContext().getResourceAsStream(xslStylesheetParam);
123             if (xslStylesheet != null)
124             {
125                 try
126                 {
127                     Class transformerClass = Class.forName("org.apache.cactus."
128                         + "internal.server.runner.XMLTransformer");
129                     Constructor transformerCtor = 
130                         transformerClass.getConstructor(
131                         new Class[] {InputStream.class});
132                     transformer = transformerCtor.newInstance(
133                         new Object[] {xslStylesheet});
134                 }
135                 catch (Throwable t)
136                 {
137                     log("Could not instantiate XMLTransformer - will not "
138                         + "perform server-side XSLT transformations", t);
139                 }
140             }
141             else
142             {
143                 throw new UnavailableException(
144                     "The initialization parameter 'xsl-stylesheet' does not "
145                     + "refer to an existing resource");
146             }
147         }
148     }
149 
150     /**
151      * Starts the test suite passed as a HTTP parameter.
152      *
153      * @param theRequest the incoming HTTP client request
154      * @param theResponse the outgoing HTTP client request to send back.
155      *
156      * @exception ServletException if an error occurs when servicing the
157      *            request
158      * @exception IOException if an error occurs when servicing the request
159      */
160     public void doGet(HttpServletRequest theRequest, 
161         HttpServletResponse theResponse) throws ServletException, 
162         IOException
163     {
164         // Verify if a suite parameter exists
165         String suiteClassName = theRequest.getParameter(HTTP_SUITE_PARAM);
166 
167         // Set up default Cactus System properties so that there is no need
168         // to have a cactus.properties file in WEB-INF/classes
169         setSystemProperties(theRequest);
170 
171         if (suiteClassName == null)
172         {
173             throw new ServletException("Missing HTTP parameter ["
174                 + HTTP_SUITE_PARAM + "] in request");
175         }
176 
177         // Get the XSL stylesheet parameter if any
178         String xslParam = theRequest.getParameter(HTTP_XSL_PARAM);
179 
180         // Get the transform parameter if any
181         String transformParam = theRequest.getParameter(HTTP_TRANSFORM_PARAM);
182 
183         // Get the enconding parameter, if any
184         String encoding = theRequest.getParameter(ENCODING_PARAM);
185         
186         // Run the tests
187         String xml = run(suiteClassName, xslParam, encoding);
188 
189         // Check if we should do the transformation server side
190         if ((transformParam != null) && (transformer != null))
191         {
192             // Transform server side
193             try
194             {
195                 Method getContentTypeMethod =
196                     transformer.getClass().getMethod(
197                         "getContentType", new Class[0]);
198                 theResponse.setContentType((String)
199                     getContentTypeMethod.invoke(transformer, new Object[0]));
200                 PrintWriter out = theResponse.getWriter();
201                 Method transformMethod =
202                     transformer.getClass().getMethod(
203                         "transform", new Class[] {Reader.class, Writer.class});
204                 transformMethod.invoke(transformer,
205                     new Object[] {new StringReader(xml), out});
206             }
207             catch (Exception e)
208             {
209                 throw new ServletException(
210                     "Problem applying the XSLT transformation", e);
211             }
212         }
213         else
214         {
215             // Transform client side (or not at all)
216             theResponse.setContentType("text/xml");
217             PrintWriter pw = theResponse.getWriter();
218             pw.println(xml);
219         }
220     }
221 
222     /**
223      * Set up default Cactus System properties so that there is no need
224      * to have a <code>cactus.properties</code> file in WEB-INF/classes. 
225      * However, if a <code>cactus.properties</code> file is found, the 
226      * properties are read from it.
227      * 
228      * Note: If the JVM security policy prevents setting System properties
229      * you will still need to provide a cactus.properties file.
230      * 
231      * @param theRequest the HTTP request coming from the browser (used
232      *        to extract information about the server name, port, etc) 
233      */
234     private void setSystemProperties(HttpServletRequest theRequest)
235     {
236         // TODO: We cannot call BaseConfiguration.getContextURL() as it
237         // throws an exception if the context URL property is not defined.
238         // It would be good to change that behavior for a better reuse
239         String contextURL = System.getProperty(
240             BaseConfiguration.CACTUS_CONTEXT_URL_PROPERTY);
241 
242         // If the context URL propety has not been set, we set a default
243         // value based on the ServletTestRunner mapping in the web app.
244         if (contextURL == null)
245         {
246             if (this.canSetSystemProperty)
247             {
248                 try
249                 {
250                     System.setProperty(
251                         BaseConfiguration.CACTUS_CONTEXT_URL_PROPERTY,
252                         "http://" + theRequest.getServerName() + ":"
253                         + theRequest.getServerPort()
254                         + theRequest.getContextPath());
255                 }
256                 catch (SecurityException se)
257                 {
258                     log("Could not set the Cactus context URL as system "
259                         + "property, you will have to include a Cactus "
260                         + "properties file in the class path of the web "
261                         + "application", se);
262                     this.canSetSystemProperty = false;
263                 }
264             }
265         }
266     }
267     
268     /**
269      * Run the suite tests and return the result.
270      *
271      * @param theSuiteClassName the suite containing the tests to run
272      * @param theXslFileName the name of the XSL stylesheet or null if we don't
273      *        want to apply a stylesheet to the returned XML data
274      * @param theEncoding the encoding to use for the returned XML or null if
275      *        default encoding is to be used
276      * @return the result object
277      * @exception ServletException if the suite failed to be loaded
278      */
279     protected String run(String theSuiteClassName, String theXslFileName,
280         String theEncoding) throws ServletException
281     {
282         TestResult result = new TestResult();
283 
284         XMLFormatter formatter = new XMLFormatter();
285         formatter.setXslFileName(theXslFileName);
286         formatter.setSuiteClassName(theSuiteClassName);
287 
288         if (theEncoding != null)
289         {
290             formatter.setEncoding(theEncoding);
291         }
292         
293         result.addListener(formatter);
294 
295         long startTime = System.currentTimeMillis();
296 
297         WebappTestRunner testRunner = new WebappTestRunner();
298 
299         Test suite = testRunner.getTest(theSuiteClassName);
300 
301         if (suite == null)
302         {
303             throw new ServletException("Failed to load test suite ["
304                 + theSuiteClassName + "], Reason is ["
305                 + testRunner.getErrorMessage() + "]");
306         }
307 
308         // Run the tests
309         suite.run(result);
310 
311         long endTime = System.currentTimeMillis();
312 
313         formatter.setTotalDuration(endTime - startTime);
314 
315         return formatter.toXML(result);
316     }
317 }