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.integration.ant;
22  
23  import java.util.Enumeration;
24  import java.util.PropertyResourceBundle;
25  import java.util.ResourceBundle;
26  import java.io.File;
27  import java.io.FileInputStream;
28  import java.io.IOException;
29  import java.net.URL;
30  import java.net.MalformedURLException;
31  import org.apache.cactus.integration.ant.util.HttpProbe;
32  import org.apache.cactus.integration.api.deployable.DeployableFile;
33  import org.apache.cactus.integration.api.deployable.EarParser;
34  import org.apache.cactus.integration.api.deployable.WarParser;
35  import org.apache.tools.ant.BuildException;
36  import org.apache.tools.ant.Project;
37  import org.apache.tools.ant.taskdefs.optional.junit.JUnitTask;
38  import org.apache.tools.ant.taskdefs.optional.junit.JUnitTest;
39  import org.apache.tools.ant.types.Path;
40  import org.apache.tools.ant.types.Environment.Variable;
41  
42  /**
43   * An Ant task that extends the optional JUnit task to provide support for
44   * in-container testing. 
45   * This class is a refactor of CactusTask v.133 and
46   * RunContainerTask v.133 to use cargo
47   * 
48   * @version $Id: CactusTestTask.java 394301 2006-04-15 15:07:26Z felipeal $
49   * @since 1.8
50   */
51  public class CactusTestTask extends JUnitTask
52  {
53      /**
54       * The servlet port element.
55       */
56      private String serverName;
57  
58      /**
59       * The servlet port element.
60       */
61      private String servletPort;
62  
63      /**
64       * The context URL element.
65       */
66      private String contextURL;
67  
68      /**
69       * The testreportDir element.
70       */
71      private File toDir;
72  
73      /**
74       * The log element.
75       */
76      private File logs;
77  
78      /**
79       * The archive that contains the enterprise application that should be
80       * tested.
81       */
82      private File earFile;
83  
84      /**
85       * The archive that contains the web-app that is ready to be tested.
86       */
87      private File warFile;
88  
89      /**
90       * The runLocal element.
91       */
92      private boolean runLocal;
93  
94  
95      /**
96       * The deployable file.
97       */
98      private DeployableFile deployableFile;
99  
100     /**
101      * Additional classpath entries for the classpath that will be used to start
102      * the containers.
103      */
104     private Path additionalClasspath;
105 
106     /**
107      * Timeout after trying to connect(in ms).
108      */
109     private long timeout = 180000;
110 
111     /**
112      * The time interval in milliseconds to sleep between polling the container.
113      */
114     private long checkInterval = 500;
115 
116 
117     // Constructors -----------------------------------------------------------
118 
119     /**
120      * Constructor.
121      *
122      * @throws Exception
123      *             If the constructor of JUnitTask throws an exception
124      */
125     public CactusTestTask() throws Exception
126     {
127         super();
128     }
129 
130     // Public Methods -------------------------------------------------------
131 
132     /**
133      * @see org.apache.tools.ant.Task#init()
134      */
135     public void init()
136     {
137         super.init();
138 
139         addClasspathEntry("/org/apache/cactus/ServletTestCase.class");
140         addClasspathEntry("/org/apache/commons/logging/Log.class");
141         addClasspathEntry("/org/apache/commons/httpclient/HttpClient.class");
142     }
143 
144     /**
145      * {@inheritDoc}
146      * @see org.apache.tools.ant.Task#execute()
147      */
148     public void execute() throws BuildException
149     {
150         if ((this.warFile != null) && (this.earFile != null))
151         {
152              throw new BuildException(
153                     "You must specify either the [warfile] or "
154                           + "the [earfile] attribute but not both");
155         }
156         if ((this.warFile == null) && (this.earFile == null))
157         {
158             throw new BuildException("You must specify either the [warfile] or "
159                 + "the [earfile] attribute");
160         }
161         // Parse deployment descriptors for WAR or EAR files
162         if (this.warFile != null)
163         {
164             deployableFile = WarParser.parse(this.warFile);
165         }
166         else
167         {
168             deployableFile = EarParser.parse(this.earFile);
169         }
170 
171         addRedirectorNameProperties();
172 
173         if (runLocal)
174         {
175            log("Tests run locally", Project.MSG_VERBOSE);
176            super.execute();
177            return;
178         }
179 
180         if (this.contextURL == null)
181         {
182             if (this.servletPort == null)
183             {
184                  log("Using default servletport=8080", Project.MSG_INFO);
185                  servletPort = "8080";
186 
187             }
188             if (this.serverName == null)
189             {
190                 log("Using default servername=localhost", Project.MSG_INFO);
191                 serverName = "localhost";
192             }
193         }
194 
195         if (this.toDir == null)
196         {
197             throw new BuildException(
198                     "You must specify the test report directory");
199 
200         }
201 
202 
203         Variable contextUrlVar = new Variable();
204         contextUrlVar.setKey("cactus.contextURL");
205         if (this.contextURL != null)
206         {
207            contextUrlVar.setValue(this.contextURL);
208         }
209         else
210         {
211             contextUrlVar.setValue("http://" + this.serverName + ":"
212                     + this.servletPort
213                     + "/" + deployableFile.getTestContext());
214         }
215 
216         addSysproperty(contextUrlVar);
217 
218         //Setup logs
219         setupLogs();
220         //Run the test cases
221         testInContainer();
222     }
223 
224     /**
225      * Extracts the redirector mappings from the deployment descriptor and sets
226      * the corresponding system properties.
227      */
228     private void addRedirectorNameProperties()
229     {
230         String filterRedirectorMapping = deployableFile
231                 .getFilterRedirectorMapping();
232         if (filterRedirectorMapping != null)
233         {
234             Variable filterRedirectorVar = new Variable();
235             filterRedirectorVar.setKey("cactus.filterRedirectorName");
236             filterRedirectorVar.setValue(filterRedirectorMapping
237                     .substring(1));
238             addSysproperty(filterRedirectorVar);
239         }
240         else
241         {
242             log("No mapping of the filter redirector found",
243                     Project.MSG_VERBOSE);
244         }
245 
246         String jspRedirectorMapping = deployableFile.getJspRedirectorMapping();
247         if (jspRedirectorMapping != null)
248         {
249             Variable jspRedirectorVar = new Variable();
250             jspRedirectorVar.setKey("cactus.jspRedirectorName");
251             jspRedirectorVar.setValue(jspRedirectorMapping.substring(1));
252             addSysproperty(jspRedirectorVar);
253         }
254         else
255         {
256             log("No mapping of the JSP redirector found",
257                     Project.MSG_VERBOSE);
258         }
259 
260         String servletRedirectorMapping = deployableFile
261                 .getServletRedirectorMapping();
262         if (servletRedirectorMapping != null)
263         {
264             Variable servletRedirectorVar = new Variable();
265             servletRedirectorVar.setKey("cactus.servletRedirectorName");
266             servletRedirectorVar.setValue(servletRedirectorMapping
267                     .substring(1));
268             addSysproperty(servletRedirectorVar);
269         }
270         else
271         {
272             throw new BuildException("The WAR has not been cactified");
273         }
274 
275     }
276 
277 
278     /**
279      * Executes the unit tests in the given container.
280      *
281      */
282     private void testInContainer()
283     {
284         URL testURL = null;
285         try
286         {
287             if (this.contextURL == null)
288             {
289                 testURL = new URL("http://" + this.serverName + ":"
290                         + this.servletPort + "/"
291                         + deployableFile.getTestContext()
292                         + deployableFile.getServletRedirectorMapping()
293                         + "?Cactus_Service=RUN_TEST");
294             }
295             else
296             {
297                 testURL = new URL(this.contextURL
298                         + deployableFile.getServletRedirectorMapping()
299                         + "?Cactus_Service=RUN_TEST");
300             }
301 
302 
303         }
304         catch (MalformedURLException e)
305         {
306             throw new BuildException("Invalid URL format: " + testURL);
307         }
308         //Ping the container
309         //Continuously try calling the test URL until it succeeds or
310         // until a timeout is reached (we then throw a build exception).
311         try
312         {
313             HttpProbe httpProbe = new HttpProbe(testURL);
314             if (httpProbe.timeout(this.timeout, this.checkInterval))
315             {
316                 throw new BuildException("Failed to start the container after "
317                     + "more than [" + this.timeout + "] ms. Trying to connect "
318                     + "to the [" + testURL + "] test URL yielded an "
319                     + "error code. Please run in debug mode for more details  "
320                     + "about the error.");
321             }
322         }
323         catch (InterruptedException ie)
324         {
325             throw new BuildException("Unexpected thread interruption");
326         }
327         catch (IOException io)
328         {
329             throw new BuildException("Http reading exception");
330         }
331 
332         log("Starting up tests", Project.MSG_VERBOSE);
333         try
334         {
335 
336             Enumeration tests = getIndividualTests();
337             while (tests.hasMoreElements())
338             {
339                 JUnitTest test = (JUnitTest) tests.nextElement();
340                 if (test.shouldRun(getProject()))
341                 {
342                     test.setTodir(toDir);
343                     execute(test);
344                 }
345             }
346         }
347         finally
348         {
349             log("Finishing tests", Project.MSG_VERBOSE);
350 
351         }
352     }
353 
354     /**
355      * Sets the enterprise application archive that will be tested. It must
356      * already contain the test-cases and the required libraries as a web
357      * module.
358      *
359      * @param theEarFile
360      *            The EAR file to set
361      */
362     public final void setEarFile(File theEarFile)
363     {
364         if (this.warFile != null)
365         {
366             throw new BuildException(
367                     "You may only specify one of [earfile] and [warfile]");
368         }
369         this.earFile = theEarFile;
370     }
371 
372     /**
373      * Sets the web application archive that will be tested. It must already
374      * contain the test-cases and the required libraries.
375      *
376      * @param theWarFile
377      *            The WAR file to set
378      */
379     public final void setWarFile(File theWarFile)
380     {
381         if (this.earFile != null)
382         {
383             throw new BuildException(
384                     "You may only specify one of [earfile] and [warfile]");
385         }
386         this.warFile = theWarFile;
387     }
388 
389     /**
390      * Sets the server host that will be tested.
391      *
392      * @param theServerName
393      *            The server host
394      */
395     public final void setServerName(String theServerName)
396     {
397         this.serverName = theServerName;
398     }
399 
400     /**
401      * Sets the servlet port that will be tested.
402      *
403      * @param theServletPort
404      *            The servlet port
405      */
406     public final void setServletPort(String theServletPort)
407     {
408         this.servletPort = theServletPort;
409     }
410 
411     /**
412      * Sets the context url that will be tested.
413      *
414      * @param theContextURL
415      *            The context url
416      */
417     public final void setContextURL(String theContextURL)
418     {
419         this.contextURL = theContextURL;
420     }
421 
422     /**
423      * Sets the test report dir.
424      *
425      * @param theToDir
426      *            The test report to set
427      */
428     public final void setToDir(File theToDir)
429     {
430         this.toDir = theToDir;
431     }
432 
433     /**
434     * Sets the scope of the test.
435      *
436      * @param theRunLocal
437      *            Run Local define
438      */
439     public final void setRunLocal(boolean theRunLocal)
440     {
441         this.runLocal = theRunLocal;
442     }
443 
444     /**
445      * Sets the Logs.
446      *
447      * @param theLogs
448      *            Different logs define
449      */
450     public final void setLogs(File theLogs)
451     {
452         this.logs = theLogs;
453     }
454     // Private Methods ---------------------------------------------------------
455 
456 
457     /**
458      * @param theTimeout the timeout after which we stop trying to call the test
459      *        URL.
460      */
461     public void setTimeout(long theTimeout)
462     {
463         this.timeout = theTimeout;
464     }
465     /**
466      * Set up the logs.
467      */
468     public void setupLogs()
469     {
470 
471         if (this.logs == null)
472         {
473             throw new BuildException("Missing 'logs' attribute");
474         }
475 
476         ResourceBundle bundle = null;
477         try
478         {
479             bundle = new PropertyResourceBundle(
480                 new FileInputStream(this.logs));
481         }
482         catch (IOException e)
483         {
484             throw new BuildException("Failed to load properties "
485                 + "file [" + this.logs + "]");
486         }
487         Enumeration keys = bundle.getKeys();
488         while (keys.hasMoreElements())
489         {
490             String key = (String) keys.nextElement();
491             Variable var = new Variable();
492             var.setKey(key);
493             var.setValue(bundle.getString(key));
494             super.addSysproperty(var);
495 
496         }
497 
498     }
499 }