/* * 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.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URL; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Properties; import java.util.StringTokenizer; import com.slamd.job.JobClass; import com.slamd.job.OptimizationAlgorithm; import com.slamd.job.SingleStatisticOptimizationAlgorithm; import com.slamd.common.Constants; import com.slamd.jobs.JSSEBlindTrustSocketFactory; import com.slamd.parameter.BooleanParameter; import com.slamd.parameter.FloatParameter; import com.slamd.parameter.IntegerParameter; import com.slamd.parameter.InvalidValueException; import com.slamd.parameter.LabelParameter; import com.slamd.parameter.MultiChoiceParameter; import com.slamd.parameter.Parameter; import com.slamd.parameter.ParameterList; import com.slamd.parameter.PlaceholderParameter; import com.slamd.parameter.StringParameter; import com.slamd.http.HTTPClient; import com.slamd.http.HTTPRequest; import com.slamd.http.HTTPResponse; /** * This class defines a utility that may be used to schedule SLAMD jobs from the * command line. It does this by communicating with the SLAMD administrative * interface over HTTP to ensure that the scheduler is properly updated, that * all appropriate validity checking is performed, and that any required * authentication is honored. * * * @author Neil A. Wilson */ public class CommandLineJobScheduler { // The set of parameters that define information about the way in which the // job should be scheduled. private BooleanParameter disabledParameter = new BooleanParameter("create_disabled", "Create Job As Disabled", "Indicates whether the job should be disabled " + "when it is scheduled.", false); private BooleanParameter monitorClientsIfAvailableParameter = new BooleanParameter("monitor_clients_if_available", "Monitor Clients if Available", "Indicates whether the SLAMD server should " + "automatically request resource monitoring for " + "client systems used for a job if they also have " + "active resource monitor clients.", false); private BooleanParameter optimizeParameter = new BooleanParameter("optimize_results", "Optimize Results", "Indicates whether to create an optimizing job.", true); private BooleanParameter rerunParameter = new BooleanParameter("rerun", "Re-Run Best Iteration", "Indicates whether the best iteration of the " + "optimizing job should be re-run.", false); private BooleanParameter waitParameter = new BooleanParameter("wait_for_clients", "Wait for Available Clients", "Indicates whether the job should wait for a " + "sufficient set of clients to be available if " + "they are not available when the job start time " + "arrives.", true); private IntegerParameter clientsParameter = new IntegerParameter("num_clients", "Number of Clients", "The number of clients to use to run the job.", true, 1, true, 1, false, 0); private IntegerParameter collectionIntervalParameter = new IntegerParameter("collection_interval", "Statistics Collection Interval", "The collection interval to use when capturing " + "statistics.", true, 60, true, 1, false, 0); private IntegerParameter delayParameter = new IntegerParameter("delay_between_iterations", "Delay Between Iterations", "The length of time in seconds that should be " + "allowed between iterations of an optimizing job.", true, 0, true, 0, false, 0); private IntegerParameter durationParameter = new IntegerParameter("duration", "Job Duration", "The maximum length of time in seconds that the " + "job should be allowed to run.", false, -1, false, 0, false, 0); private IntegerParameter maxNonImprovingParameter = new IntegerParameter("max_nonimproving", "Maximum Consecutive Non-Improving Iterations", "The maximum number of iterations that should " + "be allowed to run that do not produce results " + "better than the best iteration so far.", true, 1, true, 1, false, 1); private IntegerParameter maxThreadsParameter = new IntegerParameter("max_threads", "Maximum Number of Threads", "The maximum number of threads per client to use " + "for this optimizing job.", false, -1, false, 0, false, 0); private IntegerParameter minThreadsParameter = new IntegerParameter("min_threads", "Minimum Number of Threads", "The minimum number of threads per client to use " + "for this optimizing job.", true, 1, true, 1, false, 0); private IntegerParameter rerunDurationParameter = new IntegerParameter("rerun_duration", "Re-Run Duration", "The length of time in seconds to use when " + "re-running the best iteration of this " + "optimizing job.", false, -1, false, 0, false, 0); private IntegerParameter threadIncrementParameter = new IntegerParameter("thread_increment", "Thread Increment Between Iterations", "The increment to use when increasing the number " + "of threads per client between optimizing job " + "iterations.", true, 1, true, 1, false, 0); private IntegerParameter threadsParameter = new IntegerParameter("num_threads", "Number of Threads per Client", "The number of threads per client to use to run " + "the job.", true, 1, true, 1, false, 0); private IntegerParameter threadStartupDelayParameter = new IntegerParameter("thread_startup_delay", "Thread Startup Delay (ms)", "The delay in milliseconds that will be used " + "when starting the individual threads on the " + "client. If no value is specified, then there " + "will not be any delay between client thread " + "startup.", false, 0, true, 0, false, 0); private StringParameter algorithmParameter = new StringParameter("optimization_algorithm", "Optimization Algorithm", "The fully-qualified name of the Java class that " + "provides the optimization algorithm to use for " + "the optimizing job.", true, ""); private StringParameter classParameter = new StringParameter("job_class", "Job Class Name", "The fully-qualified name of the Java class that " + "provides an implementation of the job class to " + "execute.", true, ""); private StringParameter descriptionParameter = new StringParameter("description", "Job Description", "The description for the job.", false, ""); private StringParameter folderParameter = new StringParameter("job_folder", "Place in Folder", "The name of the job folder in which the job " + "should be placed when it is scheduled.", false, ""); private StringParameter monitorClientsParameter = new StringParameter("monitor_clients", "Resource Monitor Clients", "The address(es) of the resource monitor " + "client(s) that should be used to collect " + "information while the job is running.", false, ""); private StringParameter notifyAddrsParameter = new StringParameter("notify_addresses", "Notify Addresses", "The e-mail address(es) of the user(s) to notify " + "when the job is complete. If multiple addresses " + "are provided, they should be separated by commas.", false, ""); private StringParameter requestedClientsParameter = new StringParameter("requested_clients", "Requested Clients", "The address(es) of the client(s) that should be " + "used to run the job. If multiple clients are to " + "be requested, they should be separated by commas.", false, ""); private StringParameter startTimeParameter = new StringParameter("start_time", "Job Start Time", "The time that the job should be considered " + "eligible for execution, in the format " + "YYYYMMDDhhmmss.", true, ""); private StringParameter stopTimeParameter = new StringParameter("stop_time", "Job Stop Time", "The time that the job should stop running, in " + "the format YYYYMMDDhhmmss.", false, ""); // Parameter arrays to use when constructing the parameters to use for the // configuration file. private Parameter[] jobParameters = new Parameter[] { classParameter, folderParameter, disabledParameter, descriptionParameter, startTimeParameter, stopTimeParameter, durationParameter, clientsParameter, waitParameter, requestedClientsParameter, monitorClientsParameter, monitorClientsIfAvailableParameter, threadsParameter, threadStartupDelayParameter, collectionIntervalParameter, notifyAddrsParameter }; private Parameter[] optimizingParameters = new Parameter[] { classParameter, optimizeParameter, algorithmParameter, folderParameter, descriptionParameter, startTimeParameter, durationParameter, delayParameter, clientsParameter, requestedClientsParameter, monitorClientsParameter, monitorClientsIfAvailableParameter, minThreadsParameter, maxThreadsParameter, threadIncrementParameter, collectionIntervalParameter, notifyAddrsParameter, maxNonImprovingParameter, rerunParameter, rerunDurationParameter }; // Variables used processing. private boolean createDisabled = false; private boolean monitorClientsIfAvailable = false; private boolean rerunBestIteration = false; private boolean optimizingJob = false; private boolean useSSL = false; private boolean verboseMode = false; private boolean waitForClients = true; private Date startDate = null; private Date stopDate = null; private int collectionInterval = 60; private int delayBetweenIterations = 0; private int duration = -1; private int maxThreads = -1; private int minThreads = 1; private int nonImprovingIterations = 1; private int numClients = -1; private int rerunDuration = -1; private int slamdPort = 8080; private int threadIncrement = 1; private int threadsPerClient = -1; private int threadStartupDelay = 0; private JobClass jobInstance = null; private OptimizationAlgorithm optimizationAlgorithm = new SingleStatisticOptimizationAlgorithm(); private Parameter[] jobSpecificParameters = null; private Parameter[] optimizationParameters = null; private String authID = null; private String authPW = null; private String configFile = null; private String dependencyID = null; private String description = null; private String eol = Constants.EOL; private String folderName = null; private String jobClassName = null; private String postURI = "/slamd"; private String slamdHost = "127.0.0.1"; private String startTime = null; private String stopTime = null; private String[] monitorClients = null; private String[] notifyAddresses = null; private String[] requestedClients = null; /** * Invokes the constructor and provides it with the command-line arguments. * * @param args The command-line arguments provided to this program. */ public static void main(String[] args) { ArrayList<String> configFileList = new ArrayList<String>(); boolean generateConfig = false; boolean makeInterDependent = false; boolean optimizingJob = false; boolean useSSL = false; boolean verboseMode = false; int slamdPort = 8080; OptimizationAlgorithm optimizationAlgorithm = new SingleStatisticOptimizationAlgorithm(); String authID = null; String authPW = null; String dependencyID = null; String jobClassName = null; String postURI = "/slamd"; String slamdHost = "127.0.0.1"; // Iterate through the command line arguments. for (int i=0; i < args.length; i++) { if (args[i].equals("-f")) { configFileList.add(args[++i]); } else if (args[i].equals("-h")) { slamdHost = args[++i]; } else if(args[i].equals("-p")) { slamdPort = Integer.parseInt(args[++i]); } else if (args[i].equals("-S")) { useSSL = true; } else if (args[i].equals("-A")) { authID = args[++i]; } else if (args[i].equals("-P")) { authPW = args[++i]; } else if (args[i].equals("-D")) { dependencyID = args[++i]; } else if (args[i].equals("-u")) { postURI = args[++i]; } else if (args[i].equals("-d")) { makeInterDependent = true; } else if (args[i].equals("-v")) { verboseMode = true; } else if (args[i].equals("-g")) { generateConfig = true; jobClassName = args[++i]; } else if (args[i].equals("-O")) { optimizingJob = true; } else if (args[i].equals("-o")) { String algorithmClassName = args[++i]; try { Class algorithmClass = Constants.classForName(algorithmClassName); optimizationAlgorithm = (OptimizationAlgorithm) algorithmClass.newInstance(); } catch (Exception e) { System.err.println("Unable to load optimization algorithm class \"" + algorithmClassName + "\" -- " + e); return; } } else if (args[i].equals("-H")) { displayUsage(); return; } else { System.err.println("ERROR: Invalid argument \"" + args[i] + '"'); displayUsage(); return; } } if (configFileList.isEmpty()) { System.err.println("ERROR: At least one configuration file must be " + "specified."); displayUsage(); return; } if (generateConfig) { if (configFileList.size() > 1) { System.err.println("ERROR: Only one file may be specified when " + "generating a configuration file."); displayUsage(); return; } String configFile = configFileList.get(0); CommandLineJobScheduler scheduler = new CommandLineJobScheduler(configFile, slamdHost, slamdPort, useSSL, authID, authPW, postURI, dependencyID, jobClassName, optimizingJob, optimizationAlgorithm, verboseMode); scheduler.generateConfigFile(); return; } for (int i=0; i < configFileList.size(); i++) { String configFile = configFileList.get(i); CommandLineJobScheduler scheduler = new CommandLineJobScheduler(configFile, slamdHost, slamdPort, useSSL, authID, authPW, postURI, dependencyID, jobClassName, optimizingJob, optimizationAlgorithm, verboseMode); if (! scheduler.parseConfigFile()) { System.err.println("An error occurred while parsing configuration " + "file \"" + configFile + '"'); return; } String scheduledID; if (scheduler.optimizingJob) { scheduledID = scheduler.scheduleOptimizingJob(); } else { scheduledID = scheduler.scheduleJob(); } if (scheduledID == null) { System.err.println("An error occurred while attempting to schedule " + "the job defined in configuration file \"" + configFile + '"'); return; } if (makeInterDependent) { dependencyID = scheduledID; } } } /** * Creates a new instance of this command-line scheduler with the provided * information. * * @param configFile The path to the configuration file to use. * @param slamdHost The address to use for the SLAMD server. * @param slamdPort The port of the SLAMD server's HTTP * administration interface. * @param useSSL Indicates whether to use SSL to communicate * with the SLAMD server. * @param authID The user ID to use to authenticate to the * server. * @param authPW The password to use to authenticate to the * server. * @param postURI The URI in the SLAMD server to which the * request should be posted. * @param dependencyID The job ID of the job on which to make the * new job dependent. * @param jobClassName The name of the job class for which to * generate the configuration. * @param optimizingJob Indicates whether the configuration * generated should be an optimizing job. * @param optimizationAlgorithm The optimization algorithm that should be * used to create the optimizing job. * @param verboseMode Indicates whether the scheduler should * operate in verbose mode. */ public CommandLineJobScheduler(String configFile, String slamdHost, int slamdPort, boolean useSSL, String authID, String authPW, String postURI, String dependencyID, String jobClassName, boolean optimizingJob, OptimizationAlgorithm optimizationAlgorithm, boolean verboseMode) { this.configFile = configFile; this.slamdHost = slamdHost; this.slamdPort = slamdPort; this.useSSL = useSSL; this.authID = authID; this.authPW = authPW; this.postURI = postURI; this.dependencyID = dependencyID; this.jobClassName = jobClassName; this.optimizingJob = optimizingJob; this.optimizationAlgorithm = optimizationAlgorithm; this.verboseMode = verboseMode; } /** * Generates a configuration file to use to schedule the job. */ public void generateConfigFile() { // First, verify that the specified job class exists and that it is a valid // job class. try { Class<?> jobClass = Constants.classForName(jobClassName); Class<?> jobSuperClass = Constants.classForName(Constants.JOB_THREAD_SUPERCLASS_NAME); if (jobSuperClass.isAssignableFrom(jobClass)) { try { jobInstance = (JobClass) jobClass.newInstance(); } catch (Exception e) { if (verboseMode) { e.printStackTrace(); } System.err.println("ERROR: Could not create an instance of the " + "job class " + jobClassName + ": " + e); return; } } else { System.err.println("ERROR: Class " + jobClassName + " is not a valid SLAMD job class"); return; } } catch (ClassNotFoundException cnfe) { if (verboseMode) { cnfe.printStackTrace(); } System.err.println("ERROR: Could not find job class " + jobClassName + ". Is it in the classpath?"); return; } // Set the name of the job class in the config file. try { classParameter.setValue(jobClassName); } catch (InvalidValueException ive) { // This should never happen. if (verboseMode) { ive.printStackTrace(); } System.err.println("ERROR: Unable to set job class name -- " + ive); return; } // Set the name of the optimization algorithm in the config file. try { algorithmParameter.setValue(optimizationAlgorithm.getClass().getName()); } catch (InvalidValueException ive) { // This should never happen. if (verboseMode) { ive.printStackTrace(); } System.err.println("ERROR: Unable to set optimization algorithm -- " + ive); return; } // Set the start time to the current time. try { SimpleDateFormat dateFormat = new SimpleDateFormat(Constants.ATTRIBUTE_DATE_FORMAT); startTimeParameter.setValue(dateFormat.format(new Date())); } catch (InvalidValueException ive) { // This also should never happen. if (verboseMode) { ive.printStackTrace(); } System.err.println("ERROR: Unable to set job start time -- " + ive); return; } // If this is to be an optimizing job, then make sure it is optimizable with // the specified algorithm. if (optimizingJob) { if (! optimizationAlgorithm.availableWithJobClass(jobInstance)) { System.err.println("ERROR: Job class \"" + jobClassName + "\" may not be used with optimization algorithm \"" + optimizationAlgorithm.getClass().getName() + '"'); return; } } try { // Create the file writer BufferedWriter writer = new BufferedWriter(new FileWriter(configFile)); // Write the header writer.write("#################################################" + "##############################" + eol); writeComment(writer, "SLAMD Command-Line Job Scheduler Config File"); writeComment(writer, ""); writeComment(writer, "Job Name: " + jobInstance.getJobName()); writeComment(writer, ""); writeComment(writer, "Job Class: " + jobClassName); writeComment(writer, ""); writeComment(writer, "Job Class Description: " + jobInstance.getShortDescription()); writeComment(writer, ""); writer.write("#################################################" + "##############################" + eol); writer.write(eol+eol); if (optimizingJob) { for (int i=0; i < optimizingParameters.length; i++) { writeParam(writer, optimizingParameters[i], null); } // Write all the optimization algorithm parameters. Parameter[] optimizationParams = optimizationAlgorithm. getOptimizationAlgorithmParameterStubs(jobInstance). getParameters(); for (int i=0; i < optimizationParams.length; i++) { writeParam(writer, optimizationParams[i], Constants.SERVLET_PARAM_OPTIMIZATION_PARAM_PREFIX); } } else { for (int i=0; i < jobParameters.length; i++) { writeParam(writer, jobParameters[i], null); } } // Iterate through each of the job-specific parameters and write out the // comments and configuration info for each Parameter[] jobParams = jobInstance.getParameterStubs().getParameters(); for (int i=0; i < jobParams.length; i++) { writeParam(writer, jobParams[i], Constants.SERVLET_PARAM_JOB_PARAM_PREFIX); } // Close the file and be done. writer.flush(); writer.close(); System.out.println("The configuration file was written to " + configFile + '.'); System.out.println("You may need to edit the file before it can be " + "used to run the job."); } catch (IOException ioe) { if (verboseMode) { ioe.printStackTrace(); } System.err.println("ERROR writing sample configuration file " + configFile + ": " + ioe); } } /** * Writes information about the specified parameter to the configuration file * using the given writer. * * @param writer The writer to use to send data to the config file. * @param parameter The parameter to write to the config file. * @param prefix The prefix to use for the parameter when writing it to * the config file. If no prefix should be used, this may * be either null or an empty string. * * @throws IOException If a problem occurs while writing to the * configuration file. */ private void writeParam(BufferedWriter writer, Parameter parameter, String prefix) throws IOException { // If the parameter is a placeholder, then don't do anything with it. if ((parameter instanceof PlaceholderParameter) || (parameter instanceof LabelParameter)) { return; } // Write the display name and description. writeComment(writer, parameter.getDisplayName()); writeComment(writer, parameter.getDescription()); // Indicate whether the parameter is required or optional. if (parameter.isRequired()) { writeComment(writer, "This parameter is required."); } else { writeComment(writer, "This parameter is optional."); } // If there are any constraints on the value, then state what they are. if (parameter instanceof BooleanParameter) { writeComment(writer, "The value must be either \"true\" or \"false\"."); } else if (parameter instanceof FloatParameter) { FloatParameter fp = (FloatParameter) parameter; if (fp.hasLowerBound()) { if (fp.hasUpperBound()) { writeComment(writer, "The value must be between " + fp.getLowerBound() + " and " + fp.getUpperBound() + '.'); } else { writeComment(writer, "The value must be greater than or equal to " + fp.getLowerBound() + '.'); } } else if (fp.hasUpperBound()) { writeComment(writer, "The value must be less than or equal to " + fp.getUpperBound() + '.'); } } else if (parameter instanceof IntegerParameter) { IntegerParameter ip = (IntegerParameter) parameter; if (ip.hasLowerBound()) { if (ip.hasUpperBound()) { writeComment(writer, "The value must be between " + ip.getLowerBound() + " and " + ip.getUpperBound() + '.'); } else { writeComment(writer, "The value must be greater than or equal to " + ip.getLowerBound() + '.'); } } else if (ip.hasUpperBound()) { writeComment(writer, "The value must be less than or equal to " + ip.getUpperBound() + '.'); } } else if (parameter instanceof MultiChoiceParameter) { writeComment(writer, "The value must be one of the following:"); String[] choices = ((MultiChoiceParameter) parameter).getChoices(); for (int j=0; j < choices.length; j++) { writeComment(writer, " - \"" + choices[j] + '"'); } } // Finally, write the parameter name and possibly a value if ((prefix != null) && (prefix.length() > 0)) { writer.write(prefix); } writer.write(parameter.getName() + '=' + parameter.getValueString() + eol + eol + eol); } /** * Writes the provided comment to the generated configuration file, wrapping * long lines if necessary. * * @param writer The buffered writer used to write information to the * configuration file. * @param comment The comment to be written to the file. * * @throws IOException If a problem occurs while writing the comment to the * configuration file. */ private void writeComment(BufferedWriter writer, String comment) throws IOException { if (comment.length() > 75) { String indentStr = ""; while (comment.length() > (75-indentStr.length())) { int spacePos = comment.lastIndexOf(' ', 75); if (spacePos < 0) { spacePos = comment.indexOf(' '); if (spacePos < 0) { writer.write("# " + indentStr + comment + eol); return; } else { writer.write("# " + indentStr + comment.substring(0, spacePos) + eol); comment = comment.substring(spacePos+1); } } else { writer.write("# " + indentStr + comment.substring(0, spacePos) + eol); comment = comment.substring(spacePos+1); } indentStr = " "; } writer.write("# " + indentStr + comment + eol); } else { writer.write("# " + comment + eol); } } /** * Parses the configuration file and assigns its contents to instance * variables. * * @return <CODE>true</CODE> if the file was parsed successfully and the job * configuration is valid, or <CODE>false</CODE> if there was a * problem of some kind. */ public boolean parseConfigFile() { boolean configValid = true; debug("Parsing configuration file " + configFile); // Read the configuration file Properties configProperties = new Properties(); try { configProperties.load(new FileInputStream(configFile)); } catch (IOException ioe) { if (verboseMode) { ioe.printStackTrace(); } System.err.println("ERROR: Unable to open configuration file " + configFile); return false; } // Get the name of the job class and create the job instance. jobClassName = configProperties.getProperty(classParameter.getName()); if (jobClassName == null) { System.err.println("ERROR: No job class name specified."); return false; } try { debug("Loading and instantiating job class \"" + jobClassName + '"'); Class<?> jobClass = Constants.classForName(jobClassName); Class<?> jobSuperClass = Constants.classForName(Constants.JOB_THREAD_SUPERCLASS_NAME); if (jobSuperClass.isAssignableFrom(jobClass)) { try { jobInstance = (JobClass) jobClass.newInstance(); } catch (Exception e) { if (verboseMode) { e.printStackTrace(); } System.err.println("ERROR: Could not create an instance of the " + "job class " + jobClassName + ": " + e); return false; } } else { System.err.println("ERROR: Class " + jobClassName + " is not a valid SLAMD job class"); return false; } } catch (ClassNotFoundException cnfe) { if (verboseMode) { cnfe.printStackTrace(); } System.err.println("ERROR: Could not find job class " + jobClassName + ". Is it in the classpath?"); return false; } // See if this is to be an optimizing job. String optimizingStr = configProperties.getProperty(optimizeParameter.getName()); if ((optimizingStr != null) && optimizingStr.equalsIgnoreCase("true")) { optimizingJob = true; } // Get the optimization algorithm to use. String algorithmStr = configProperties.getProperty(algorithmParameter.getName()); if (optimizingJob) { try { Class algorithmClass = Constants.classForName(algorithmStr); optimizationAlgorithm = (OptimizationAlgorithm) algorithmClass.newInstance(); } catch (Exception e) { System.err.println("ERROR: Unable to use optimization algorithm \"" + algorithmStr + "\" -- " + e); configValid = false; } } // Get the folder name. It can be blank and needs no validation, but an // invalid folder name can prevent the job from being scheduled properly. folderName = configProperties.getProperty(folderParameter.getName()); // Get the job description. We don't really care about this. description = configProperties.getProperty(descriptionParameter.getName()); // Get the job start time. It must be present and properly formatted. startTime = configProperties.getProperty(startTimeParameter.getName()); if ((startTime == null) || (startTime.length() == 0)) { System.err.println("ERROR: No start time specified."); configValid = false; } else if (startTime.length() != 14) { System.err.println("ERROR: Invalid start time. It must be in the " + "form YYYYMMDDhhmmss"); configValid = false; } else { SimpleDateFormat dateFormat = new SimpleDateFormat(Constants.ATTRIBUTE_DATE_FORMAT); try { startDate = dateFormat.parse(startTime); } catch (ParseException pe) { if (verboseMode) { pe.printStackTrace(); } System.err.println("ERROR: Invalid start time. It must be in the " + "form YYYYMMDDhhmmss"); configValid = false; } } // Get the job stop time. It is optional, but if provided must be properly // formatted. stopTime = configProperties.getProperty(stopTimeParameter.getName()); if ((stopTime != null) && (stopTime.length() > 0)) { if (stopTime.length() != 14) { System.err.println("ERROR: Invalid stop time. It must be in the " + "form YYYYMMDDhhmmss"); configValid = false; } else { SimpleDateFormat dateFormat = new SimpleDateFormat(Constants.ATTRIBUTE_DATE_FORMAT); try { stopDate = dateFormat.parse(stopTime); } catch (ParseException pe) { if (verboseMode) { pe.printStackTrace(); } System.err.println("ERROR: Invalid stop time. It must be in the " + "form YYYYMMDDhhmmss"); configValid = false; } } } // Get the job duration. It is optional, but if provided must be an // integer. String durationStr = configProperties.getProperty(durationParameter.getName()); if ((durationStr != null) && (durationStr.length() > 0)) { try { duration = Integer.parseInt(durationStr); } catch (NumberFormatException nfe) { if (verboseMode) { nfe.printStackTrace(); } System.err.println("ERROR: Job duration must be an integer."); configValid = false; } } // Get the number of clients. It must be specified and must be a positive // integer. String clientsStr = configProperties.getProperty(clientsParameter.getName()); if ((clientsStr == null) || (clientsStr.length() == 0)) { System.err.println("ERROR: Number of clients must be specified."); configValid = false; } else { try { numClients = Integer.parseInt(clientsStr); if (numClients <= 0) { System.err.println("ERROR: Number of clients must be greater than " + "zero."); configValid = false; } } catch (NumberFormatException nfe) { if (verboseMode) { nfe.printStackTrace(); } System.err.println("ERROR: Number of clients must be an integer."); configValid = false; } } // Get the set of requested clients. This is optional. String requestedClientsStr = configProperties.getProperty(requestedClientsParameter.getName()); if ((requestedClientsStr != null) && (requestedClientsStr.length() > 0)) { ArrayList<String> clientList = new ArrayList<String>(); StringTokenizer st = new StringTokenizer(requestedClientsStr, ", \t"); while (st.hasMoreTokens()) { clientList.add(st.nextToken()); } requestedClients = new String[clientList.size()]; clientList.toArray(requestedClients); } // Get the set of resource monitor clients. This is optional. String monitorClientsStr = configProperties.getProperty(monitorClientsParameter.getName()); if ((monitorClientsStr != null) && (monitorClientsStr.length() > 0)) { ArrayList<String> clientList = new ArrayList<String>(); StringTokenizer st = new StringTokenizer(monitorClientsStr, ", \t"); while (st.hasMoreTokens()) { clientList.add(st.nextToken()); } monitorClients = new String[clientList.size()]; clientList.toArray(monitorClients); } // Get the statistics collection interval. It must be specified and must be // a positive integer. String intervalStr = configProperties.getProperty(collectionIntervalParameter.getName()); if ((intervalStr == null) || (intervalStr.length() == 0)) { System.err.println("ERROR: Statistics collection interval must be " + "specified."); configValid = false; } else { try { collectionInterval = Integer.parseInt(intervalStr); if (collectionInterval <= 0) { System.err.println("ERROR: Statistics collection interval must be " + "greater than zero."); configValid = false; } } catch (NumberFormatException nfe) { if (verboseMode) { nfe.printStackTrace(); } System.err.println("ERROR: Statistics collection interval must be " + "an integer."); configValid = false; } } // Get the set of e-mail addresses of the users to notify when the job is // complete. This is optional. String notifyStr = configProperties.getProperty(notifyAddrsParameter.getName()); if ((notifyStr != null) && (notifyStr.length() > 0)) { ArrayList<String> addressList = new ArrayList<String>(); StringTokenizer st = new StringTokenizer(notifyStr, ", \t"); while (st.hasMoreTokens()) { addressList.add(st.nextToken()); } notifyAddresses = new String[addressList.size()]; addressList.toArray(notifyAddresses); } // Get values of parameters that differ based on whether this is an // optimizing job. if (optimizingJob) { // Get the delay between iterations. It must be specified and must be // an integer greater than or equal to zero. String delayStr = configProperties.getProperty(delayParameter.getName()); if ((delayStr == null) || (delayStr.length() == 0)) { System.err.println("ERROR: Delay between iterations must be " + "specified."); configValid = false; } else { try { delayBetweenIterations = Integer.parseInt(delayStr); if (delayBetweenIterations < 0) { System.err.println("ERROR: Delay between iterations must be " + "greater than or equal to zero."); configValid = false; } } catch (NumberFormatException nfe) { if (verboseMode) { nfe.printStackTrace(); } System.err.println("ERROR: Delay between iterations must be an " + "integer."); configValid = false; } } // Get the minimum number of threads per client. It must be specified and // must be a positive integer. String minThreadsStr = configProperties.getProperty(minThreadsParameter.getName()); if ((minThreadsStr == null) || (minThreadsStr.length() == 0)) { System.err.println("ERROR: Minimum number of threads must be " + "specified."); configValid = false; } else { try { minThreads = Integer.parseInt(minThreadsStr); if (minThreads <= 0) { System.err.println("ERROR: Minimum number of threads must be " + "greater than zero."); configValid = false; } } catch (NumberFormatException nfe) { if (verboseMode) { nfe.printStackTrace(); } System.err.println("ERROR: Minimum number of threads must be an " + "integer."); configValid = false; } } // Get the maximum number of threads per client. It is optional, but if // specified must be an integer. String maxThreadsStr = configProperties.getProperty(maxThreadsParameter.getName()); if ((maxThreadsStr != null) && (maxThreadsStr.length() > 0)) { try { maxThreads = Integer.parseInt(maxThreadsStr); } catch (NumberFormatException nfe) { if (verboseMode) { nfe.printStackTrace(); } System.err.println("ERROR: Maximum number of threads must be an " + "integer."); configValid = false; } } // Get the thread increment. It must be specified and must be a positive // integer. String incrementStr = configProperties.getProperty(threadIncrementParameter.getName()); if ((incrementStr == null) || (incrementStr.length() == 0)) { System.err.println("ERROR: Thread increment must be specified."); configValid = false; } else { try { threadIncrement = Integer.parseInt(incrementStr); if (threadIncrement <= 0) { System.err.println("ERROR: Thread increment must be greater " + "than zero."); configValid = false; } } catch (NumberFormatException nfe) { if (verboseMode) { nfe.printStackTrace(); } System.err.println("ERROR: Thread increment must be an integer."); configValid = false; } } // Get the maximum number of non-improving iterations. It must be // specified and must be a positive integer. String nonImprovingStr = configProperties.getProperty(maxNonImprovingParameter.getName()); if ((nonImprovingStr == null) || (nonImprovingStr.length() == 0)) { System.err.println("ERROR: Maximum number of non-improving " + "iterations must be specified."); configValid = false; } else { try { nonImprovingIterations = Integer.parseInt(nonImprovingStr); if (nonImprovingIterations <= 0) { System.err.println("ERROR: Maximum number of non-improving " + "iterations must be greater than zero."); configValid = false; } } catch (NumberFormatException nfe) { if (verboseMode) { nfe.printStackTrace(); } System.err.println("ERROR: Maximum number of non-improving " + "iterations must be an integer."); configValid = false; } } // Determine whether to re-run the best iteration. It is optional. String reRunStr = configProperties.getProperty(rerunParameter.getName()); if ((reRunStr != null) && reRunStr.equalsIgnoreCase("true")) { rerunBestIteration = true; } // Determine the duration to use for the re-run iteration. It is // optional, but if specified must be an integer. String reRunDurationStr = configProperties.getProperty(rerunDurationParameter.getName()); if ((reRunDurationStr != null) && (reRunDurationStr.length() > 0)) { try { rerunDuration = Integer.parseInt(reRunDurationStr); } catch (NumberFormatException nfe) { if (verboseMode) { nfe.printStackTrace(); } System.err.println("ERROR: Re-run duration must be an integer."); configValid = false; } } } else { // Determine whether the job should be disabled when it is created. String disabledStr = configProperties.getProperty(disabledParameter.getName()); if ((disabledStr != null) && disabledStr.equalsIgnoreCase("true")) { createDisabled = true; } // Determine whether the job should wait for clients to be available. String waitStr = configProperties.getProperty(waitParameter.getName()); if ((waitStr != null) && waitStr.equalsIgnoreCase("false")) { waitForClients = false; } // Determine whether to monitor clients if they are available. String monitorStr = configProperties.getProperty( monitorClientsIfAvailableParameter.getName()); if ((monitorStr != null) && monitorStr.equalsIgnoreCase("true")) { monitorClientsIfAvailable = true; } // Get the number of threads per client. It must be specified and must be // a positive integer. String threadsStr = configProperties.getProperty(threadsParameter.getName()); if ((threadsStr == null) || (threadsStr.length() == 0)) { System.err.println("ERROR: Number of threads per client must be " + "specified."); configValid = false; } else { try { threadsPerClient = Integer.parseInt(threadsStr); if (threadsPerClient <= 0) { System.err.println("ERROR: Number of threads per client must be " + "greater than zero."); configValid = false; } } catch (NumberFormatException nfe) { if (verboseMode) { nfe.printStackTrace(); } System.err.println("ERROR: Number of threads per client must be " + "an integer."); configValid = false; } } } // Get the thread startup delay. It is optional, but if provided must be // an integer greater than or equal to zero. String delayStr = configProperties.getProperty(threadStartupDelayParameter.getName()); if ((delayStr == null) || (delayStr.length() == 0)) { threadStartupDelay = 0; } else { try { threadStartupDelay = Integer.parseInt(delayStr); if (threadStartupDelay < 0) { System.err.println("ERROR: Thread startup delay must be greater " + "than or equal to zero."); configValid = false; } } catch (NumberFormatException nfe) { if (verboseMode) { nfe.printStackTrace(); } System.err.println("ERROR: Thread startup delay must be an integer."); configValid = false; } } // Validate the values of all the optimization algorithm parameters. if (optimizingJob) { debug("Validating optimization algorithm parameters"); optimizationParameters = optimizationAlgorithm. getOptimizationAlgorithmParameterStubs(jobInstance). getParameters(); for (int i=0; i < optimizationParameters.length; i++) { if ((optimizationParameters[i] instanceof PlaceholderParameter) || (optimizationParameters[i] instanceof LabelParameter)) { continue; } String valueStr = configProperties.getProperty( Constants.SERVLET_PARAM_OPTIMIZATION_PARAM_PREFIX + optimizationParameters[i].getName()); if (valueStr == null) { valueStr = ""; } try { optimizationParameters[i].setValueFromString(valueStr); } catch (InvalidValueException ive) { if (verboseMode) { ive.printStackTrace(); } System.err.println("ERROR: Invalid value for optimization " + "algorithm parameter \"" + optimizationParameters[i].getDisplayName() + "\": " + ive.getMessage()); configValid = false; } } } // Validate the values of all the job-specific parameters. debug("Validating job-specific parameters"); jobSpecificParameters = jobInstance.getParameterStubs().getParameters(); for (int i=0; i < jobSpecificParameters.length; i++) { if ((jobSpecificParameters[i] instanceof PlaceholderParameter) || (jobSpecificParameters[i] instanceof LabelParameter)) { continue; } String valueStr = configProperties.getProperty( Constants.SERVLET_PARAM_JOB_PARAM_PREFIX + jobSpecificParameters[i].getName()); if (valueStr == null) { valueStr = ""; } // Replace any occurrences of "\n" (with the exception of "\\n") with line // breaks. int pos = 0; while ((pos < valueStr.length()) && ((pos = valueStr.indexOf('\\', pos)) >= 0)) { if (pos == (valueStr.length() - 1)) { break; } switch (valueStr.charAt(pos+1)) { case 'n': valueStr = valueStr.substring(0, pos) + '\n' + valueStr.substring(pos+2); pos++; break; case '\\': valueStr = valueStr.substring(0, pos) + valueStr.substring(pos+1); pos++; break; default: pos += 2; break; } } try { jobSpecificParameters[i].setValueFromString(valueStr); } catch (InvalidValueException ive) { if (verboseMode) { ive.printStackTrace(); } System.err.println("ERROR: Invalid value for job-specific " + "parameter \"" + jobSpecificParameters[i].getDisplayName() + "\": " + ive.getMessage()); configValid = false; } } // Validate the job as a whole. if (configValid) { debug("Validating job configuration"); ParameterList paramList = new ParameterList(jobSpecificParameters); try { jobInstance.validateJobInfo(numClients, threadsPerClient, 0, startDate, stopDate, duration, collectionInterval, paramList); } catch (InvalidValueException ive) { if (verboseMode) { ive.printStackTrace(); } } } debug("Done parsing job configuration -- job is " + (configValid ? "" : "not ") + "acceptable"); return configValid; } /** * Handles all processing required to schedule a job for execution. * * @return The job ID of the job that was scheduled, or <CODE>null</CODE> if * an error occurred to prevent the job from being scheduled * properly. */ public String scheduleJob() { // Create the HTTP client to use to send the request. HTTPClient httpClient = new HTTPClient(); httpClient.setFollowRedirects(true); httpClient.setRetrieveAssociatedFiles(false); if ((authID != null) && (authID.length() > 0) && (authPW != null) && (authPW.length() > 0)) { httpClient.enableAuthentication(authID, authPW); } if (verboseMode) { httpClient.enableDebugMode(); } String protocol = "http"; if (useSSL) { try { protocol = "https"; httpClient.setSSLSocketFactory(new JSSEBlindTrustSocketFactory()); } catch (Exception e) { System.err.println("ERROR: Unable to initialize the SSL socket " + "factory:"); e.printStackTrace(); return null; } } // Construct the URL for the request. String urlStr = protocol + "://" + slamdHost + ':' + slamdPort + postURI; URL requestURL; try { requestURL = new URL(urlStr); } catch (Exception e) { System.err.println("ERROR: The constructed URL '" + urlStr + "' is invalid:"); e.printStackTrace(); return null; } // Construct the request to send to the server. HTTPRequest request = new HTTPRequest(false, requestURL); request.addParameter(Constants.SERVLET_PARAM_SECTION, Constants.SERVLET_SECTION_JOB); request.addParameter(Constants.SERVLET_PARAM_SUBSECTION, Constants.SERVLET_SECTION_JOB_SCHEDULE); request.addParameter(Constants.SERVLET_PARAM_JOB_CLASS, jobClassName); request.addParameter(Constants.SERVLET_PARAM_JOB_VALIDATE_SCHEDULE, "1"); request.addParameter(Constants.SERVLET_PARAM_SUBMIT, Constants.SUBMIT_STRING_SCHEDULE_JOB); if (createDisabled) { request.addParameter(Constants.SERVLET_PARAM_JOB_DISABLED, "on"); } if ((folderName == null) || (folderName.length() == 0)) { request.addParameter(Constants.SERVLET_PARAM_JOB_FOLDER, Constants.FOLDER_NAME_UNCLASSIFIED); } else { request.addParameter(Constants.SERVLET_PARAM_JOB_FOLDER, folderName); } if ((description != null) && (description.length() > 0)) { request.addParameter(Constants.SERVLET_PARAM_JOB_DESCRIPTION, description); } request.addParameter(Constants.SERVLET_PARAM_JOB_START_TIME, startTime); if ((stopTime != null) && (stopTime.length() > 0)) { request.addParameter(Constants.SERVLET_PARAM_JOB_STOP_TIME, stopTime); } if (duration > 0) { request.addParameter(Constants.SERVLET_PARAM_JOB_DURATION, String.valueOf(duration)); } request.addParameter(Constants.SERVLET_PARAM_JOB_NUM_CLIENTS, String.valueOf(numClients)); if ((requestedClients != null) && (requestedClients.length > 0)) { String requestedClientsStr = requestedClients[0]; for (int i=1; i < requestedClients.length; i++) { requestedClientsStr += '\n' + requestedClients[i]; } request.addParameter(Constants.SERVLET_PARAM_JOB_CLIENTS, requestedClientsStr); } if ((monitorClients != null) && (monitorClients.length > 0)) { String monitorClientsStr = monitorClients[0]; for (int i=1; i < monitorClients.length; i++) { monitorClientsStr += '\n' + monitorClients[i]; } request.addParameter(Constants.SERVLET_PARAM_JOB_MONITOR_CLIENTS, monitorClientsStr); } if (monitorClientsIfAvailable) { request.addParameter( Constants.SERVLET_PARAM_JOB_MONITOR_CLIENTS_IF_AVAILABLE, "on"); } if (waitForClients) { request.addParameter(Constants.SERVLET_PARAM_JOB_WAIT_FOR_CLIENTS, "on"); } request.addParameter(Constants.SERVLET_PARAM_JOB_THREADS_PER_CLIENT, String.valueOf(threadsPerClient)); request.addParameter(Constants.SERVLET_PARAM_JOB_THREAD_STARTUP_DELAY, String.valueOf(threadStartupDelay)); request.addParameter(Constants.SERVLET_PARAM_JOB_COLLECTION_INTERVAL, String.valueOf(collectionInterval)); if ((dependencyID != null) && (dependencyID.length() > 0)) { request.addParameter(Constants.SERVLET_PARAM_JOB_DEPENDENCY, dependencyID); } if ((notifyAddresses != null) && (notifyAddresses.length > 0)) { String notifyStr = notifyAddresses[0]; for (int i=1; i < notifyAddresses.length; i++) { notifyStr += ',' + notifyAddresses[i]; } request.addParameter(Constants.SERVLET_PARAM_JOB_NOTIFY_ADDRESS, notifyStr); } for (int i=0; i < jobSpecificParameters.length; i++) { String valueStr = jobSpecificParameters[i].getHTMLPostValue(); if (valueStr != null) { // The value is already encoded, and doing it again would cause // problems. Therefore, manually, construct the post string. String name = Constants.SERVLET_PARAM_JOB_PARAM_PREFIX + jobSpecificParameters[i].getName(); try { request.addEncodedParameter(name, valueStr); } catch (UnsupportedEncodingException uee) { request.addParameter(name, valueStr); } } } // Send the request and read the response. HTTPResponse response; try { response = httpClient.sendRequest(request); httpClient.closeAll(); } catch (Exception e) { System.err.println("Error communicating with SLAMD server:"); e.printStackTrace(); return null; } // Make sure that the response status code was "200". if (response.getStatusCode() != 200) { System.err.println("ERROR: Unexpected response status code (" + response.getStatusCode() + ' ' + response.getResponseMessage() + ')'); return null; } // See if the response header included a job ID. If so, then the job was // scheduled successfully. If not, then there was a problem to prevent it // from being scheduled. String jobID = response.getHeader(Constants.SERVLET_PARAM_JOB_ID); if (jobID == null) { System.err.println("ERROR: Unable to determine the job ID from the " + "response."); System.err.println("This probably means that the job was not scheduled " + "properly."); if (verboseMode) { System.err.println("The body of the HTTP response was:"); try { byte[] responseData = response.getResponseData(); BufferedReader reader = new BufferedReader(new InputStreamReader( new ByteArrayInputStream(responseData))); String line = reader.readLine(); while (line != null) { System.out.println(line); line = reader.readLine(); } reader.close(); } catch (Exception e) { System.err.println("Error decoding response data:"); e.printStackTrace(); } } return null; } else { System.out.println("Successfully scheduled job " + jobID + " using config file \"" + configFile + "\"."); return jobID; } } /** * Handles all processing required to schedule an optimizing job for * execution. * * @return The job ID of the optimizing job that was scheduled, or * <CODE>null</CODE> if an error occurred to prevent the job from * being scheduled properly. */ public String scheduleOptimizingJob() { // Create the HTTP client to use to send the request. HTTPClient httpClient = new HTTPClient(); httpClient.setFollowRedirects(true); httpClient.setRetrieveAssociatedFiles(false); if ((authID != null) && (authID.length() > 0) && (authPW != null) && (authPW.length() > 0)) { httpClient.enableAuthentication(authID, authPW); } if (verboseMode) { httpClient.enableDebugMode(); } String protocol = "http"; if (useSSL) { try { protocol = "https"; httpClient.setSSLSocketFactory(new JSSEBlindTrustSocketFactory()); } catch (Exception e) { System.err.println("ERROR: Unable to initialize the SSL socket " + "factory:"); e.printStackTrace(); return null; } } // Construct the URL for the request. String urlStr = protocol + "://" + slamdHost + ':' + slamdPort + postURI; URL requestURL; try { requestURL = new URL(urlStr); } catch (Exception e) { System.err.println("ERROR: The constructed URL '" + urlStr + "' is invalid:"); e.printStackTrace(); return null; } // Construct the request to send to the server. HTTPRequest request = new HTTPRequest(false, requestURL); request.addParameter(Constants.SERVLET_PARAM_SECTION, Constants.SERVLET_SECTION_JOB); request.addParameter(Constants.SERVLET_PARAM_SUBSECTION, Constants.SERVLET_SECTION_JOB_OPTIMIZE); request.addParameter(Constants.SERVLET_PARAM_JOB_CLASS, jobClassName); request.addParameter(Constants.SERVLET_PARAM_OPTIMIZATION_ALGORITHM, optimizationAlgorithm.getClass().getName()); request.addParameter( Constants.SERVLET_PARAM_JOB_INCLUDE_THREAD_IN_DESCRIPTION, "on"); request.addParameter(Constants.SERVLET_PARAM_CONFIRMED, "Schedule"); if ((folderName == null) || (folderName.length() == 0)) { request.addParameter(Constants.SERVLET_PARAM_JOB_FOLDER, Constants.FOLDER_NAME_UNCLASSIFIED); } else { request.addParameter(Constants.SERVLET_PARAM_JOB_FOLDER, folderName); } if ((description != null) && (description.length() > 0)) { request.addParameter(Constants.SERVLET_PARAM_JOB_DESCRIPTION, description); } request.addParameter(Constants.SERVLET_PARAM_JOB_START_TIME, startTime); if (duration > 0) { request.addParameter(Constants.SERVLET_PARAM_JOB_DURATION, String.valueOf(duration)); } request.addParameter(Constants.SERVLET_PARAM_TIME_BETWEEN_STARTUPS, String.valueOf(delayBetweenIterations)); request.addParameter(Constants.SERVLET_PARAM_JOB_NUM_CLIENTS, String.valueOf(numClients)); if ((requestedClients != null) && (requestedClients.length > 0)) { String requestedClientsStr = requestedClients[0]; for (int i=1; i < requestedClients.length; i++) { requestedClientsStr += '\n' + requestedClients[i]; } request.addParameter(Constants.SERVLET_PARAM_JOB_CLIENTS, requestedClientsStr); } if ((monitorClients != null) && (monitorClients.length > 0)) { String monitorClientsStr = monitorClients[0]; for (int i=1; i < monitorClients.length; i++) { monitorClientsStr += '\n' + monitorClients[i]; } request.addParameter(Constants.SERVLET_PARAM_JOB_MONITOR_CLIENTS, monitorClientsStr); } if (monitorClientsIfAvailable) { request.addParameter( Constants.SERVLET_PARAM_JOB_MONITOR_CLIENTS_IF_AVAILABLE, "on"); } request.addParameter(Constants.SERVLET_PARAM_JOB_THREADS_MIN, String.valueOf(minThreads)); if (maxThreads > 0) { request.addParameter(Constants.SERVLET_PARAM_JOB_THREADS_MAX, String.valueOf(maxThreads)); } request.addParameter(Constants.SERVLET_PARAM_THREAD_INCREMENT, String.valueOf(threadIncrement)); request.addParameter(Constants.SERVLET_PARAM_JOB_COLLECTION_INTERVAL, String.valueOf(collectionInterval)); for (int i=0; i < optimizationParameters.length; i++) { if ((optimizationParameters[i] instanceof PlaceholderParameter) || (optimizationParameters[i] instanceof LabelParameter)) { continue; } String name = Constants.SERVLET_PARAM_OPTIMIZATION_PARAM_PREFIX + optimizationParameters[i].getName(); try { request.addEncodedParameter(name, optimizationParameters[i].getHTMLPostValue()); } catch (UnsupportedEncodingException uee) { request.addParameter(name, optimizationParameters[i].getHTMLPostValue()); } } request.addParameter(Constants.SERVLET_PARAM_JOB_MAX_NON_IMPROVING, String.valueOf(nonImprovingIterations)); if (rerunBestIteration) { request.addParameter(Constants.SERVLET_PARAM_RERUN_BEST_ITERATION, "on"); } if (rerunDuration > 0) { request.addParameter(Constants.SERVLET_PARAM_RERUN_DURATION, String.valueOf(rerunDuration)); } if ((dependencyID != null) && (dependencyID.length() > 0)) { request.addParameter(Constants.SERVLET_PARAM_JOB_DEPENDENCY, dependencyID); } if ((notifyAddresses != null) && (notifyAddresses.length > 0)) { String notifyStr = notifyAddresses[0]; for (int i=1; i < notifyAddresses.length; i++) { notifyStr += ',' + notifyAddresses[i]; } request.addParameter(Constants.SERVLET_PARAM_JOB_NOTIFY_ADDRESS, notifyStr); } for (int i=0; i < jobSpecificParameters.length; i++) { String valueStr = jobSpecificParameters[i].getHTMLPostValue(); if (valueStr != null) { // The value is already encoded, and doing it again would cause // problems. Therefore, manually, construct the post string. String name = Constants.SERVLET_PARAM_JOB_PARAM_PREFIX + jobSpecificParameters[i].getName(); try { request.addEncodedParameter(name, valueStr); } catch (UnsupportedEncodingException uee) { request.addParameter(name, valueStr); } } } // Send the request and read the response. HTTPResponse response; try { response = httpClient.sendRequest(request); httpClient.closeAll(); } catch (Exception e) { System.err.println("Error communicating with SLAMD server:"); e.printStackTrace(); return null; } // Make sure that the response status code was "200". if (response.getStatusCode() != 200) { System.err.println("ERROR: Unexpected response status code (" + response.getStatusCode() + ' ' + response.getResponseMessage() + ')'); return null; } // See if the response header included an optimizing job ID. If so, then // the job was scheduled successfully. If not, then there was a problem to // prevent it from being scheduled. String optimizingJobID = response.getHeader(Constants.SERVLET_PARAM_OPTIMIZING_JOB_ID); if (optimizingJobID == null) { System.err.println("ERROR: Unable to determine the optimizing job ID " + "from the response."); System.err.println("This probably means that the job was not scheduled " + "properly."); if (verboseMode) { System.err.println("The body of the HTTP response was:"); try { byte[] responseData = response.getResponseData(); BufferedReader reader = new BufferedReader(new InputStreamReader( new ByteArrayInputStream(responseData))); String line = reader.readLine(); while (line != null) { System.out.println(line); line = reader.readLine(); } reader.close(); } catch (Exception e) { System.err.println("Error decoding response data:"); e.printStackTrace(); } } return null; } else { System.out.println("Successfully scheduled optimizing job " + optimizingJobID + " using config file \"" + configFile + "\"."); return optimizingJobID; } } /** * Prints the provided message if the program is operating in verbose mode. * * @param message The message to be printed. */ public void debug(String message) { if (verboseMode) { System.out.println(message); } } /** * Displays usage information for this program. */ public static void displayUsage() { String eol = Constants.EOL; System.out.println( "USAGE: java -cp {classpath} CommandLineJobScheduler {options}" + eol + " where {options} for running a job include:" + eol + "-f {configFile} -- The configuration file to use to schedule the job" + eol + "-h {slamdHost} -- The address of the SLAMD server" + eol + "-p {slamdPort} -- The port of the SLAMD server's admin interface" + eol + "-A {authID} -- The username to use to authenticate to the server" + eol + "-P {authPW} -- The password to use to authenticate to the server" + eol + "-D {jobID} -- The job on which this job should be dependent" + eol + "-u {uri} -- The URI to which the request should be sent" + eol + "-S -- Communicate with the SLAMD server over SSL" + eol + "-d -- Make scheduled jobs interdependent" + eol + "-v -- Enable verbose mode" + eol + eol + " where {options} for generating a config file include:" + eol + "-g {className} -- Generate a config file for the specified job class" + eol + "-f {configFile} -- The configuration file to use to schedule the job" + eol + "-O -- Generate a config file for an optimizing job" + eol + "-o {className} -- Use the specified optimization algorithm" + eol + eol + " where {options} for displaying usage include:" + eol + "-H -- Display usage information and exit" ); } }