001: /*
002: * ========================================================================
003: *
004: * Copyright 2001-2004 The Apache Software Foundation.
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * ========================================================================
019: */
020: package org.apache.cactus.server.runner;
021:
022: import java.io.InputStream;
023: import java.io.IOException;
024: import java.io.Reader;
025: import java.io.PrintWriter;
026: import java.io.StringReader;
027: import java.io.Writer;
028: import java.lang.reflect.Constructor;
029: import java.lang.reflect.Method;
030:
031: import javax.servlet.ServletException;
032: import javax.servlet.UnavailableException;
033: import javax.servlet.http.HttpServlet;
034: import javax.servlet.http.HttpServletRequest;
035: import javax.servlet.http.HttpServletResponse;
036:
037: import junit.framework.Test;
038: import junit.framework.TestResult;
039:
040: import org.apache.cactus.internal.configuration.BaseConfiguration;
041: import org.apache.cactus.internal.configuration.ConfigurationInitializer;
042: import org.apache.cactus.internal.server.runner.WebappTestRunner;
043: import org.apache.cactus.internal.server.runner.XMLFormatter;
044:
045: /**
046: * Helper servlet to start a JUnit Test Runner in a webapp.
047: *
048: * <p>
049: * This class currently does a couple of reflection tricks to avoid a direct
050: * dependancy on the TraX API (<code>javax.xml.transform.*</code>),
051: * encapsulated in the
052: * {@link org.apache.cactus.internal.server.runner.XMLTransformer} class.
053: * </p>
054: *
055: * @version $Id: ServletTestRunner.java 239016 2004-06-27 15:23:30Z vmassol $
056: */
057: public class ServletTestRunner extends HttpServlet {
058: /**
059: * HTTP parameter containing name of test suite to execute
060: */
061: private static final String HTTP_SUITE_PARAM = "suite";
062:
063: /**
064: * HTTP parameter that determines whether the XML test results should be
065: * transformed using the XSLT stylesheet specified as initialization
066: * parameter.
067: */
068: private static final String HTTP_TRANSFORM_PARAM = "transform";
069:
070: /**
071: * HTTP parameter containing name of the XSL stylesheet to put in the
072: * returned XML test result. It will only work if the browser supports
073: * this feature (IE does, I don't know about others).
074: */
075: private static final String HTTP_XSL_PARAM = "xsl";
076:
077: /**
078: * Name of the servlet initialization parameter that contains the path to
079: * the XSLT stylesheet for transforming the XML report into HTML.
080: */
081: private static final String XSL_STYLESHEET_PARAM = "xsl-stylesheet";
082:
083: /**
084: * Encoding to use for the returned XML.
085: */
086: private static final String ENCODING_PARAM = "encoding";
087:
088: /**
089: * The XML transformer. Avoid direct dependancy by using reflection.
090: */
091: private Object transformer = null;
092:
093: /**
094: * Indicates whether the servlet has sufficient permissions to set a
095: * system property, to be able to set the cactus.contentURL property. This
096: * is set to false if the first attempt to set the property throws a
097: * SecurityException.
098: */
099: private boolean canSetSystemProperty = true;
100:
101: /**
102: * Called by the container when the servlet is initialized.
103: *
104: * @throws ServletException If an initialization parameter contains an
105: * illegal value
106: */
107: public void init() throws ServletException {
108: // Reset the Cactus initialization so that multiple web application can
109: // work with different Cactus configurations. Otherwise, as the Cactus
110: // initialization is JVM-wide, the config is not read again.
111: ConfigurationInitializer.initialize(true);
112:
113: // Check whether XSLT transformations should be done server-side and
114: // build the templates if an XSLT processor is available
115: String xslStylesheetParam = getInitParameter(XSL_STYLESHEET_PARAM);
116: if (xslStylesheetParam != null) {
117: InputStream xslStylesheet = getServletContext()
118: .getResourceAsStream(xslStylesheetParam);
119: if (xslStylesheet != null) {
120: try {
121: Class transformerClass = Class
122: .forName("org.apache.cactus."
123: + "internal.server.runner.XMLTransformer");
124: Constructor transformerCtor = transformerClass
125: .getConstructor(new Class[] { InputStream.class });
126: transformer = transformerCtor
127: .newInstance(new Object[] { xslStylesheet });
128: } catch (Throwable t) {
129: log(
130: "Could not instantiate XMLTransformer - will not "
131: + "perform server-side XSLT transformations",
132: t);
133: }
134: } else {
135: throw new UnavailableException(
136: "The initialization parameter 'xsl-stylesheet' does not "
137: + "refer to an existing resource");
138: }
139: }
140: }
141:
142: /**
143: * Starts the test suite passed as a HTTP parameter
144: *
145: * @param theRequest the incoming HTTP client request
146: * @param theResponse the outgoing HTTP client request to send back.
147: *
148: * @exception ServletException if an error occurs when servicing the
149: * request
150: * @exception IOException if an error occurs when servicing the request
151: */
152: public void doGet(HttpServletRequest theRequest,
153: HttpServletResponse theResponse) throws ServletException,
154: IOException {
155: // Verify if a suite parameter exists
156: String suiteClassName = theRequest
157: .getParameter(HTTP_SUITE_PARAM);
158:
159: // Set up default Cactus System properties so that there is no need
160: // to have a cactus.properties file in WEB-INF/classes
161: setSystemProperties(theRequest);
162:
163: if (suiteClassName == null) {
164: throw new ServletException("Missing HTTP parameter ["
165: + HTTP_SUITE_PARAM + "] in request");
166: }
167:
168: // Get the XSL stylesheet parameter if any
169: String xslParam = theRequest.getParameter(HTTP_XSL_PARAM);
170:
171: // Get the transform parameter if any
172: String transformParam = theRequest
173: .getParameter(HTTP_TRANSFORM_PARAM);
174:
175: // Get the enconding parameter, if any
176: String encoding = theRequest.getParameter(ENCODING_PARAM);
177:
178: // Run the tests
179: String xml = run(suiteClassName, xslParam, encoding);
180:
181: // Check if we should do the transformation server side
182: if ((transformParam != null) && (transformer != null)) {
183: // Transform server side
184: try {
185: Method getContentTypeMethod = transformer.getClass()
186: .getMethod("getContentType", new Class[0]);
187: theResponse
188: .setContentType((String) getContentTypeMethod
189: .invoke(transformer, new Object[0]));
190: PrintWriter out = theResponse.getWriter();
191: Method transformMethod = transformer.getClass()
192: .getMethod(
193: "transform",
194: new Class[] { Reader.class,
195: Writer.class });
196: transformMethod.invoke(transformer, new Object[] {
197: new StringReader(xml), out });
198: } catch (Exception e) {
199: throw new ServletException(
200: "Problem applying the XSLT transformation", e);
201: }
202: } else {
203: // Transform client side (or not at all)
204: theResponse.setContentType("text/xml");
205: PrintWriter pw = theResponse.getWriter();
206: pw.println(xml);
207: }
208: }
209:
210: /**
211: * Set up default Cactus System properties so that there is no need
212: * to have a <code>cactus.properties</code> file in WEB-INF/classes.
213: * However, if a <code>cactus.properties</code> file is found, the
214: * properties are read from it.
215: *
216: * Note: If the JVM security policy prevents setting System properties
217: * you will still need to provide a cactus.properties file.
218: *
219: * @param theRequest the HTTP request coming from the browser (used
220: * to extract information about the server name, port, etc)
221: */
222: private void setSystemProperties(HttpServletRequest theRequest) {
223: // TODO: We cannot call BaseConfiguration.getContextURL() as it
224: // throws an exception if the context URL property is not defined.
225: // It would be good to change that behavior for a better reuse
226: String contextURL = System
227: .getProperty(BaseConfiguration.CACTUS_CONTEXT_URL_PROPERTY);
228:
229: // If the context URL propety has not been set, we set a default
230: // value based on the ServletTestRunner mapping in the web app.
231: if (contextURL == null) {
232: if (this .canSetSystemProperty) {
233: try {
234: System
235: .setProperty(
236: BaseConfiguration.CACTUS_CONTEXT_URL_PROPERTY,
237: "http://"
238: + theRequest
239: .getServerName()
240: + ":"
241: + theRequest
242: .getServerPort()
243: + theRequest
244: .getContextPath());
245: } catch (SecurityException se) {
246: log(
247: "Could not set the Cactus context URL as system "
248: + "property, you will have to include a Cactus "
249: + "properties file in the class path of the web "
250: + "application", se);
251: this .canSetSystemProperty = false;
252: }
253: }
254: }
255: }
256:
257: /**
258: * Run the suite tests and return the result.
259: *
260: * @param theSuiteClassName the suite containing the tests to run
261: * @param theXslFileName the name of the XSL stylesheet or null if we don't
262: * want to apply a stylesheet to the returned XML data
263: * @param theEncoding the encoding to use for the returned XML or null if
264: * default encoding is to be used
265: * @return the result object
266: * @exception ServletException if the suite failed to be loaded
267: */
268: protected String run(String theSuiteClassName,
269: String theXslFileName, String theEncoding)
270: throws ServletException {
271: TestResult result = new TestResult();
272:
273: XMLFormatter formatter = new XMLFormatter();
274: formatter.setXslFileName(theXslFileName);
275: formatter.setSuiteClassName(theSuiteClassName);
276:
277: if (theEncoding != null) {
278: formatter.setEncoding(theEncoding);
279: }
280:
281: result.addListener(formatter);
282:
283: long startTime = System.currentTimeMillis();
284:
285: WebappTestRunner testRunner = new WebappTestRunner();
286:
287: Test suite = testRunner.getTest(theSuiteClassName);
288:
289: if (suite == null) {
290: throw new ServletException("Failed to load test suite ["
291: + theSuiteClassName + "], Reason is ["
292: + testRunner.getErrorMessage() + "]");
293: }
294:
295: // Run the tests
296: suite.run(result);
297:
298: long endTime = System.currentTimeMillis();
299:
300: formatter.setTotalDuration(endTime - startTime);
301:
302: return formatter.toXML(result);
303: }
304: }
|