KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cactus > internal > server > AbstractWebTestCaller


1 /*
2  * ========================================================================
3  *
4  * Copyright 2001-2004 The Apache Software Foundation.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * ========================================================================
19  */

20 package org.apache.cactus.internal.server;
21
22 import java.io.IOException JavaDoc;
23 import java.io.Writer JavaDoc;
24
25 import java.lang.reflect.Constructor JavaDoc;
26
27 import javax.servlet.ServletException JavaDoc;
28
29 import junit.framework.Test;
30 import junit.framework.TestCase;
31
32 import org.apache.cactus.internal.CactusTestCase;
33 import org.apache.cactus.internal.HttpServiceDefinition;
34 import org.apache.cactus.internal.ServiceEnumeration;
35 import org.apache.cactus.internal.WebTestResult;
36 import org.apache.cactus.internal.configuration.Version;
37 import org.apache.cactus.internal.util.ClassLoaderUtils;
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40
41 /**
42  * Responsible for instanciating the <code>TestCase</code> class on the server
43  * side, set up the implicit objects and call the test method. This class
44  * provides a common abstraction for all test web requests.
45  *
46  * @version $Id: AbstractWebTestCaller.java,v 1.1 2004/05/22 11:34:45 vmassol Exp $
47  */

48 public abstract class AbstractWebTestCaller
49 {
50     /**
51      * Name of the attribute in the <code>application</code> scope that will
52      * hold the results of the test.
53      */

54     protected static final String JavaDoc TEST_RESULTS =
55         "ServletTestRedirector_TestResults";
56
57     /**
58      * The logger.
59      */

60     private static final Log LOGGER =
61         LogFactory.getLog(AbstractWebTestCaller.class);
62
63     /**
64      * The implicit objects (which will be used to set the test case fields
65      * in the <code>setTesCaseFields</code> method.
66      */

67     protected WebImplicitObjects webImplicitObjects;
68
69     /**
70      * @param theObjects the implicit objects coming from the redirector
71      */

72     public AbstractWebTestCaller(WebImplicitObjects theObjects)
73     {
74         this.webImplicitObjects = theObjects;
75     }
76
77     /**
78      * Sets the implicit object in the test case class
79      *
80      * @param theTestCase the instance of the test case class on which the
81      * class variable (implicit objects) should be set
82      * @exception Exception if an errors occurs when setting the implicit
83      * objects
84      */

85     protected abstract void setTestCaseFields(TestCase theTestCase)
86         throws Exception JavaDoc;
87
88     /**
89      * @return a <code>Writer</code> object that will be used to return the
90      * test result to the client side.
91      * @exception IOException if an error occurs when retrieving the writer
92      */

93     protected abstract Writer JavaDoc getResponseWriter() throws IOException JavaDoc;
94
95     /**
96      * Calls a test method. The parameters needed to call this method are found
97      * in the HTTP request. Save the results in the <code>application</code>
98      * scope so that the Get Test Result service can find them.
99      *
100      * @exception ServletException if an unexpected error occurred
101      */

102     public void doTest() throws ServletException JavaDoc
103     {
104         WebTestResult result = null;
105
106         try
107         {
108             // Create an instance of the test class
109
TestCase testInstance = getTestClassInstance(
110                 getTestClassName(), getWrappedTestClassName(),
111                 getTestMethodName());
112
113             // Set its fields (implicit objects)
114
setTestCaseFields(testInstance);
115
116             // Call it's method corresponding to the current test case
117
if (testInstance instanceof CactusTestCase)
118             {
119                 ((CactusTestCase) testInstance).runBareServer();
120                 
121             }
122             else
123             {
124                 testInstance.runBare();
125             }
126
127             // Return an instance of <code>WebTestResult</code> with a
128
// positive result.
129
result = new WebTestResult();
130         }
131         catch (Throwable JavaDoc e)
132         {
133             // An error occurred, return an instance of
134
// <code>WebTestResult</code> with an exception.
135
result = new WebTestResult(e);
136         }
137
138         LOGGER.debug("Test result : [" + result + "]");
139
140
141         // Set the test result.
142
this.webImplicitObjects.getServletContext()
143             .setAttribute(TEST_RESULTS, result);
144
145         LOGGER.debug("Result saved in context scope");
146     }
147
148     /**
149      * Return the last test results in the HTTP response.
150      *
151      * @exception ServletException if an unexpected error occurred
152      */

153     public void doGetResults() throws ServletException JavaDoc
154     {
155         // One could think there is a potential risk that the client side of
156
// Cactus will request the result before it has been written to the
157
// context scope as the HTTP request will not block in some containers.
158
// However this will not happen because on the client side, once the
159
// first request is done to execute the test, all the result is read
160
// by the AutoReadHttpURLConnection class, thus ensuring that the
161
// request is fully finished and the result has been committed ...
162
WebTestResult result = (WebTestResult) (this.webImplicitObjects
163             .getServletContext().getAttribute(TEST_RESULTS));
164
165         // It can happen that the result has not been written in the Servlet
166
// context. This could happen for example when using a load-balancer
167
// which would direct the second Cactus HTTP connection to another
168
// instance. In that case, we throw an error.
169
if (result == null)
170         {
171             String JavaDoc message = "Error getting test result. This could happen "
172                 + "for example if you're using a load-balancer. Please disable "
173                 + "it before running Cactus tests.";
174
175             LOGGER.error(message);
176             throw new ServletException JavaDoc(message);
177         }
178
179         LOGGER.debug("Test Result = [" + result + "]");
180
181         // Write back the results to the outgoing stream as an XML string.
182

183         // Use UTF-8 to transfer the result back
184
webImplicitObjects.getHttpServletResponse().setContentType(
185             "text/xml; charset=UTF-8");
186
187         try
188         {
189             Writer JavaDoc writer = getResponseWriter();
190
191             writer.write(result.toXml());
192             writer.close();
193         }
194         catch (IOException JavaDoc e)
195         {
196             String JavaDoc message = "Error writing WebTestResult instance to output "
197                 + "stream";
198
199             LOGGER.error(message, e);
200             throw new ServletException JavaDoc(message, e);
201         }
202     }
203
204     /**
205      * Run the connection test between client and server. This is just to
206      * ensure that configuration is set up correctly.
207      *
208      * @exception ServletException if an unexpected error occurred
209      */

210     public void doRunTest() throws ServletException JavaDoc
211     {
212         // Do not return any http response (not needed). It is enough to
213
// know this point has been reached ... it means the connection has
214
// been established !
215
}
216
217     /**
218      * Return the cactus version. This is to make sure both the client side
219      * and server side are using the same version.
220      *
221      * @exception ServletException if an unexpected error occurred
222      */

223     public void doGetVersion() throws ServletException JavaDoc
224     {
225         try
226         {
227             Writer JavaDoc writer = getResponseWriter();
228             writer.write(Version.VERSION);
229             writer.close();
230         }
231         catch (IOException JavaDoc e)
232         {
233             String JavaDoc message = "Error writing HTTP response back to client "
234                 + "for service [" + ServiceEnumeration.GET_VERSION_SERVICE
235                 + "]";
236
237             LOGGER.error(message, e);
238             throw new ServletException JavaDoc(message, e);
239         }
240     }
241     
242     /**
243      * Create an HTTP Session and returns the response that contains the
244      * HTTP session as a cookie (unless URL rewriting is used in which
245      * case the jsesssionid cookie is not returned).
246      *
247      * @exception ServletException if an unexpected error occurred
248      */

249     public void doCreateSession() throws ServletException JavaDoc
250     {
251         // Create an HTTP session
252
this.webImplicitObjects.getHttpServletRequest().getSession(true);
253
254         try
255         {
256             Writer JavaDoc writer = getResponseWriter();
257             writer.close();
258         }
259         catch (IOException JavaDoc e)
260         {
261             String JavaDoc message = "Error writing HTTP response back to client "
262                 + "for service [" + ServiceEnumeration.CREATE_SESSION_SERVICE
263                 + "]";
264
265             LOGGER.error(message, e);
266             throw new ServletException JavaDoc(message, e);
267         }
268     }
269
270     /**
271      * @return the class to test class name, extracted from the HTTP request
272      * @exception ServletException if the class name of the test case is missing
273      * from the HTTP request
274      */

275     protected String JavaDoc getTestClassName() throws ServletException JavaDoc
276     {
277         String JavaDoc queryString = this.webImplicitObjects.getHttpServletRequest()
278             .getQueryString();
279         String JavaDoc className = ServletUtil.getQueryStringParameter(queryString,
280             HttpServiceDefinition.CLASS_NAME_PARAM);
281
282         if (className == null)
283         {
284             String JavaDoc message = "Missing class name parameter ["
285                 + HttpServiceDefinition.CLASS_NAME_PARAM
286                 + "] in HTTP request.";
287
288             LOGGER.error(message);
289             throw new ServletException JavaDoc(message);
290         }
291
292         LOGGER.debug("Class to call = [" + className + "]");
293
294         return className;
295     }
296
297     /**
298      * @return the optional test class that is wrapped by a Cactus test case,
299      * extracted from the HTTP request
300      * @exception ServletException if the wrapped class name is missing from
301      * the HTTP request
302      */

303     protected String JavaDoc getWrappedTestClassName() throws ServletException JavaDoc
304     {
305         String JavaDoc queryString = this.webImplicitObjects.getHttpServletRequest()
306             .getQueryString();
307         String JavaDoc className = ServletUtil.getQueryStringParameter(queryString,
308             HttpServiceDefinition.WRAPPED_CLASS_NAME_PARAM);
309
310         if (className == null)
311         {
312             LOGGER.debug("No wrapped test class");
313         }
314         else
315         {
316             LOGGER.debug("Wrapped test class = [" + className + "]");
317         }
318
319         return className;
320     }
321
322     /**
323      * @return the class method to call for the current test case, extracted
324      * from the HTTP request
325      * @exception ServletException if the method name of the test case is
326      * missing from the HTTP request
327      */

328     protected String JavaDoc getTestMethodName() throws ServletException JavaDoc
329     {
330         String JavaDoc queryString = this.webImplicitObjects.getHttpServletRequest()
331             .getQueryString();
332         String JavaDoc methodName = ServletUtil.getQueryStringParameter(queryString,
333             HttpServiceDefinition.METHOD_NAME_PARAM);
334
335         if (methodName == null)
336         {
337             String JavaDoc message = "Missing method name parameter ["
338                 + HttpServiceDefinition.METHOD_NAME_PARAM
339                 + "] in HTTP request.";
340
341             LOGGER.error(message);
342             throw new ServletException JavaDoc(message);
343         }
344
345         LOGGER.debug("Method to call = " + methodName);
346
347         return methodName;
348     }
349
350     /**
351      * @return true if the auto session flag for the Session can be found in
352      * the HTTP request
353      */

354     protected boolean isAutoSession()
355     {
356         String JavaDoc queryString = this.webImplicitObjects.getHttpServletRequest()
357             .getQueryString();
358         String JavaDoc autoSession = ServletUtil.getQueryStringParameter(queryString,
359             HttpServiceDefinition.AUTOSESSION_NAME_PARAM);
360
361         boolean isAutomaticSession =
362             Boolean.valueOf(autoSession).booleanValue();
363
364         LOGGER.debug("Auto session is " + isAutomaticSession);
365
366         return isAutomaticSession;
367     }
368
369     /**
370      * @param theClassName the name of the test class
371      * @param theWrappedClassName the name of the wrapped test class. Can be
372      * null if there is none
373      * @param theTestCaseName the name of the current test case
374      * @return an instance of the test class to call
375      * @exception ServletException if the test case instance for the current
376      * test fails to be instanciated (for example if some
377      * information is missing from the HTTP request)
378      */

379     protected TestCase getTestClassInstance(
380         String JavaDoc theClassName, String JavaDoc theWrappedClassName,
381         String JavaDoc theTestCaseName) throws ServletException JavaDoc
382     {
383         // Get the class to call and build an instance of it.
384
Class JavaDoc testClass = getTestClassClass(theClassName);
385         TestCase testInstance = null;
386         Constructor JavaDoc constructor;
387         
388         try
389         {
390             if (theWrappedClassName == null)
391             {
392                 constructor = getTestClassConstructor(testClass);
393
394                 if (constructor.getParameterTypes().length == 0)
395                 {
396                     testInstance = (TestCase) constructor.newInstance(
397                         new Object JavaDoc[0]);
398                     ((TestCase) testInstance).setName(theTestCaseName);
399                 }
400                 else
401                 {
402                     testInstance = (TestCase) constructor.newInstance(
403                         new Object JavaDoc[] {theTestCaseName});
404                 }
405             }
406             else
407             {
408                 Class JavaDoc wrappedTestClass =
409                     getTestClassClass(theWrappedClassName);
410                 Constructor JavaDoc wrappedConstructor =
411                     getTestClassConstructor(wrappedTestClass);
412
413                 TestCase wrappedTestInstance;
414                 if (wrappedConstructor.getParameterTypes().length == 0)
415                 {
416                     wrappedTestInstance =
417                         (TestCase) wrappedConstructor.newInstance(
418                         new Object JavaDoc[0]);
419                     wrappedTestInstance.setName(theTestCaseName);
420                 }
421                 else
422                 {
423                     wrappedTestInstance =
424                         (TestCase) wrappedConstructor.newInstance(
425                         new Object JavaDoc[] {theTestCaseName});
426                 }
427
428                 constructor = testClass.getConstructor(
429                     new Class JavaDoc[] {String JavaDoc.class, Test.class});
430
431                 testInstance =
432                     (TestCase) constructor.newInstance(
433                     new Object JavaDoc[] {theTestCaseName, wrappedTestInstance});
434             }
435         }
436         catch (Exception JavaDoc e)
437         {
438             String JavaDoc message = "Error instantiating class [" + theClassName + "(["
439                 + theTestCaseName + "], [" + theWrappedClassName + "])]";
440
441             LOGGER.error(message, e);
442             throw new ServletException JavaDoc(message, e);
443         }
444
445         return testInstance;
446     }
447
448     /**
449      * @param theTestClass the test class for which we want to find the
450      * constructor
451      * @return the availble constructor for the test class
452      * @throws NoSuchMethodException if no suitable constructor is found
453      */

454     private Constructor JavaDoc getTestClassConstructor(Class JavaDoc theTestClass)
455         throws NoSuchMethodException JavaDoc
456     {
457         Constructor JavaDoc constructor;
458         try
459         {
460             constructor = theTestClass.getConstructor(
461                 new Class JavaDoc[] {String JavaDoc.class});
462         }
463         catch (NoSuchMethodException JavaDoc e)
464         {
465             constructor = theTestClass.getConstructor(new Class JavaDoc[0]);
466         }
467         return constructor;
468     }
469
470     /**
471      * @param theClassName the name of the test class
472      * @return the class object the test class to call
473      * @exception ServletException if the class of the current test case
474      * cannot be loaded in memory (i.e. it is not in the
475      * classpath)
476      */

477     protected Class JavaDoc getTestClassClass(String JavaDoc theClassName)
478         throws ServletException JavaDoc
479     {
480         // Get the class to call and build an instance of it.
481
Class JavaDoc testClass = null;
482
483         try
484         {
485             testClass = ClassLoaderUtils.loadClass(theClassName,
486                 this.getClass());
487         }
488         catch (Exception JavaDoc e)
489         {
490             String JavaDoc message = "Error finding class [" + theClassName
491                 + "] using both the Context classloader and the webapp "
492                 + "classloader. Possible causes include:\r\n";
493
494             message += ("\t- Your webapp does not include your test "
495                 + "classes,\r\n");
496             message += ("\t- The cactus.jar is not located in your "
497                 + "WEB-INF/lib directory and your Container has not set the "
498                 + "Context classloader to point to the webapp one");
499
500             LOGGER.error(message, e);
501             throw new ServletException JavaDoc(message, e);
502         }
503
504         return testClass;
505     }
506
507 }
508
Popular Tags