Coverage details for com.topcoder.testframework.web.DefaultWebApplicationServer

LineHitsSource
1 /*
2  * Copyright (C) 2006 TopCoder Inc., All Rights Reserved.
3  */
4 package com.topcoder.testframework.web;
5  
6 import com.topcoder.testframework.AntTaskInfo;
7 import com.topcoder.testframework.ApplicationServer;
8 import org.apache.cactus.integration.ant.container.Container;
9 import org.apache.cactus.integration.ant.container.ContainerFactory;
10 import org.apache.cactus.integration.ant.container.ContainerRunner;
11 import org.apache.cactus.integration.ant.deployment.DeployableFile;
12 import org.apache.cactus.integration.ant.deployment.EarParser;
13 import org.apache.cactus.integration.ant.deployment.WarParser;
14 import org.apache.cactus.integration.ant.util.DefaultAntTaskFactory;
15 import org.apache.tools.ant.BuildException;
16  
17 import java.io.File;
18 import java.lang.reflect.Method;
19 import java.net.MalformedURLException;
20 import java.net.URL;
21  
22  
23 /**
24  * This class is derived from {@link ApplicationServer} class. It represents web application server to be used while
25  * running tests. It uses Cactus as a back-end. It tries to support all the servers supported by Cactus. Name of
26  * corresponding XML element in the Ant build file should be equal to the Cactus container type representing the desired
27  * server.
28  * <p/>
29  * <b>Supported attributes:</b> <ul><li><tt>timeout</tt> - non-required, specifies the timeout to wait for successful
30  * server startup in milliseconds, default value is 180000 milliseconds</li> <li><tt>testurl</tt> - non-required,
31  * specifies a url to be used to test whether the server has started up and is available, if not specified will be
32  * calculated from the prepared deployable (war or ear) file and server startup parameters</li></ul>
33  * <p/>
34  * This class is mutable, i.e. not thread-safe.
35  *
36  * @author real_vg, TCSDEVELOPER
37  * @version 1.0
38  */
39 public class DefaultWebApplicationServer extends ApplicationServer {
40     /**
41      * This is the default timeout used when no timeout has been specified. It is 180000 ms, i.e. 3 minutes.
42      */
43     private static final int DEFAULT_TIMEOUT = 180000;
44  
45     /**
46      * This field represents the Cactus container representing the server to be used when running the tests. Is
47      * initialized in constructor, using {@link org.apache.cactus.integration.ant.container.ContainerFactory} to create
48      * an instance of the class.
49      */
50     private Container container;
51  
52     /**
53      * This field stores instance of Cactus ContainerRunner class to be used for starting/stopping the server
54      * represented by the container field.
55      */
56     private ContainerRunner runner;
57  
58     /**
59      * This field represents the <tt>timeout</tt> attribute, which specifies the amount of time to wait for server to be
60      * started (in milliseconds). The field is initialized to <tt>180000</tt> and set via the {@link #setTimeout(long)}
61      * method.
62      */
6368    private long timeout = DEFAULT_TIMEOUT;
64  
65     /**
66      * This field represents the <tt>testurl</tt> attribute, which specifies the URL used to check whether the server is
67      * running.
68      * <p/>
69      * This attribute is required only when neither <tt>warfile</tt> nor <tt>earfile</tt> attributes are specified. If
70      * this attribute is set and the server is detected to be running, the server will not be started by the framework.
71      * The field is initialized to <tt>null</tt> and set via the {@link #setTestURL(java.net.URL)} method.
72      */
7368    private URL testURL = null;
74  
75     /**
76      * Creates a DefaultWebApplicationServer instance. The type of server represented by the class is specified. Type is
77      * a string like <tt>"tomcat5x"</tt>, which actually specifies the name and version of the server.
78      *
79      * @param type the type of the server
80      *
81      * @throws BuildException if the type of the server is unrecognized, or any other error happens
82      * @throws IllegalArgumentException if type is <tt>null</tt>
83      */
84     public DefaultWebApplicationServer(final String type) {
85         //arg checking done in super method
8669        super(type);
8768        final ContainerFactory containerFactory = new ContainerFactory();
8868        container = containerFactory.createContainer(type);
8968        runner = new ContainerRunner(container);
9068    }
91  
92     /**
93      * This method starts the server. In the case when <tt>startserver</tt> attribute is set to <tt>false</tt>, the
94      * server should not be started, but some initialization may still be needed.
95      * <p/>
96      * In case there is a valid <tt>testurl</tt> attribute set on this instance, this will be used to test if the server
97      * is already running. If it is running , then it is not started and stopped by the framework.
98      *
99      * @throws BuildException if any error happens
100      */
101     public void startUp() {
1025        final File configuredWarFile = getWarFile();
1035        final File configuredEarFile = getEarFile();
104  
1055        if ((configuredWarFile != null) && (configuredEarFile != null)) {
1061            throw new BuildException("You must specify either the [warfile] or "
107                 + "the [earfile] attribute but not both");
108         }
109  
110         // Get DeployableFile instance
1114        DeployableFile deployableFile = null;
1124        if (configuredWarFile != null) {
1132            deployableFile = WarParser.parse(configuredWarFile);
1142        } else if (configuredEarFile != null) {
1150            deployableFile = EarParser.parse(configuredEarFile);
116         }
117  
118         // Retrieve Ant task information
1194        final AntTaskInfo taskInfo = getAntTaskInfo();
120         // Create and set ant task factory
1214        container.setAntTaskFactory(new DefaultAntTaskFactory(taskInfo.getProject(), taskInfo.getTaskName(),
122             taskInfo.getLocation(), taskInfo.getTarget()));
123  
124         // Set deployable file
1254        container.setDeployableFile(deployableFile);
126  
1276        callSetterByReflection(container, "setServer", String.class, getServer());
1284        if (getPort() > 0) {
1293            callSetterByReflection(container, "setPort", int.class, new Integer(getPort()));
130         }
1314        callSetterByReflection(container, "setDir", File.class, getDir());
132  
133         // Init the container
1344        container.init();
135         // Set URL to test connection with
1363        if (getTestURL() != null) {
1370            runner.setURL(getTestURL());
138             // according to https://software.topcoder.com/forum/c_forum_message.jsp?f=20015788&r=21551343
139             // we set the context url to be the testURL
1400            System.setProperty("cactus.contextURL", getTestURL().toExternalForm());
1413        } else if (deployableFile != null) {
1422            final String spec = container.getBaseURL() + "/"
143                 + deployableFile.getTestContext()
144                 + deployableFile.getServletRedirectorMapping()
145                 + "?Cactus_Service=RUN_TEST";
146             try {
1472                runner.setURL(new URL(spec));
1480            } catch (MalformedURLException e) {
1490                throw new BuildException("The URL spec [" + spec + "] could not be converted into a valid URL.", e);
1502            }
151             // sets the URL to be used by the test case client when
152             // connecting to the container to call the container-side test cases
153             // see https://software.topcoder.com/forum/c_forum_message.jsp?f=20015788&r=21553769
1542            System.setProperty("cactus.contextURL", container.getBaseURL() + "/" + deployableFile.getTestContext());
155         } else {
1561            throw new BuildException("Unable to start the container: Neither a valid 'testUrl' attribute nor "
157                 + "a deployable file ('warFile' or 'earFile') were specified.");
158         }
159  
160         // Set timeout
1612        runner.setTimeout(getTimeout());
162  
163         // Start the container (starts server if server wasn't already running)
1642        runner.startUpContainer();
1652    }
166  
167     /**
168      * This method stops the server, and possibly clean-ups the data. The server should not be stopped if it wasn't
169      * started by the {@link #startUp()} method.
170      *
171      * @throws BuildException if any error happens
172      */
173     public void shutDown() {
17423        runner.shutDownContainer();
1753    }
176  
177     /**
178      * This method initializes the server with data. In the case when <tt>initserver</tt> attribute is set to
179      * <tt>false</tt>, does nothing, or can check if the data are properly initialized, the implementation can choose
180      * the preferred way. Subclasses must implement this method to perform the actual task of initializing server with
181      * data.
182      *
183      * @throws BuildException if any error happens
184      */
185     public void initData() {
186         // This method is intentionally empty
1871    }
188  
189     /**
190      * This method cleans up the data initialized by the {@link #initData()} method. In the case when
191      * <tt>initserver</tt> attribute is set to <tt>false</tt> or data was not initialized, does nothing. Subclasses must
192      * implement this method to perform the actual task of cleaning-up the data.
193      *
194      * @throws BuildException if any error happens
195      */
196     public void cleanUpData() {
197         // This method is intentionally empty
1981    }
199  
200     /**
201      * This method sets the value of the <tt>timeout</tt> attribute, which specifies the amount of time to wait for
202      * server to be started (in milliseconds).
203      *
204      * @param timeout the value of the <tt>timeout</tt> attribute to be set, in milliseconds
205      *
206      * @throws IllegalArgumentException if timeout is negative
207      */
208     public void setTimeout(final long timeout) {
2092        if (timeout < 0) {
2101            throw new IllegalArgumentException(
211                 "The parameter named [timeout] was expected to be >=0 , but was [" + timeout + "].");
212         }
213  
2141        this.timeout = timeout;
2151    }
216  
217     /**
218      * This method returns the value of the <tt>timeout</tt> attribute, which specifies the amount of time to wait for
219      * server to be started (in milliseconds).
220      *
221      * @return the value of the <tt>timeout</tt> attribute, in milliseconds
222      */
223     public long getTimeout() {
2244        return timeout;
225     }
226  
227     /**
228      * This method sets the value of the <tt>testurl</tt> attribute, which specifies the URL used to check whether the
229      * server is running. If this attribute is set and the server is running, the server will not be started by the
230      * framework.
231      *
232      * @param testURL the value of the <tt>testurl</tt> attribute to be set
233      */
234     public void setTestURL(final URL testURL) {
2351        this.testURL = testURL;
2361    }
237  
238     /**
239      * This method returns the value of the <tt>testurl</tt> attribute, which specifies URL used to check if the server
240      * is running. If this attribute is set and the server is running, the server will not be started by the framework.
241      *
242      * @return the value of the <tt>testurl</tt> attribute
243      */
244     public URL getTestURL() {
2455        return testURL;
246     }
247  
248     /**
249      * This method is a utility for the reflective invocation of a setter method in a Container instance. It tries to
250      * find a method with the given name and argument type in the given instance and in case found, invokes that method
251      * using the given value as argument.
252      *
253      * @param instance the instance on which the setter method shall be called
254      * @param setterName the name of the setter method
255      * @param valueType the type of the argument in the setter method declaration
256      * @param value the value to be passed when invoking the setter method
257      *
258      * @throws IllegalArgumentException in case the given instance, setterName or valueType are <tt>null</tt>, or the
259      * given setterName is an empty(trim'd) String
260      * @throws BuildException in case the appropriate setter method was not found or threw an Throwable during
261      * invocation
262      */
263     private static void callSetterByReflection(final Object instance, final String setterName, final Class valueType,
264                                                final Object value) {
26511        if (instance == null) {
2660            throw new IllegalArgumentException("The parameter named [instance] was null.");
267         }
26811        if (setterName == null) {
2690            throw new IllegalArgumentException("The parameter named [setterName] was null.");
270         }
27111        if (setterName.trim().length() == 0) {
2720            throw new IllegalArgumentException("The parameter named [setterName] was an empty String.");
273         }
27411        if (valueType == null) {
2750            throw new IllegalArgumentException("The parameter named [valueType] was null.");
276         }
277  
27811        final Class instanceType = instance.getClass();
279  
280         final Method method;
281         try {
28211            method = instanceType.getMethod(setterName, new Class[]{valueType});
2830        } catch (NoSuchMethodException e) {
2840            throw new BuildException("The container [" + instance + "] of type [" + instanceType.getName()
285                 + "] did not have an appropriate setter method named [" + setterName + "] with argument of type ["
286                 + valueType.getName() + "].", e);
28711        }
288         try {
28911            method.invoke(instance, new Object[]{value});
2900        } catch (Throwable e) {
291             // catch of Throwable is normally discouraged by the TC coding style, but
292             // as a lot of Errors can arise from a reflection invocation and these
293             // errors don't need to be let pass through this catch clause (as they
294             // definitely belong to the reflective invocation and not the normal program
295             // execution) all Throwables are caught here and
296             // wrapped into a BuildException.
2970            throw new BuildException("The container [" + instance + "] of type [" + instanceType.getName()
298                 + "] did throw an exception upon invocation of the setter method named [" + setterName
299                 + "] with argument of type [" + valueType.getName() + "], using the argument [" + value
300                 + "] in the invocation.", e);
30111        }
30211    }
303 }

this report was generated by version 1.0.5 of jcoverage.
visit www.jcoverage.com for updates.

copyright © 2003, jcoverage ltd. all rights reserved.
Java is a trademark of Sun Microsystems, Inc. in the United States and other countries.