diff --git a/utilities/testrunner/build.xml b/utilities/testrunner/build.xml
index e6f5f87c9..6025d1a74 100644
--- a/utilities/testrunner/build.xml
+++ b/utilities/testrunner/build.xml
@@ -60,6 +60,9 @@ run - Run the tester.
deprecation="true"
optimize="true"
source="1.6">
+
+
+
@@ -117,9 +120,12 @@ run - Run the tester.
fork="yes"
dir="${acceptance.dir}"
failonerror="true">
-
-
-
+
+
+
+
+
+
diff --git a/utilities/testrunner/lib/commons-codec-1.3.jar b/utilities/testrunner/lib/commons-codec-1.3.jar
new file mode 100644
index 000000000..957b6752a
Binary files /dev/null and b/utilities/testrunner/lib/commons-codec-1.3.jar differ
diff --git a/utilities/testrunner/lib/commons-httpclient-3.1.jar b/utilities/testrunner/lib/commons-httpclient-3.1.jar
new file mode 100644
index 000000000..7c59774ae
Binary files /dev/null and b/utilities/testrunner/lib/commons-httpclient-3.1.jar differ
diff --git a/utilities/testrunner/lib/commons-logging-1.1.1.jar b/utilities/testrunner/lib/commons-logging-1.1.1.jar
new file mode 100644
index 000000000..8758a96b7
Binary files /dev/null and b/utilities/testrunner/lib/commons-logging-1.1.1.jar differ
diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/FileHelper.java b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/FileHelper.java
index 38b4deba6..db11deb6b 100644
--- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/FileHelper.java
+++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/FileHelper.java
@@ -2,6 +2,7 @@
package edu.cornell.mannlib.vitro.utilities.testrunner;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
@@ -9,6 +10,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.Reader;
/**
* Some utility methods for dealing with files and directories.
@@ -137,6 +139,26 @@ public class FileHelper {
}
}
+ /**
+ * Suck all the data from a {@link Reader} into a {@link String}.
+ */
+ public static String readAll(Reader reader) throws IOException {
+ StringBuilder result = new StringBuilder();
+ BufferedReader buffered = new BufferedReader(reader);
+ char[] chunk = new char[4096];
+ int howMany;
+
+ try {
+ while (-1 != (howMany = buffered.read(chunk))) {
+ result.append(chunk, 0, howMany);
+ }
+ } finally {
+ reader.close();
+ }
+
+ return result.toString();
+ }
+
/** No need to instantiate this, since all methods are static. */
private FileHelper() {
// Nothing to initialize.
diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/ModelCleaner.java b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/ModelCleaner.java
index cc6d4bfc9..9a698431f 100644
--- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/ModelCleaner.java
+++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/ModelCleaner.java
@@ -29,13 +29,6 @@ public class ModelCleaner {
this.tomcatController = tomcatController;
sanityCheck();
- try {
- tomcatController.stopTheWebapp();
- tomcatController.startTheWebapp();
- } catch (CommandRunnerException e) {
- throw new FatalException(
- "sanityCheck: Failed to stop and start Tomcat.", e);
- }
}
private void sanityCheck() {
diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/ModelCleanerProperties.java b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/ModelCleanerProperties.java
index 285b4fd5d..c612e4fe1 100644
--- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/ModelCleanerProperties.java
+++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/ModelCleanerProperties.java
@@ -10,19 +10,17 @@ import java.util.Properties;
* model.
*/
public class ModelCleanerProperties {
- public static final String PROP_TOMCAT_START_COMMAND = "tomcat_start_command";
- public static final String PROP_TOMCAT_START_DELAY = "tomcat_start_delay";
- public static final String PROP_TOMCAT_STOP_COMMAND = "tomcat_stop_command";
- public static final String PROP_TOMCAT_STOP_DELAY = "tomcat_stop_delay";
+ public static final String PROP_VIVO_WEBAPP_NAME = "vivo_webapp_name";
+ public static final String PROP_TOMCAT_MANAGER_USERNAME = "tomcat_manager_username";
+ public static final String PROP_TOMCAT_MANAGER_PASSWORD = "tomcat_manager_password";
public static final String PROP_MYSQL_USERNAME = "mysql_username";
public static final String PROP_MYSQL_PASSWORD = "mysql_password";
public static final String PROP_MYSQL_DB_NAME = "mysql_db_name";
public static final String PROP_WEBAPP_DIRECTORY = "vivo_webapp_directory";
- private final String tomcatStartCommand;
- private final int tomcatStartDelay;
- private final String tomcatStopCommand;
- private final int tomcatStopDelay;
+ private final String vivoWebappName;
+ private final String tomcatManagerUsername;
+ private final String tomcatManagerPassword;
private final String mysqlUsername;
private final String mysqlPassword;
private final String mysqlDbName;
@@ -33,15 +31,11 @@ public class ModelCleanerProperties {
* reasonable.
*/
public ModelCleanerProperties(Properties props) {
- this.tomcatStartCommand = getRequiredProperty(props,
- PROP_TOMCAT_START_COMMAND);
- this.tomcatStartDelay = getRequiredIntegerProperty(props,
- PROP_TOMCAT_START_DELAY);
-
- this.tomcatStopCommand = getRequiredProperty(props,
- PROP_TOMCAT_STOP_COMMAND);
- this.tomcatStopDelay = getRequiredIntegerProperty(props,
- PROP_TOMCAT_STOP_DELAY);
+ this.vivoWebappName = checkWebappName(props);
+ this.tomcatManagerUsername = getRequiredProperty(props,
+ PROP_TOMCAT_MANAGER_USERNAME);
+ this.tomcatManagerPassword = getRequiredProperty(props,
+ PROP_TOMCAT_MANAGER_PASSWORD);
this.mysqlUsername = getRequiredProperty(props, PROP_MYSQL_USERNAME);
this.mysqlPassword = getRequiredProperty(props, PROP_MYSQL_PASSWORD);
@@ -50,22 +44,6 @@ public class ModelCleanerProperties {
this.webappDirectory = confirmWebappDirectory(props);
}
- public String getTomcatStartCommand() {
- return tomcatStartCommand;
- }
-
- public int getTomcatStartDelay() {
- return tomcatStartDelay;
- }
-
- public String getTomcatStopCommand() {
- return tomcatStopCommand;
- }
-
- public int getTomcatStopDelay() {
- return tomcatStopDelay;
- }
-
public String getMysqlUsername() {
return mysqlUsername;
}
@@ -82,6 +60,18 @@ public class ModelCleanerProperties {
return webappDirectory;
}
+ public String getVivoWebappName() {
+ return vivoWebappName;
+ }
+
+ public String getTomcatManagerUsername() {
+ return tomcatManagerUsername;
+ }
+
+ public String getTomcatManagerPassword() {
+ return tomcatManagerPassword;
+ }
+
/**
* Get the value for this property. If there isn't one, or if it's empty,
* complain.
@@ -95,14 +85,19 @@ public class ModelCleanerProperties {
return value;
}
- private int getRequiredIntegerProperty(Properties props, String key) {
- String value = getRequiredProperty(props, key);
- try {
- return Integer.parseInt(value.trim());
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("Property value for '" + key
- + "' is not a valid integer: " + value);
+ /**
+ * The website URL must end with the webapp name.
+ */
+ private String checkWebappName(Properties props) {
+ String websiteUrl = getRequiredProperty(props,
+ SeleniumRunnerParameters.PROP_WEBSITE_URL);
+ String webappName = getRequiredProperty(props, PROP_VIVO_WEBAPP_NAME);
+ if (!websiteUrl.endsWith(webappName)) {
+ throw new IllegalArgumentException("The " + PROP_VIVO_WEBAPP_NAME
+ + " must be the last item in the "
+ + SeleniumRunnerParameters.PROP_WEBSITE_URL);
}
+ return webappName;
}
/**
@@ -130,10 +125,9 @@ public class ModelCleanerProperties {
}
public String toString() {
- return "\n tomcatStartCommand: " + tomcatStartCommand
- + "\n tomcatStartDelay: " + tomcatStartDelay
- + "\n tomcatStopCommand: " + tomcatStopCommand
- + "\n tomcatStopDelay: " + tomcatStopDelay
+ return "\n vivoWebappName: " + vivoWebappName
+ + "\n tomcatManagerUsername: " + tomcatManagerUsername
+ + "\n tomcatManagerPassword: " + tomcatManagerPassword
+ "\n mysqlUsername: " + mysqlUsername
+ "\n mysqlPassword: " + mysqlPassword
+ "\n mysqlDbName: " + mysqlDbName
diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/TomcatController.java b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/TomcatController.java
index 2d89b6ea9..fbb06f9ef 100644
--- a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/TomcatController.java
+++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/TomcatController.java
@@ -2,140 +2,177 @@
package edu.cornell.mannlib.vitro.utilities.testrunner;
-import java.util.ArrayList;
-import java.util.List;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
+import edu.cornell.mannlib.vitro.utilities.testrunner.tomcat.HttpHelper;
/**
* Start and stop the webapp, so we can clean the database.
*/
public class TomcatController {
+ private static final Pattern PATTERN_WEBAPP_LISTING = Pattern
+ .compile("/(\\w+):(\\w+):");
+
private final SeleniumRunnerParameters parms;
private final ModelCleanerProperties properties;
private final Listener listener;
+ private final String tomcatBaseUrl;
+ private final String tomcatManagerUrl;
+ private final String tomcatManagerUsername;
+ private final String tomcatManagerPassword;
+ private final String webappName;
+
public TomcatController(SeleniumRunnerParameters parms) {
this.parms = parms;
this.properties = parms.getModelCleanerProperties();
this.listener = parms.getListener();
+
+ this.webappName = properties.getVivoWebappName();
+ this.tomcatBaseUrl = figureBaseUrl();
+ this.tomcatManagerUrl = this.tomcatBaseUrl + "/manager";
+ this.tomcatManagerUsername = properties.getTomcatManagerUsername();
+ this.tomcatManagerPassword = properties.getTomcatManagerPassword();
+
+ checkThatTomcatIsReady();
+ }
+
+ private String figureBaseUrl() {
+ String url = parms.getWebsiteUrl();
+ return url.substring(0, url.length() - webappName.length());
}
/**
- * Stop Tomcat and wait the prescribed number of seconds for it to clean up.
+ * Insure that Tomcat is running and has the ability to start and stop VIVO.
*/
- public void stopTheWebapp() throws CommandRunnerException {
- String tomcatStopCommand = properties.getTomcatStopCommand();
- int tomcatStopDelay = properties.getTomcatStopDelay();
+ private void checkThatTomcatIsReady() {
+ HttpHelper hh = new HttpHelper();
- CommandRunner runner = new CommandRunner(parms);
-
- listener.webappStopping(tomcatStopCommand);
- runner.run(parseCommandLine(tomcatStopCommand));
-
- int returnCode = runner.getReturnCode();
- if (returnCode != 0) {
- listener.webappStopFailed(returnCode);
- // Throw no exception - this can happen if Tomcat isn't running.
+ // Is Tomcat responding?
+ if (!hh.getPage(tomcatBaseUrl)) {
+ throw newHttpException(hh, "Tomcat does not respond");
}
- listener.webappWaitingForStop(tomcatStopDelay);
- try {
- Thread.sleep(tomcatStopDelay * 1000L);
- } catch (InterruptedException e) {
- // Just continue.
+ // Does the manager respond?
+ hh.getPage(tomcatManagerUrl + "/list");
+ if (hh.getStatus() == 404) {
+ throw newHttpException(hh,
+ "Tomcat manager application does not respond. "
+ + "Is it installed?");
}
- listener.webappStopped();
+ // Do we have the correct authorization for the manager?
+ hh.getPage(tomcatManagerUrl + "/list", tomcatManagerUsername,
+ tomcatManagerPassword);
+ if (hh.getStatus() != 200) {
+ throw newHttpException(hh, "Failed to list Tomcat applications");
+ }
+
+ // Is the VIVO application running?
+ boolean running = isVivoRunning(hh.getResponseText());
+
+ if (running) {
+ stopTheWebapp();
+ }
+
+ // Be sure that we can start it.
+ startTheWebapp();
}
/**
- * Start Tomcat and wait for it to initialize.
+ * Tell Tomcat to start the webapp. Check the response.
*/
public void startTheWebapp() {
- String tomcatStartCommand = properties.getTomcatStartCommand();
- int tomcatStartDelay = properties.getTomcatStartDelay();
+ String startCommand = tomcatManagerUrl + "/start?path=/" + webappName;
+ listener.webappStarting(startCommand);
- CommandRunner runner = new CommandRunner(parms);
+ HttpHelper hh = new HttpHelper();
+ hh.getPage(startCommand, tomcatManagerUsername, tomcatManagerPassword);
- listener.webappStarting(tomcatStartCommand);
- try {
- runner.runAsBackground(parseCommandLine(tomcatStartCommand));
- } catch (CommandRunnerException e) {
- throw new FatalException(e);
- }
-
- // Can't check the return code because the process shouldn't end.
-
- listener.webappWaitingForStart(tomcatStartDelay);
- try {
- Thread.sleep(tomcatStartDelay * 1000L);
- } catch (InterruptedException e) {
- // Just continue.
+ if ((hh.getStatus() != 200) || (!hh.getResponseText().startsWith("OK"))) {
+ listener.webappStartFailed(hh.getStatus());
+ throw newHttpException(hh, "Failed to start the webapp '"
+ + webappName + "'");
}
listener.webappStarted();
}
/**
- * A command line must be broken into separate arguments, where arguments
- * are delimited by blanks unless the blank (and the argument) is enclosed
- * in quotes.
+ * Tell Tomcat to stop the webapp. Check the response.
*/
- static List parseCommandLine(String commandLine) {
- List pieces = new ArrayList();
- StringBuilder piece = null;
- boolean inDelimiter = true;
- boolean inQuotes = false;
- for (int i = 0; i < commandLine.length(); i++) {
- char thisChar = commandLine.charAt(i);
- if ((thisChar == ' ') && !inQuotes) {
- if (inDelimiter) {
- // No effect.
- } else {
- inDelimiter = true;
- pieces.add(piece.toString());
+ public void stopTheWebapp() {
+ String stopCommand = tomcatManagerUrl + "/stop?path=/" + webappName;
+ listener.webappStopping(stopCommand);
+
+ HttpHelper hh = new HttpHelper();
+ hh.getPage(stopCommand, tomcatManagerUsername, tomcatManagerPassword);
+
+ if ((hh.getStatus() != 200) || (!hh.getResponseText().startsWith("OK"))) {
+ listener.webappStopFailed(hh.getStatus());
+ throw newHttpException(hh, "Failed to stop the webapp '"
+ + webappName + "'");
+ }
+
+ listener.webappStopped();
+ }
+
+ /**
+ * Is the VIVO application listed, and is it running?
+ */
+ private boolean isVivoRunning(String responseText) {
+ boolean found = false;
+ boolean running = false;
+
+ BufferedReader r = new BufferedReader(new StringReader(responseText));
+ try {
+ String line;
+ while (null != (line = r.readLine())) {
+ Matcher m = PATTERN_WEBAPP_LISTING.matcher(line);
+ if (m.find()) {
+ if (this.webappName.equals(m.group(1))) {
+ found = true;
+ if ("running".equals(m.group(2))) {
+ running = true;
+ }
+ break;
+ }
}
- } else if (thisChar == '"') {
- // Quotes are not carried into the parsed strings.
- inQuotes = !inQuotes;
- } else { // Not a blank or a quote.
- if (inDelimiter) {
- inDelimiter = false;
- piece = new StringBuilder();
- }
- piece.append(thisChar);
}
+ r.close();
+ } catch (IOException e) {
+ // Can't happen when reading from a string.
+ e.printStackTrace();
}
- // There is an implied delimiter at the end of the command line.
- if (!inDelimiter) {
- pieces.add(piece.toString());
+ if (!found) {
+ throw new FatalException("Webapp '" + this.webappName
+ + "' not found in Tomcat's list of webapps: \n"
+ + responseText);
}
- // Quotes must appear in pairs
- if (inQuotes) {
- throw new IllegalArgumentException(
- "Command line contains mismatched quotes: " + commandLine);
- }
+ return running;
+ }
- return pieces;
+ /**
+ * Generate a {@link FatalException} that contains a bunch of info from the
+ * {@link HttpHelper}.
+ */
+ private FatalException newHttpException(HttpHelper hh, String text) {
+ return new FatalException(text + " status is " + hh.getStatus()
+ + ", response text is '" + hh.getResponseText() + "'");
}
/**
* The run is finished. Do we need to do anything?
*/
public void cleanup() {
- // If we've been starting and stopping Tomcat,
- // stop it one more time.
- if (parms.isCleanModel()) {
- try {
- stopTheWebapp();
- } catch (CommandRunnerException e) {
- throw new FatalException(e);
- }
- }
-
+ // Leave the webapp running.
}
}
diff --git a/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/tomcat/HttpHelper.java b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/tomcat/HttpHelper.java
new file mode 100644
index 000000000..3ecbd5613
--- /dev/null
+++ b/utilities/testrunner/src/edu/cornell/mannlib/vitro/utilities/testrunner/tomcat/HttpHelper.java
@@ -0,0 +1,141 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.utilities.testrunner.tomcat;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.UsernamePasswordCredentials;
+import org.apache.commons.httpclient.auth.AuthScope;
+import org.apache.commons.httpclient.auth.BasicScheme;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
+import org.apache.commons.httpclient.methods.GetMethod;
+
+import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper;
+
+/**
+ * TODO
+ */
+public class HttpHelper {
+ private String responseText;
+ private int status = -1;
+ private Throwable exception;
+
+ /**
+ * Read the page at the specified URL, and set the response text and status.
+ */
+ public boolean getPage(String url) {
+ this.responseText = null;
+ this.status = -1;
+ this.exception = null;
+
+ HttpClient httpClient = new HttpClient();
+ HttpMethod method = new GetMethod(url);
+ Reader reader = null;
+ try {
+ method.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
+ method.getParams().setSoTimeout(60000);
+
+ httpClient.executeMethod(method);
+
+ this.status = method.getStatusCode();
+
+ reader = new InputStreamReader(method.getResponseBodyAsStream(),
+ Charset.forName("UTF-8"));
+ this.responseText = FileHelper.readAll(reader);
+
+ return true;
+ } catch (IOException e) {
+ this.exception = e;
+ return false;
+ } finally {
+ method.releaseConnection();
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * Read the page at the specified URL, performing authentication with the
+ * specified username and password, and set the response text and status.
+ */
+ public boolean getPage(String url, String username, String password) {
+ responseText = null;
+ status = -1;
+ this.exception = null;
+
+ HttpClient httpClient = new HttpClient();
+ httpClient.getState().setCredentials(new AuthScope(null, -1, null, "basic"),
+ new UsernamePasswordCredentials(username, password));
+
+ HttpMethod method = new GetMethod(url);
+
+ Reader reader = null;
+ try {
+ method.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
+ method.getParams().setSoTimeout(60000);
+
+ httpClient.executeMethod(method);
+
+ this.status = method.getStatusCode();
+
+ reader = new InputStreamReader(method.getResponseBodyAsStream(),
+ Charset.forName("UTF-8"));
+ responseText = FileHelper.readAll(reader);
+
+ return true;
+ } catch (IOException e) {
+ this.exception = e;
+ return false;
+ } finally {
+ method.releaseConnection();
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public String getResponseText() {
+ return responseText;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public static class HttpHelperException extends Exception {
+
+ public HttpHelperException() {
+ }
+
+ public HttpHelperException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public HttpHelperException(String message) {
+ super(message);
+ }
+
+ public HttpHelperException(Throwable cause) {
+ super(cause);
+ }
+
+ }
+}
diff --git a/utilities/testrunner/test/edu/cornell/mannlib/vitro/utilities/testrunner/TomcatControllerTest.java b/utilities/testrunner/test/edu/cornell/mannlib/vitro/utilities/testrunner/TomcatControllerTest.java
deleted file mode 100644
index 8db9b5b9b..000000000
--- a/utilities/testrunner/test/edu/cornell/mannlib/vitro/utilities/testrunner/TomcatControllerTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/* $This file is distributed under the terms of the license in /doc/license.txt$ */
-
-package edu.cornell.mannlib.vitro.utilities.testrunner;
-
-import static org.junit.Assert.*;
-
-import java.util.Arrays;
-
-import org.junit.Test;
-
-/**
- * TODO
- */
-public class TomcatControllerTest {
-
- // ----------------------------------------------------------------------
- // Tests for parseCommandLine()
- // ----------------------------------------------------------------------
-
- @Test
- public void oneArgument() {
- assertExpectedParsing("oneArgument", "oneArgument");
- }
-
- @Test
- public void multipleArguments() {
- assertExpectedParsing("more than one", "more", "than", "one");
- }
-
- @Test
- public void quotedArgument() {
- assertExpectedParsing("contains \"quoted blank\" string", "contains",
- "quoted blank", "string");
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void mismatchedQuotes() {
- assertExpectedParsing("contains mismatched \"quote");
- }
-
- @Test
- public void emptyLine() {
- assertExpectedParsing("");
- }
-
- private void assertExpectedParsing(String commandLine,
- String... expectedPieces) {
- assertEquals("parse", Arrays.asList(expectedPieces),
- TomcatController.parseCommandLine(commandLine));
- }
-}