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 }