001: /*
002: * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: */
007: package winstone.testCase.load;
008:
009: import java.io.IOException;
010: import java.util.ArrayList;
011: import java.util.HashMap;
012: import java.util.Iterator;
013: import java.util.List;
014: import java.util.Map;
015:
016: import winstone.Logger;
017: import winstone.WebAppConfiguration;
018: import winstone.WinstoneResourceBundle;
019:
020: import com.meterware.httpunit.WebConversation;
021:
022: /**
023: * This class is an attempt to benchmark performance under load for winstone. It
024: * works by hitting a supplied URL with parallel threads (with keep-alives or
025: * without) at an escalating rate, and counting the no of failures.
026: *
027: * It uses HttpUnit's WebConversation class for the connection.
028: *
029: * @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
030: * @version $Id: LoadTest.java,v 1.2 2006/02/28 07:32:49 rickknowles Exp $
031: */
032: public class LoadTest {
033: private String url;
034: private boolean useKeepAlives;
035: private int startThreads;
036: private int endThreads;
037: private int stepSize;
038: private long stepPeriod;
039: private long gracePeriod;
040: private long successTimeTotal;
041: private int successCount;
042: private WinstoneResourceBundle resources;
043:
044: private static String LOCAL_RESOURCE_FILE = "winstone.testCase.load.LocalStrings";
045:
046: public LoadTest(WinstoneResourceBundle resources, String url,
047: boolean useKeepAlives, int startThreads, int endThreads,
048: int stepSize, long stepPeriod, long gracePeriod) {
049: this .resources = resources;
050: this .url = url;
051: this .useKeepAlives = useKeepAlives;
052: this .startThreads = startThreads;
053: this .endThreads = endThreads;
054: this .stepSize = stepSize;
055: this .stepPeriod = stepPeriod;
056: this .gracePeriod = gracePeriod;
057:
058: Logger.log(Logger.INFO, resources, "LoadTest.Config",
059: new String[] { this .url, this .useKeepAlives + "",
060: this .startThreads + "", this .endThreads + "",
061: this .stepSize + "", this .stepPeriod + "",
062: this .gracePeriod + "" });
063: }
064:
065: public void test() throws InterruptedException {
066: WebConversation wc = null;
067:
068: // Loop through in steps
069: for (int n = this .startThreads; n <= this .endThreads; n += this .stepSize) {
070: if (this .useKeepAlives)
071: wc = new WebConversation();
072:
073: // Spawn the threads
074: int noOfSeconds = (int) this .stepPeriod / 1000;
075: List threads = new ArrayList();
076: for (int m = 0; m < n; m++)
077: threads.add(new LoadTestThread(this .url, this ,
078: this .resources, wc, noOfSeconds - 1));
079:
080: // Sleep for step period
081: Thread.sleep(this .stepPeriod + gracePeriod);
082:
083: // int errorCount = (noOfSeconds * n) - this.successCount;
084: Long averageSuccessTime = this .successCount == 0 ? null
085: : new Long(this .successTimeTotal
086: / this .successCount);
087:
088: // Write out results
089: Logger.log(Logger.INFO, resources, "LoadTest.LineResult",
090: new String[] {
091: n + "",
092: this .successCount + "",
093: ((noOfSeconds * n) - this .successCount)
094: + "", averageSuccessTime + "" });
095:
096: // Close threads
097: for (Iterator i = threads.iterator(); i.hasNext();)
098: ((LoadTestThread) i.next()).destroy();
099:
100: this .successTimeTotal = 0;
101: this .successCount = 0;
102:
103: }
104: }
105:
106: public void incTimeTotal(long amount) {
107: this .successTimeTotal += amount;
108: }
109:
110: public void incSuccessCount() {
111: this .successCount++;
112: }
113:
114: public static void main(String args[]) throws Exception {
115: WinstoneResourceBundle resources = new WinstoneResourceBundle(
116: LOCAL_RESOURCE_FILE);
117:
118: // Loop for args
119: Map options = new HashMap();
120: // String operation = "";
121: for (int n = 0; n < args.length; n++) {
122: String option = args[n];
123: if (option.startsWith("--")) {
124: int equalPos = option.indexOf('=');
125: String paramName = option.substring(2,
126: equalPos == -1 ? option.length() : equalPos);
127: String paramValue = (equalPos == -1 ? "true" : option
128: .substring(equalPos + 1));
129: options.put(paramName, paramValue);
130: }
131: }
132:
133: if (options.size() == 0) {
134: printUsage(resources);
135: return;
136: }
137: Logger.setCurrentDebugLevel(Integer
138: .parseInt(WebAppConfiguration.stringArg(options,
139: "debug", "5")));
140:
141: String url = WebAppConfiguration.stringArg(options, "url",
142: "http://localhost:8080/");
143: boolean keepAlive = WebAppConfiguration.booleanArg(options,
144: "keepAlive", true);
145: String startThreads = WebAppConfiguration.stringArg(options,
146: "startThreads", "20");
147: String endThreads = WebAppConfiguration.stringArg(options,
148: "endThreads", "1000");
149: String stepSize = WebAppConfiguration.stringArg(options,
150: "stepSize", "20");
151: String stepPeriod = WebAppConfiguration.stringArg(options,
152: "stepPeriod", "5000");
153: String gracePeriod = WebAppConfiguration.stringArg(options,
154: "gracePeriod", "5000");
155:
156: LoadTest lt = new LoadTest(resources, url, keepAlive, Integer
157: .parseInt(startThreads), Integer.parseInt(endThreads),
158: Integer.parseInt(stepSize), Integer
159: .parseInt(stepPeriod), Integer
160: .parseInt(gracePeriod));
161:
162: lt.test();
163: }
164:
165: /**
166: * Displays the usage message
167: */
168: private static void printUsage(WinstoneResourceBundle resources)
169: throws IOException {
170: System.out.println(resources.getString("LoadTest.Usage"));
171: }
172:
173: }
|