/* * Sun Public License * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is available at http://www.sun.com/ * * The Original Code is the SLAMD Distributed Load Generation Engine. * The Initial Developer of the Original Code is Neil A. Wilson. * Portions created by Neil A. Wilson are Copyright (C) 2004-2010. * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): Neil A. Wilson */ package com.slamd.tools; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import com.slamd.client.ClientMessageWriter; import com.slamd.client.ClientSideJob; import com.slamd.common.Constants; import com.slamd.parameter.BooleanParameter; import com.slamd.parameter.FileURLParameter; import com.slamd.parameter.MultiLineTextParameter; import com.slamd.parameter.Parameter; import com.slamd.parameter.ParameterList; import com.slamd.scripting.ScriptedJobClass; import com.slamd.scripting.engine.ScriptException; import com.slamd.scripting.engine.ScriptParser; import com.slamd.stat.StatTracker; /** * This class defines a specialized SLAMD client that may be used to execute * SLAMD scripts without the need for a SLAMD server to be running and without * the need for a configuration file as would be used by the standalone client. * * * @author Neil A. Wilson */ public class RunScript implements ClientMessageWriter { // Indicates whether the data collected by each thread should be aggregated // before reporting to the end user. private boolean aggregateThreadData; // Indicates whether the script should be executed in debug mode. private boolean debugMode; // Indicates whether the script file should only be validated but not actually // executed. private boolean validateOnly; // Indicates whether the script should be executed in verbose mode. private boolean verboseMode; // The length of time (in seconds) to use as the statistics collection // interval. private int collectionInterval; // The maximum length of time (in seconds) that the script should be allowed // to run. private int duration; // The number of threads to use when running the job. private int numThreads; // The scripted job class used to get parameter information. private ScriptedJobClass scriptedJob; // The path to the script file. private String scriptFile; // The end-of-line character for this platform. private String eol = Constants.EOL; // The set of arguments provided to the script. private String[] scriptArguments; /** * Create a new standalone client instance and pass all the arguments to it. * * @param args The set of arguments provided on the command line. */ public static void main(String[] args) { new RunScript(args); } /** * Create a new standalone client instance, process the configuration, and * run the specified job. * * @param args The set of arguments provided on the command line. */ public RunScript(String[] args) { // Set default values for all the parameters. aggregateThreadData = false; debugMode = false; validateOnly = false; scriptFile = null; duration = 0; collectionInterval = Constants.DEFAULT_COLLECTION_INTERVAL; numThreads = 1; // Create an array list to hold the script arguments. ArrayList<String> argList = new ArrayList<String>(); // Parse the command-line arguments for (int i=0; i < args.length; i++) { if (args[i].equals("-d")) { try { duration = Integer.parseInt(args[++i]); } catch (NumberFormatException nfe) { System.err.println("ERROR: Duration must be an integer"); displayUsage(); System.exit(1); } } else if (args[i].equals("-i")) { try { collectionInterval = Integer.parseInt(args[++i]); } catch (NumberFormatException nfe) { System.err.println("ERROR: Statistics collection interval must be " + "an integer"); displayUsage(); System.exit(1); } } else if (args[i].equals("-t")) { try { numThreads = Integer.parseInt(args[++i]); } catch (NumberFormatException nfe) { System.err.println("ERROR: Number of threads must be an integer"); displayUsage(); System.exit(1); } } else if (args[i].equals("-a")) { aggregateThreadData = true; } else if (args[i].equals("-D")) { debugMode = true; verboseMode = true; } else if (args[i].equals("-v")) { verboseMode = true; } else if (args[i].equals("-V")) { validateOnly = true; } else if (args[i].equals("-h")) { displayUsage(); System.exit(0); } else if (args[i].startsWith("-")) { System.err.println("ERROR: Unrecognized parameter " + args[i]); displayUsage(); System.exit(1); } else { if (scriptFile == null) { scriptFile = args[i]; } else { argList.add(args[i]); } } } // Make sure that the required parameters were given values if (scriptFile == null) { System.err.println("ERROR: No script file specified"); displayUsage(); System.exit(1); } // Convert the argument list to an array. scriptArguments = new String[argList.size()]; argList.toArray(scriptArguments); // Make sure that the script file given is an absolute path. File scriptFileObject = new File(scriptFile); URL scriptFileURL = null; if (! (scriptFileObject.exists() && scriptFileObject.canRead())) { System.err.println("Script file \"" + scriptFile + "\" does not exist or is not readable."); System.exit(1); } else { try { scriptFileURL = new URL("file:" + scriptFileObject.getAbsolutePath()); } catch (Exception e) { System.err.println("Unable to create script file URL: " + e); if (debugMode) { e.printStackTrace(); } } } // Read and parse the script file. ScriptParser scriptParser = null; try { scriptParser = new ScriptParser(); } catch (ScriptException se) { System.err.println("ERROR: Unable to instantiate the script parser -- " + se); if (debugMode) { se.printStackTrace(); } System.exit(1); } try { scriptParser.read(scriptFile); } catch (IOException ioe) { System.err.println("ERROR: Unable to read script file \"" + scriptFile + "\" -- " + ioe); if (debugMode) { ioe.printStackTrace(); } System.exit(1); } try { scriptParser.parse(); } catch (ScriptException se) { System.err.println("ERROR: Unable to parse script file \"" + scriptFile + "\" -- " + se.getMessage()); if (debugMode) { se.printStackTrace(); } System.exit(1); } if (validateOnly) { System.out.println("No errors found in script file \"" + scriptFile + '"'); System.exit(0); } // Load and verify the job class. scriptedJob = new ScriptedJobClass(); // Convert the command-line arguments to a set of parameters for the job. BooleanParameter debugModeParameter = new BooleanParameter(ScriptedJobClass.DEBUG_PARAMETER_NAME, debugMode); FileURLParameter scriptURLParameter = new FileURLParameter(ScriptedJobClass.SCRIPT_FILE_PARAMETER_NAME, null, scriptFileURL); MultiLineTextParameter scriptArgsParameter = new MultiLineTextParameter( ScriptedJobClass.SCRIPT_ARGUMENTS_PARAMETER_NAME, scriptArguments); Parameter[] jobParams = new Parameter[] { debugModeParameter, scriptURLParameter, scriptArgsParameter }; ParameterList parameters = new ParameterList(jobParams); // Create a new client side job to actually do the processing. ClientSideJob clientJob = new ClientSideJob(this, null, scriptedJob.getClass().getName(), numThreads, duration, collectionInterval, parameters, false, false, null); // Start the job and wait for it to complete. writeMessage("Starting script execution...."); clientJob.startAndWait(); // Print out information about the job. writeMessage("Job Processing Complete"); int jobDuration = clientJob.getActualDuration(); writeMessage("Job Processing Time: " + jobDuration + " seconds"); StatTracker[] statTrackers = clientJob.getStatTrackers(aggregateThreadData); for (int i=0; i < statTrackers.length; i++) { System.out.println(); System.out.println(statTrackers[i].getDisplayName() + " -- Thread " + statTrackers[i].getThreadID()); if (debugMode) { System.out.println(statTrackers[i].getDetailString()); } else { System.out.println(statTrackers[i].getSummaryString()); } } } /** * Writes usage information for this program to standard error. */ public void displayUsage() { System.err.println( "Usage: java RunScript [options] scriptFile scriptArgs" + eol + " where [options] include:" + eol + "-d {value} -- Specifies the maximum length of time the job should run" + eol + "-i {value} -- Specifies the length of time in seconds that should be" + eol + " used as the statistics collection interval" + eol + "-t {value} -- Specifies the number of threads that should be used" + eol + "-a -- Specifies that data from each of the threads should be" + eol + " aggregated before displaying the results" + eol + "-D -- Indicates that the script should be executed in debug" + eol + " mode. This also implies verbose mode" + eol + "-v -- Indicates that the script should be executed in verbose" + eol + " (but not debug) mode" + eol + "-V -- Indicates that the script should be validated but not" + eol + " executed" + eol + "-h -- Displays usage information for this program" ); } /** * Writes information logged during job processing to standard output. * * @param message The message to be written to standard output. */ public void writeMessage(String message) { System.out.println(message); System.out.flush(); } /** * Writes verbose information logged during job processing to standard * output (if verbose logging is enabled). * * @param message The message to be written to standard output. */ public void writeVerbose(String message) { if (verboseMode) { System.out.println(message); } } /** * Indicates whether the message writer is using verbose mode and therefore * will display messages written with the <CODE>writeVerbose</CODE> method. * * @return <CODE>true</CODE> if the message writer is using verbose mode, or * <CODE>false</CODE> if not. */ public boolean usingVerboseMode() { return verboseMode; } }