/* * 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.jobs; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Date; import java.util.Random; import com.slamd.http.HTTPClient; import com.slamd.http.HTTPRequest; import com.slamd.http.HTTPResponse; import com.slamd.job.JobClass; import com.slamd.job.UnableToRunException; import com.slamd.parameter.BooleanParameter; import com.slamd.parameter.FileURLParameter; import com.slamd.parameter.IntegerParameter; import com.slamd.parameter.InvalidValueException; import com.slamd.parameter.MultiLineTextParameter; import com.slamd.parameter.Parameter; import com.slamd.parameter.ParameterList; import com.slamd.parameter.PasswordParameter; import com.slamd.parameter.PlaceholderParameter; import com.slamd.parameter.StringParameter; import com.slamd.stat.IncrementalTracker; import com.slamd.stat.StatTracker; import com.unboundid.util.FixedRateBarrier; /** * This class implements a SLAMD job class for performing repeated HTTP GET * requests against a Web server. All of the configuration for this job class * can be provided through parameters. * * * @author Neil A. Wilson */ public class HTTPGetRateJobClass extends JobClass { /** * The system property used to specify the location of the JSSE key store. */ public static final String SSL_KEY_STORE_PROPERTY = "javax.net.ssl.keyStore"; /** * The system property used to specify the password for the JSSE key store. */ public static final String SSL_KEY_PASSWORD_PROPERTY = "javax.net.ssl.keyStorePassword"; /** * The system property used to specify the location of the JSSE trust store. */ public static final String SSL_TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore"; /** * The system property used to specify the password for the JSSE trust store. */ public static final String SSL_TRUST_PASSWORD_PROPERTY = "javax.net.ssl.trustStorePassword"; /** * The display name for the stat tracker that will be used to track the number * of exceptions caught. */ public static final String STAT_TRACKER_EXCEPTIONS_CAUGHT = "Exceptions Caught"; // The parameter that indicates whether the client should trust any SSL cert. private BooleanParameter blindTrustParameter = new BooleanParameter("blind_trust", "Blindly Trust Any Certificate", "Indicates whether the client should blindly " + "trust any certificate presented by the server, " + "or whether the key and trust stores should be " + "used.", true); // The parameter that indicates whether to automatically follow any redirects // that are encountered. private BooleanParameter followRedirectsParameter = new BooleanParameter("follow_redirects", "Follow Redirects", "Indicates whether the client should " + "automatically follow any HTTP redirects that may " + "be returned while retrieving a URL.", true); // The parameter that indicates whether associated files should automatically // be retrieved. private BooleanParameter retrieveAssociatedFilesParameter = new BooleanParameter("retrieve_associated_files", "Retrieve Associated Files", "Indicates whether to automatically retrieve " + "associated files (e.g., images, frames, remote " + "stylesheets, etc.) when retrieving an HTML " + "document.", false); // The parameter that indicates whether to use a persistent connection. private BooleanParameter useKeepaliveParameter = new BooleanParameter("use_keepalive", "Use KeepAlive", "Indicates whether the KeepAlive feature should " + "be used to keep the connection open for " + "multiple requests.", false); // The parameter that specifies the URL to a file containing a list of URLs to // retrieve. private FileURLParameter urlFileParameter = new FileURLParameter("url_file", "File with URLs to Retrieve", "The URL to a file containing a list of URLs to " + "retrieve.", null, false); // The parameter that specifies the cool down time. private IntegerParameter coolDownTimeParameter = new IntegerParameter("cool_down_time", "Cool Down Time", "The length of time in seconds before the job " + "stops running that the client should stop " + "collecting statistics. Note that this cannot " + "always be accurate, particularly when no stop " + "time or duration is provided.", true, 0, true, 0, false, 0); // The parameter that specifies the number of iterations. private IntegerParameter iterationsParameter = new IntegerParameter("iterations", "Number of Iterations", "The number of requests that each thread should " + "issue to the server before completing (a value " + "less than zero indicates no limit.", true, -1, false, 0, false, 0); // The parameter that specifies the time between requests. private IntegerParameter delayParameter = new IntegerParameter("delay", "Time Between Requests (ms)", "The minimum length of time in milliseconds that " + "should pass between issuing one request and " + "issuing the next. Note that if it takes longer " + "than this length of time to process the request " + "then no sleep will be performed.", true, 0, true, 0, false, 0); // The parameter that specifies the maximum request rate. private IntegerParameter maxRateParameter = new IntegerParameter("maxRate", "Max Request Rate (Requests/Second/Client)", "Specifies the maximum request rate (in requests per second per " + "client) to attempt to maintain. If multiple clients are used, " + "then each client will attempt to maintain this rate. A value " + "less than or equal to zero indicates that the client should " + "attempt to perform requests as quickly as possible.", true, -1); // The parameter that specifies the port to use for the proxy server. private IntegerParameter proxyPortParameter = new IntegerParameter("proxy_port", "Proxy Server Port", "The port to use to communicate with the HTTP " + "proxy server.", false, 8080, true, 1, true, 65535); // The parameter that specifies the interval over which to enforce the maximum // request rate. private IntegerParameter rateLimitDurationParameter = new IntegerParameter( "maxRateDuration", "Max Rate Enforcement Interval (Seconds)", "Specifies the duration in seconds of the interval over which to " + "attempt to maintain the configured maximum rate. A value of " + "zero indicates that it should be equal to the statistics " + "collection interval. Large values may allow more variation but " + "may be more accurate over time. Small values can better " + "ensure that the rate doesn't exceed the requested level but may " + "be less able to achieve the desired rate.", true, 0, true,0, false, 0); // The parameter that specifies the warm up time. private IntegerParameter warmUpTimeParameter = new IntegerParameter("warm_up_time", "Warm Up Time", "The length of time in seconds before the job " + "stops running that the client should stop " + "collecting statistics. Note that this cannot " + "always be accurate, particularly when no stop " + "time or duration is provided.", true, 0, true, 0, false, 0); // The parameter that specifies the set of client addresses that may be used // for the job. private MultiLineTextParameter clientAddressesParameter = new MultiLineTextParameter("client_addresses", "Client Addresses", "Specifies a set of IP addresses that " + "should be used as the source address when " + "establishing the HTTP connections. Each " + "client thread will use a different client " + "address (although some threads may share " + "the same address if fewer addresses were " + "given than the number of client threads " + "requested). This option is only " + "available for use if the job will run " + "using only one client.", null, false); // The parameter that specifies the password for the SSL key store private PasswordParameter keyPWParameter = new PasswordParameter("sslkeypw", "SSL Key Store Password", "The password for the JSSE key store. This " + "only applies for HTTPS URLs.", false, ""); // The parameter that specifies the password to use to authenticate to the // proxy server. private PasswordParameter proxyUserPWParameter = new PasswordParameter("proxy_user_pw", "Proxy User Password", "The password to use to authenticate to the " + "proxy server if one is required.", false, ""); // The parameter that specifies the password for the SSL key store private PasswordParameter trustPWParameter = new PasswordParameter("ssltrustpw", "SSL Trust Store Password", "The password for the JSSE trust store. This " + "only applies for HTTPS URLs.", false, ""); // A placeholder parameter that is only used for formatting. private PlaceholderParameter placeholder = new PlaceholderParameter(); // The parameter that specifies the location of the SSL key store private StringParameter keyStoreParameter = new StringParameter("sslkeystore", "SSL Key Store", "The path to the JSSE key store to use for an " + "SSL-based connection. This only applies for " + "HTTPS URLs.", false, ""); // The parameter that specifies the address of the proxy server to use. private StringParameter proxyHostParameter = new StringParameter("proxy_host", "Proxy Server Address", "The address of the HTTP proxy server to use if " + "one is required.", false, ""); // The parameter that specifies the user ID to use to authenticate to the // proxy server. private StringParameter proxyUserIDParameter = new StringParameter("proxy_user_id", "Proxy User ID", "The user ID to use to authenticate to the proxy " + "server if one is required.", false, ""); // The parameter that specifies the location of the SSL trust store private StringParameter trustStoreParameter = new StringParameter("ssltruststore", "SSL Trust Store", "The path to the JSSE trust store to use for an " + "SSL-based connection. This only applies for " + "HTTPS URLs.", false, ""); // The parameter that provides the URL to retrieve. private StringParameter urlParameter = new StringParameter("url", "URL To Retrieve", "The URL of the page to be retrieved.", false, ""); // Instance variables that correspond to the parameter values. private static boolean blindTrust; private static boolean followRedirects; private static boolean retrieveAssociatedFiles; private static boolean useKeepAlive; private static HTTPRequest[] requests; private static int coolDownTime; private static int numIterations; private static int proxyPort; private static int timeBetweenRequests; private static int warmUpTime; private static String proxyHost; private static String proxyUserID; private static String proxyUserPW; private static String sslKeyStore; private static String sslKeyStorePassword; private static String sslTrustStore; private static String sslTrustStorePassword; private static String[] clientAddresses; // The rate limiter for this job. private static FixedRateBarrier rateLimiter; // The HTTP client that will be used by this thread to actually handle the // requests. private HTTPClient httpClient; // The stat tracker that will be used to track the number of exceptions // caught. private IncrementalTracker exceptionsCaught; // A random number generator to use for the client and the thread. private static Random parentRandom; private Random random; /** * Creates a new instance of this HTTP GetRate job class. */ public HTTPGetRateJobClass() { super(); } /** * {@inheritDoc} */ @Override() public String getJobName() { return "HTTP GetRate"; } /** * {@inheritDoc} */ @Override() public String getShortDescription() { return "Issue repeated HTTP GET requests against a web server"; } /** * {@inheritDoc} */ @Override() public String[] getLongDescription() { return new String[] { "This job can be used to issue repeated HTTP GET requests to a web " + "server to retrieve one or more URLs." }; } /** * {@inheritDoc} */ @Override() public String getJobCategoryName() { return "HTTP"; } /** * {@inheritDoc} */ @Override() public ParameterList getParameterStubs() { Parameter[] parameters = new Parameter[] { placeholder, urlParameter, urlFileParameter, useKeepaliveParameter, followRedirectsParameter, retrieveAssociatedFilesParameter, placeholder, proxyHostParameter, proxyPortParameter, proxyUserIDParameter, proxyUserPWParameter, placeholder, warmUpTimeParameter, coolDownTimeParameter, iterationsParameter, delayParameter, maxRateParameter, rateLimitDurationParameter, clientAddressesParameter, placeholder, blindTrustParameter, keyStoreParameter, keyPWParameter, trustStoreParameter, trustPWParameter }; return new ParameterList(parameters); } /** * {@inheritDoc} */ @Override() public StatTracker[] getStatTrackerStubs(String clientID, String threadID, int collectionInterval) { StatTracker[] clientStubs = new HTTPClient().getStatTrackerStubs(clientID, threadID, collectionInterval); StatTracker[] stubs = new StatTracker[clientStubs.length+1]; System.arraycopy(clientStubs, 0, stubs, 0, clientStubs.length); stubs[clientStubs.length] = new IncrementalTracker(clientID, threadID, STAT_TRACKER_EXCEPTIONS_CAUGHT, collectionInterval); return stubs; } /** * {@inheritDoc} */ @Override() public StatTracker[] getStatTrackers() { StatTracker[] clientStats = httpClient.getStatTrackers(); StatTracker[] stats = new StatTracker[clientStats.length+1]; System.arraycopy(clientStats, 0, stats, 0, clientStats.length); stats[clientStats.length] = exceptionsCaught; return stats; } /** * {@inheritDoc} */ @Override() public void validateJobInfo(int numClients, int threadsPerClient, int threadStartupDelay, Date startTime, Date stopTime, int duration, int collectionInterval, ParameterList parameters) throws InvalidValueException { // See if the user has provided either a single URL or a file with a list of // URLs. StringParameter urlParam = parameters.getStringParameter(urlParameter.getName()); FileURLParameter urlFileParam = parameters.getFileURLParameter(urlFileParameter.getName()); if (((urlParam == null) || (! urlParam.hasValue())) && ((urlFileParam == null) || (! urlFileParam.hasValue()))) { throw new InvalidValueException("A value must be provided for either " + "the \"" + urlParameter.getDisplayName() + "\" parameter or the \"" + urlFileParameter.getDisplayName() + "\" parameter."); } if ((urlParam != null) && urlParam.hasValue() && (urlFileParam != null) && urlFileParam.hasValue()) { throw new InvalidValueException("You may not provide a value for both " + "the \"" + urlParameter.getDisplayName() + "\" parameter and the \"" + urlFileParameter.getDisplayName() + "\" parameter."); } if ((urlParam != null) && urlParam.hasValue()) { try { URL url = new URL(urlParam.getStringValue()); } catch (Exception e) { throw new InvalidValueException("Unable to parse the provided URL \"" + urlParam.getStringValue() + "\": " + e, e); } } if (numClients > 1) { MultiLineTextParameter clientAddrsParam = parameters.getMultiLineTextParameter( clientAddressesParameter.getName()); if ((clientAddrsParam != null) && clientAddrsParam.hasValue()) { throw new InvalidValueException("The set of client addresses may " + "only be specified if a single client" + "is to be used."); } } } /** * {@inheritDoc} */ @Override() public boolean providesParameterTest() { return true; } /** * {@inheritDoc} */ @Override() public boolean testJobParameters(ParameterList parameters, ArrayList<String> outputMessages) { // Get the necessary parameter values. URL urlToRetrieve = null; StringParameter urlParam = parameters.getStringParameter(urlParameter.getName()); if ((urlParam != null) && urlParam.hasValue()) { try { urlToRetrieve = new URL(urlParam.getStringValue()); } catch (Exception e) { outputMessages.add("ERROR: Unable to parse URL to retrieve '" + urlParam.getStringValue() + "': " + stackTraceToString(e)); return false; } } else { FileURLParameter urlFileParam = parameters.getFileURLParameter(urlFileParameter.getName()); if ((urlFileParam == null) || (! urlFileParam.hasValue())) { outputMessages.add("ERROR: Either a URL to retrieve or a URL file " + "must be provided."); return false; } else { try { String[] urls = urlFileParam.getNonBlankFileLines(); if ((urls == null) || (urls.length == 0)) { outputMessages.add("ERROR: The file containing the URLs to " + "retrieve, '" + urlFileParam.getFileURL().toExternalForm() + "', appears to be empty."); return false; } else { try { urlToRetrieve = new URL(urls[0]); } catch (Exception e) { outputMessages.add("ERROR: Unable to parse URL to retrieve '" + urls[0] + "': " + stackTraceToString(e)); return false; } } } catch (Exception e) { outputMessages.add("ERROR: Unable to retrieve the URL file '" + urlFileParam.getFileURL().toExternalForm() + "': " + stackTraceToString(e)); } } } String proxyHost = null; StringParameter proxyHostParam = parameters.getStringParameter(proxyHostParameter.getName()); if ((proxyHostParam != null) && proxyHostParam.hasValue()) { proxyHost = proxyHostParam.getStringValue(); } int proxyPort = -1; IntegerParameter proxyPortParam = parameters.getIntegerParameter(proxyPortParameter.getName()); if ((proxyPortParam != null) && proxyPortParam.hasValue()) { proxyPort = proxyPortParam.getIntValue(); } String proxyUserID = null; StringParameter proxyUserIDParam = parameters.getStringParameter(proxyUserIDParameter.getName()); if ((proxyUserIDParam != null) && proxyUserIDParam.hasValue()) { proxyUserID = proxyUserIDParam.getStringValue(); } String proxyUserPW = null; PasswordParameter proxyUserPWParam = parameters.getPasswordParameter(proxyUserPWParameter.getName()); if ((proxyUserPWParam != null) && proxyUserPWParam.hasValue()) { proxyUserPW = proxyUserPWParam.getStringValue(); } boolean followRedirects = false; BooleanParameter redirectsParam = parameters.getBooleanParameter(followRedirectsParameter.getName()); if (redirectsParam != null) { followRedirects = redirectsParam.getBooleanValue(); } boolean blindTrust = true; BooleanParameter blindTrustParam = parameters.getBooleanParameter(blindTrustParameter.getName()); if (blindTrustParam != null) { blindTrust = blindTrustParam.getBooleanValue(); } String keyStore = null; StringParameter keyStoreParam = parameters.getStringParameter(keyStoreParameter.getName()); if ((keyStoreParam != null) && keyStoreParam.hasValue()) { keyStore = keyStoreParam.getStringValue(); File keyStoreFile = new File(keyStore); if ((! blindTrust) && (! keyStoreFile.exists())) { outputMessages.add("WARNING: Key store file \"" + keyStore + "\" not found on SLAMD server system. This test " + "will blindly trust any SSL certificate " + "presented by the directory server."); outputMessages.add(""); blindTrust = true; } else { System.setProperty(SSL_KEY_STORE_PROPERTY, keyStore); } } String keyStorePassword = ""; StringParameter keyPassParam = parameters.getStringParameter(keyPWParameter.getName()); if ((keyPassParam != null) && keyPassParam.hasValue()) { keyStorePassword = keyPassParam.getStringValue(); System.setProperty(SSL_KEY_PASSWORD_PROPERTY, keyStorePassword); } String trustStore = null; StringParameter trustStoreParam = parameters.getStringParameter(trustStoreParameter.getName()); if ((trustStoreParam != null) && trustStoreParam.hasValue()) { trustStore = trustStoreParam.getStringValue(); File trustStoreFile = new File(trustStore); if ((! blindTrust) && (! trustStoreFile.exists())) { outputMessages.add("WARNING: trust store file \"" + trustStore + "\" not found on SLAMD server system. This test " + "will blindly trust any SSL certificate " + "presented by the directory server."); outputMessages.add(""); blindTrust = true; } else { System.setProperty(SSL_TRUST_STORE_PROPERTY, trustStore); } } String trustStorePassword = ""; StringParameter trustPassParam = parameters.getStringParameter(trustPWParameter.getName()); if ((trustPassParam != null) && trustPassParam.hasValue()) { trustStorePassword = trustPassParam.getStringValue(); System.setProperty(SSL_TRUST_PASSWORD_PROPERTY, trustStorePassword); } // Create an HTTPClient to use to retrieve the URL. HTTPClient client = new HTTPClient(); client.setFollowRedirects(followRedirects); if ((proxyHost != null) && (proxyPort > 0)) { client.enableProxy(proxyHost, proxyPort, proxyUserID, proxyUserPW); } if (blindTrust) { try { client.setSSLSocketFactory(new JSSEBlindTrustSocketFactory()); } catch (Exception e) { outputMessages.add("ERROR: Unable to instantiate blind trust socket " + "factory: " + stackTraceToString(e)); return false; } } // Construct the request and send it to the server. try { outputMessages.add("Attempting to retrieve URL '" + urlToRetrieve.toExternalForm() + "'...."); HTTPRequest request = new HTTPRequest(true, urlToRetrieve); HTTPResponse response = client.sendRequest(request); int statusCode = response.getStatusCode(); String responseMessage = response.getResponseMessage(); outputMessages.add("HTTP response status code was " + statusCode + '.'); if ((responseMessage != null) && (responseMessage.length() > 0)) { outputMessages.add("HTTP response message was " + responseMessage + '.'); } outputMessages.add(""); outputMessages.add("All tests completed."); client.closeAll(); return true; } catch (Exception e) { outputMessages.add("ERROR: Unable to perform the GET: " + stackTraceToString(e)); client.closeAll(); return false; } } /** * {@inheritDoc} */ @Override() public void initializeClient(String clientID, ParameterList parameters) throws UnableToRunException { urlParameter = parameters.getStringParameter(urlParameter.getName()); if ((urlParameter != null) && urlParameter.hasValue()) { try { HTTPRequest request = new HTTPRequest(true, new URL(urlParameter.getStringValue())); requests = new HTTPRequest[] { request }; } catch (Exception e) { throw new UnableToRunException("Unable to create HTTP request based " + "on provided URL \"" + urlParameter.getStringValue() + "\": " + e, e); } } urlFileParameter = parameters.getFileURLParameter(urlFileParameter.getName()); if ((urlFileParameter != null) && urlFileParameter.hasValue()) { try { String[] urls = urlFileParameter.getFileLines(); requests = new HTTPRequest[urls.length]; for (int i=0; i < requests.length; i++) { requests[i] = new HTTPRequest(true, new URL(urls[i])); } } catch (Exception e) { throw new UnableToRunException("Unable to obtain or parse the set of " + "URLs to retrieve: " + e, e); } } // See if we should use HTTP keepalive. useKeepAlive = false; useKeepaliveParameter = parameters.getBooleanParameter(useKeepaliveParameter.getName()); if (useKeepaliveParameter != null) { useKeepAlive = useKeepaliveParameter.getBooleanValue(); } // See if we should follow redirects. followRedirects = true; followRedirectsParameter = parameters.getBooleanParameter(followRedirectsParameter.getName()); if (followRedirectsParameter != null) { followRedirects = followRedirectsParameter.getBooleanValue(); } // See if we should automatically retrieve associated files. retrieveAssociatedFiles = false; retrieveAssociatedFilesParameter = parameters.getBooleanParameter( retrieveAssociatedFilesParameter.getName()); if (retrieveAssociatedFilesParameter != null) { retrieveAssociatedFiles = retrieveAssociatedFilesParameter.getBooleanValue(); } // See if we should use a proxy host. proxyHost = null; proxyHostParameter = parameters.getStringParameter(proxyHostParameter.getName()); if ((proxyHostParameter != null) && proxyHostParameter.hasValue()) { proxyHost = proxyHostParameter.getStringValue(); } // Get the port for the proxy server. proxyPort = -1; proxyPortParameter = parameters.getIntegerParameter(proxyPortParameter.getName()); if ((proxyPortParameter != null) && proxyPortParameter.hasValue()) { proxyPort = proxyPortParameter.getIntValue(); } // Get the user ID to use to authenticate to the proxy server. proxyUserID = null; proxyUserIDParameter = parameters.getStringParameter(proxyUserIDParameter.getName()); if ((proxyUserIDParameter != null) && proxyUserIDParameter.hasValue()) { proxyUserID = proxyUserIDParameter.getStringValue(); } // Get the password to use to authenticate to the proxy server. proxyUserPW = null; proxyUserPWParameter = parameters.getPasswordParameter(proxyUserPWParameter.getName()); if ((proxyUserPWParameter != null) && proxyUserPWParameter.hasValue()) { proxyUserPW = proxyUserPWParameter.getStringValue(); } // Get the warm up time. warmUpTime = 0; warmUpTimeParameter = parameters.getIntegerParameter(warmUpTimeParameter.getName()); if ((warmUpTimeParameter != null) && warmUpTimeParameter.hasValue()) { warmUpTime = warmUpTimeParameter.getIntValue(); } // Get the cool down time. coolDownTime = 0; coolDownTimeParameter = parameters.getIntegerParameter(coolDownTimeParameter.getName()); if ((coolDownTimeParameter != null) && coolDownTimeParameter.hasValue()) { coolDownTime = coolDownTimeParameter.getIntValue(); } // Get the time between requests. timeBetweenRequests = 0; delayParameter = parameters.getIntegerParameter(delayParameter.getName()); if ((delayParameter != null) && delayParameter.hasValue()) { timeBetweenRequests = delayParameter.getIntValue(); } rateLimiter = null; maxRateParameter = parameters.getIntegerParameter(maxRateParameter.getName()); if ((maxRateParameter != null) && maxRateParameter.hasValue()) { int maxRate = maxRateParameter.getIntValue(); if (maxRate > 0) { int rateIntervalSeconds = 0; rateLimitDurationParameter = parameters.getIntegerParameter( rateLimitDurationParameter.getName()); if ((rateLimitDurationParameter != null) && rateLimitDurationParameter.hasValue()) { rateIntervalSeconds = rateLimitDurationParameter.getIntValue(); } if (rateIntervalSeconds <= 0) { rateIntervalSeconds = getClientSideJob().getCollectionInterval(); } rateLimiter = new FixedRateBarrier(rateIntervalSeconds * 1000L, maxRate * rateIntervalSeconds); } } // Get the number of iterations. numIterations = -1; iterationsParameter = parameters.getIntegerParameter(iterationsParameter.getName()); if ((iterationsParameter != null) && iterationsParameter.hasValue()) { numIterations = iterationsParameter.getIntValue(); } // Get the set of client addresses. clientAddresses = new String[0]; clientAddressesParameter = parameters.getMultiLineTextParameter( clientAddressesParameter.getName()); if ((clientAddressesParameter != null) && clientAddressesParameter.hasValue()) { clientAddresses = clientAddressesParameter.getNonBlankLines(); } // Get the arguments related to SSL usage. blindTrustParameter = parameters.getBooleanParameter(blindTrustParameter.getName()); if (blindTrustParameter != null) { blindTrust = blindTrustParameter.getBooleanValue(); } sslKeyStore = null; keyStoreParameter = parameters.getStringParameter(keyStoreParameter.getName()); if ((keyStoreParameter != null) && keyStoreParameter.hasValue()) { sslKeyStore = keyStoreParameter.getStringValue(); System.setProperty(SSL_KEY_STORE_PROPERTY, sslKeyStore); } sslKeyStorePassword = null; keyPWParameter = parameters.getPasswordParameter(keyPWParameter.getName()); if ((keyPWParameter != null) && keyPWParameter.hasValue()) { sslKeyStorePassword = keyPWParameter.getStringValue(); System.setProperty(SSL_KEY_PASSWORD_PROPERTY, sslKeyStorePassword); } sslTrustStore = null; trustStoreParameter = parameters.getStringParameter(trustStoreParameter.getName()); if ((trustStoreParameter != null) && trustStoreParameter.hasValue()) { sslTrustStore = trustStoreParameter.getStringValue(); System.setProperty(SSL_TRUST_STORE_PROPERTY, sslTrustStore); } sslTrustStorePassword = null; trustPWParameter = parameters.getPasswordParameter(trustPWParameter.getName()); if ((trustPWParameter != null) && trustPWParameter.hasValue()) { sslTrustStorePassword = trustPWParameter.getStringValue(); System.setProperty(SSL_TRUST_PASSWORD_PROPERTY, sslTrustStorePassword); } parentRandom = new Random(); } /** * {@inheritDoc} */ @Override() public void initializeThread(String clientID, String threadID, int collectionInterval, ParameterList parameters) throws UnableToRunException { // Initialize the random number generator for this client. random = new Random(parentRandom.nextLong()); // Initialize the exceptionsCaught tracker. exceptionsCaught = new IncrementalTracker(clientID, threadID, STAT_TRACKER_EXCEPTIONS_CAUGHT, collectionInterval); // Create and initialize the HTTP client that we will use for handling the // requests. httpClient = new HTTPClient(); httpClient.setFollowRedirects(followRedirects); httpClient.setRetrieveAssociatedFiles(retrieveAssociatedFiles); httpClient.setUseKeepAlive(useKeepAlive); if ((clientAddresses != null) && (clientAddresses.length > 0)) { int slot = getThreadNumber() % clientAddresses.length; try { httpClient.setClientAddress(clientAddresses[slot]); } catch (Exception e) { throw new UnableToRunException("Unable to set the client address for " + "thread " + threadID + " to " + clientAddresses[slot] + ": " + e, e); } } if ((proxyHost != null) && (proxyPort > 0)) { httpClient.enableProxy(proxyHost, proxyPort, proxyUserID, proxyUserPW); } if (blindTrust) { try { httpClient.setSSLSocketFactory(new JSSEBlindTrustSocketFactory()); } catch (Exception e) { throw new UnableToRunException("Unable to use blind trust socket " + "factory: " + e); } } } /** * {@inheritDoc} */ @Override() public void runJob() { // Determine the range of time for which we should collect statistics. long currentTime = System.currentTimeMillis(); boolean collectingStats = false; long startCollectingTime = currentTime + (1000 * warmUpTime); long stopCollectingTime = Long.MAX_VALUE; if ((coolDownTime > 0) && (getShouldStopTime() > 0)) { stopCollectingTime = getShouldStopTime() - (1000 * coolDownTime); } // First, see if this should operate "infinitely" (i.e., not a fixed number // of iterations boolean infinite = (numIterations <= 0); // Create a variable that will be used to handle the delay between requests. long requestStartTime = 0; // Create a loop that will run until it needs to stop for (int i=0; ((! shouldStop()) && ((infinite || (i < numIterations)))); i++) { if (rateLimiter != null) { if (rateLimiter.await()) { continue; } } currentTime = System.currentTimeMillis(); if ((! collectingStats) && (currentTime >= startCollectingTime) && (currentTime < stopCollectingTime)) { // Tell the stat trackers that they should start tracking now httpClient.enableStatisticsCollection(getClientID(), getThreadID(), getCollectionInterval(), getJobID(), getStatReporter()); exceptionsCaught.startTracker(); collectingStats = true; } else if ((collectingStats) && (currentTime >= stopCollectingTime)) { httpClient.stopTrackers(); exceptionsCaught.stopTracker(); collectingStats = false; } if (timeBetweenRequests > 0) { requestStartTime = System.currentTimeMillis(); } try { HTTPResponse response = httpClient.sendRequest(getRandomRequest()); } catch (Exception e) { if (collectingStats) { exceptionsCaught.increment(); } } if (timeBetweenRequests > 0) { long requestStopTime = System.currentTimeMillis(); long eTime = requestStopTime - requestStartTime; long sleepTime = timeBetweenRequests - eTime; if (sleepTime > 0) { try { Thread.sleep(sleepTime); } catch (InterruptedException ie) {} } } } if (collectingStats) { httpClient.stopTrackers(); exceptionsCaught.stopTracker(); collectingStats = false; } httpClient.closeAll(); } /** * {@inheritDoc} */ @Override() public void destroyThread() { if (httpClient != null) { httpClient.closeAll(); } } /** * Retrieves the next request that should be sent. * * @return The next request that should be sent. */ public HTTPRequest getRandomRequest() { if (requests.length == 1) { return requests[0]; } else { int position = (random.nextInt() & 0x7FFFFFFF) % requests.length; return requests[position]; } } }