/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.test.scripts.test; import java.lang.InterruptedException ; import java.io.OutputStream ; import java.io.ByteArrayOutputStream ; import java.io.BufferedReader ; import java.io.InputStreamReader ; import java.io.PrintWriter ; import java.io.StringWriter ; import java.io.FileReader ; import java.io.FileWriter ; import java.io.File ; import java.io.FileInputStream ; import java.io.FileOutputStream ; import java.io.FileNotFoundException ; import java.io.IOException ; import java.net.URL ; import java.net.MalformedURLException ; import java.net.URLConnection ; import java.net.HttpURLConnection ; import org.jboss.test.scripts.support.LogFileAssertionChecker ; import org.jboss.test.scripts.support.ShellScriptExecutor ; import org.jboss.test.scripts.support.AsyncShellScriptExecutor ; import org.jboss.test.JBossTestCase ; import junit.framework.Assert ; /** * Base class to test command-line scripts. * * @author Richard Achmatowicz * @version $Revision: 1.0 */ public abstract class ScriptsTestBase extends JBossTestCase { public static final String SERVER_STARTED_MESSAGE = "Started in" ; public static final String SERVER_STOPPED_MESSAGE = "Shutdown complete" ; public static final String SERVER_HALT_MESSAGE = "Server halt" ; public static final String SERVER_EXIT_MESSAGE = "Server exit" ; public static final String SERVER_HALTED_MESSAGE = "halting the JVM now" ; ShellScriptExecutor se = null ; AsyncShellScriptExecutor ase = null ; LogFileAssertionChecker errorLogChecker = null ; LogFileAssertionChecker outputLogChecker = null ; LogFileAssertionChecker bootLogChecker = null ; LogFileAssertionChecker systemLogChecker = null ; public ScriptsTestBase(String name) { super(name); // initialise the script executors se = new ShellScriptExecutor() ; ase = new AsyncShellScriptExecutor() ; // initialise the log checkers (error checking here could be improved) // these will be initialised to a single server instance, so we canb't change server instances // part way through a test suite! :-( String logDir = getLogDir() ; errorLogChecker = new LogFileAssertionChecker(logDir + "/error.log") ; outputLogChecker = new LogFileAssertionChecker(logDir + "/output.log") ; bootLogChecker = new LogFileAssertionChecker(logDir + "/boot.log") ; systemLogChecker = new LogFileAssertionChecker(logDir + "/system.log") ; } protected void setUp() throws Exception { super.setUp(); // setup AsyncShellScriptExecutor to deploy shutdown.sar ase.setUseShutdown(true) ; ase.setPathToShutdownSar(getDeployURL("shutdown.sar").getPath()) ; ase.setDeployDir(getDeployDir()) ; } protected void tearDown() throws Exception { super.tearDown(); } public ShellScriptExecutor getShellScriptExecutor() { return se ; } public AsyncShellScriptExecutor getAsyncShellScriptExecutor() { return ase ; } public LogFileAssertionChecker getOutputLogChecker() { return outputLogChecker ; } public LogFileAssertionChecker getErrorLogChecker() { return errorLogChecker ; } public LogFileAssertionChecker getBootLogChecker() { return bootLogChecker ; } public LogFileAssertionChecker getSystemLogChecker() { return systemLogChecker ; } /* location helpers * * these really need to be improved to use JBossTestCase, but unfortunately * JBossTestCase does not give us direct access to the deploy directory * Here, i'm mimicking what they do */ public String getDistDir() { String distDir = System.getProperty("jboss.dist") ; if (distDir == null) fail("Can't get the JBoss distribution directory") ; return distDir ; } /* * how to get the server directory? */ public String getServerConfig() { String serverConfig = System.getProperty("jbosstest.server.config"); if (serverConfig == null) { serverConfig = "default"; } return serverConfig ; } public String getBinDir() { return getDistDir() + "/bin" ; } public String getLogDir() { return getDistDir() + "/server/" + getServerConfig() + "/log" ; } public String getDeployDir() { return getDistDir() + "/server/" + getServerConfig() + "/deploy" ; } public boolean isWindows() { String osName = System.getProperty("os.name") ; if (osName == null) fail("Can't get the operating system name") ; return (osName.indexOf("Windows") > -1) || (osName.indexOf("windows") > -1) ; } /* * method for constructing command lines for the shell executor, which * in general takes three arguments * String[] command, String[] envp, File workingDirectory * * This command creates the first argument. * * Windows: three tokens need to be passed in String[] command * cmd /c <entire command string to execute> * cmd /c "twiddle.bat -s jnp://192.168.0.100 jsr77" * * UNIX: three tokens need to be passed in String[] command * bash -c <entire command string to execute> * bash -c "./twiddle.sh -s jnp://192.168.0.100 jsr77" * * This arrangement may not be optimal ... */ public String[] getShellCommand(String commandName, String options, String args) { String[] shellCommand = new String[3] ; String commandLine = null ; if (commandName == null) fail("No command name specified for shell to execute") ; // set up the base command (platform specific) if (isWindows()) { shellCommand[0] = "cmd" ; shellCommand[1] = "/c" ; commandLine = commandName + ".bat" ; } else { shellCommand[0] = "bash" ; shellCommand[1] = "-c" ; commandLine = "./" + commandName + ".sh" ; } // add in the rest (platform-independent) if (options != null) commandLine += " " + options ; if (args != null) commandLine += " " + args ; shellCommand[2] = commandLine ; return shellCommand ; } /* assertion helpers */ public void assertOnOutputLog(String string, String failureMessage, boolean useCheckpoint, boolean resetCheckpoint) { if (!outputLogChecker.isStringInLog(string, useCheckpoint, resetCheckpoint)) { // assertion does not hold Assert.fail(failureMessage) ; } } public void assertOnErrorLog(String string, String failureMessage, boolean useCheckpoint, boolean resetCheckpoint) { if (!errorLogChecker.isStringInLog(string, useCheckpoint, resetCheckpoint)) { // assertion does not hold Assert.fail(failureMessage) ; } } public void assertOnBootLog(String string, String failureMessage, boolean useCheckpoint, boolean resetCheckpoint) { if (!bootLogChecker.isStringInLog(string, useCheckpoint, resetCheckpoint)) { // assertion does not hold Assert.fail(failureMessage) ; } } public void assertOnSystemLog(String string, String failureMessage, boolean useCheckpoint, boolean resetCheckpoint) { if (!systemLogChecker.isStringInLog(string, useCheckpoint, resetCheckpoint)) { // assertion does not hold Assert.fail(failureMessage) ; } } /* check if there is a Tomcat connection to the server */ private static boolean isServerStarted(String host) throws MalformedURLException { // URL to Tomcat URL url = new URL("http", host, 8080, "/") ; try { URLConnection conn = url.openConnection() ; if (conn instanceof HttpURLConnection) { HttpURLConnection http = (HttpURLConnection) conn ; int responseCode = http.getResponseCode(); if (responseCode > 0 && responseCode < 400) { return true ; } } } catch(IOException e) { return false ; } return false ; } /* * Wait for the server (started by the AsyncShellExecutor) to start * * @throws Exception if server does not start successfully * * A successful server start means: * (i) we can reach Tomcat * (ii) no exceptions in error log * (iii) no ERROR statements in the server log * If the server does not start successfully, we call joinShellCommand with a 1 second timout. * This will wait for one second and then attempt to shutdown both the server and the bash shell, * before throwing an exception. * * NOTE: this is a hack, and assumes the process has been started using the AsynchShellExecutor */ protected static void waitForServerStart(AsyncShellScriptExecutor ase, String host, int timeout) throws IOException { boolean serverStarted = false ; boolean logsExceptionFree = true ; int tries = 0 ; while (tries++ < timeout) { if (!ase.isRunning()) { throw new IOException("Server failed to start. See logs.") ; } // wait for a sec sleepForSeconds(1) ; if (isServerStarted(host)) { serverStarted = true ; break ; } } // problem here is that if another server is started: // (i) the preceeding code will indicate serverStarted=true // (iI) the following code will get executed before the starting server has had a chance to start // (and before exceptions are written). // This results in two servers being started, one incompletely. // So wait here for 5 seconds to allow the logs to accumulate exceptions. sleepForSeconds(5) ; // check for startup errors in the server logs. We check both because: // (i) log4j will write all CONSOLE appender logging to System.in only, and these // may contain ERROR log entries corresponding to exceptions // (ii) other exceptions in the AS should get written to System.err if not handled // via log4j if (ase.getOutput().indexOf("ERROR") > -1 || ase.getError().indexOf("Exception") > -1) { logsExceptionFree = false ; } // debugging //System.out.println("Server started = " + serverStarted) ; //System.out.println("logsExceptionFree = " + logsExceptionFree) ; //System.out.println("output = " + ase.getOutput()) ; //System.out.println("error = " + ase.getError()) ; // kill the server before going on if not correctly started if (!serverStarted || !logsExceptionFree) { // kill process and close streams ase.joinShellCommand(1) ; // now throw exception if (!serverStarted) throw new IOException("Server failed to start: couldn't connect to Tomcat. See logs.") ; else if (!logsExceptionFree) { throw new IOException("Server failed to start: ERROR statements found. See logs.") ; } } // if we reach here, the server started and no ERRORs were found } /* Wait for the server (started by the AsyncShellExecutor) to stop * * @throws Exception if server does not stop successfully * * A successful server start means: * (i) we see the "VM halted" message in the server log * (ii) no ERROR statements in the server log * If the server does not stop successfully, we call joinShellCommand with a 1 second timout. * This will wait for one second and then attempt to shutdown both the server and the bash shell, * before throwing an exception. */ protected static void waitForServerStop(AsyncShellScriptExecutor ase, int timeout) throws IOException { boolean serverStopped = false ; boolean logsExceptionFree = true ; boolean haltCalled = false ; System.out.println("waitForServerStop: waiting " + timeout + " seconds") ; int tries = 0 ; while (tries++ < timeout) { if (!ase.isRunning()) { // FIX-ME throw new IOException("Server not running on shutdown. Something fishy") ; } // check if stopped by inspecting console log String currentOutput = ase.getOutput() ; if (currentOutput.indexOf(SERVER_STOPPED_MESSAGE) > -1 || currentOutput.indexOf(SERVER_HALTED_MESSAGE) > -1) { serverStopped = true ; } // wait for a sec sleepForSeconds(1) ; } // Wait here for 5 seconds to allow the logs to accumulate. sleepForSeconds(5) ; // check for shutdown errors in the server logs. We check both because: // (i) log4j will write all CONSOLE appender logging to System.in only, and these // may contain ERROR log entries corresponding to exceptions // (ii) other exceptions in the AS should get written to System.err if not handled // via log4j // NOTE: when we halt, an ERROR log message is written so we have to be careful // if (ase.getOutput().indexOf(SERVER_HALTED_MESSAGE) > -1) haltCalled = true ; if (!haltCalled && (ase.getOutput().indexOf("ERROR") > -1 || ase.getError().indexOf("Exception") > -1)) { logsExceptionFree = false ; } // kill the server before going on if not correctly started if (!serverStopped || !logsExceptionFree) { // kill process and close streams ase.joinShellCommand(1) ; // now throw exception if (!serverStopped) throw new IOException("Server failed to stop: didn't find message in logs. See logs.") ; else if (!logsExceptionFree) { throw new IOException("Server failed to stop: ERROR statements found. See logs.") ; } } // if we reach here, the server stopped and no ERRORs were found } private static void sleepForSeconds(int seconds) { try { Thread.sleep(seconds * 1000) ; } catch(InterruptedException e) { } } }