1 package org.whatsitcalled.webflange.engine;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.File;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.StringWriter;
8 import java.util.ArrayList;
9 import java.util.Calendar;
10 import java.util.List;
11 import java.util.Properties;
12
13 import net.grinder.Grinder;
14
15 import org.apache.commons.io.FileUtils;
16 import org.apache.log4j.Logger;
17 import org.apache.velocity.VelocityContext;
18 import org.apache.velocity.app.Velocity;
19 import org.apache.velocity.exception.MethodInvocationException;
20 import org.apache.velocity.exception.ParseErrorException;
21 import org.apache.velocity.exception.ResourceNotFoundException;
22 import org.whatsitcalled.spring.SpringContextUtil;
23 import org.whatsitcalled.webflange.ResourceFactory;
24 import org.whatsitcalled.webflange.file.FileManager;
25 import org.whatsitcalled.webflange.model.LoadTest;
26 import org.whatsitcalled.webflange.model.LoadTestRun;
27 import org.whatsitcalled.webflange.model.LoadTestRunDAO;
28 import org.whatsitcalled.webflange.model.LoadTestSummary;
29 import org.whatsitcalled.webflange.model.LoadTestSummaryDAO;
30 import org.whatsitcalled.webflange.service.LoadTestService;
31
32
33
34
35 public class GrinderTestRunner {
36 private static final Logger LOGGER = Logger
37 .getLogger(GrinderTestRunner.class);
38
39 private FileManager fileManager;
40
41 private TestScheduler scheduler;
42
43 private LoadTestSummaryDAO summaryDAO;
44
45 private LoadTestRunDAO runDAO;
46
47 private LoadTestService loadTestService;
48
49 private List<String> grinderJars;
50
51 private VelocityContext velocityContext;
52
53 public void init() {
54
55
56 velocityContext = new VelocityContext();
57 String filePath = fileManager.getUploadFolder().getAbsolutePath();
58 filePath = filePath.replaceAll("\\\\", "/");
59 velocityContext.put("filePath", filePath);
60 try {
61 Velocity.init();
62 } catch (Exception e) {
63 LOGGER.error("Problem initializing Velocity", e);
64 }
65
66 }
67
68 public void run(LoadTest loadTest) {
69 loadTest.setRunning(true);
70 loadTestService.saveLoadTest(loadTest);
71
72 long time = Calendar.getInstance().getTimeInMillis();
73 saveRuntimeProperties(loadTest, loadTestService.getGrinderHostId(loadTest, time));
74
75 LOGGER.debug("Grinder CLASSPATH=" + System.getenv("CLASSPATH"));
76 LOGGER.debug("Grinder JAVA_OPTS=" + System.getenv("JAVA_OPTS"));
77
78 String path = fileManager.getPropertyFile(loadTest).getAbsolutePath();
79 String[] args = { path };
80 try {
81 LOGGER.debug("Executing test: " + loadTest.getName());
82 Grinder.main(args);
83 loadStats(loadTest.getId(), time);
84
85 } catch (Exception e) {
86 LOGGER.error("Execution of test: " + loadTest + " failed!", e);
87 } finally {
88 loadTest.setRunning(false);
89 loadTestService.saveLoadTest(loadTest);
90 }
91
92 }
93
94 public void saveRuntimeProperties(LoadTest loadTest, String grinderHostId) {
95
96 String path = fileManager.getPropertyFile(loadTest).getAbsolutePath();
97 File file = new File(path);
98 java.util.Properties props = new Properties();
99
100
101 StringWriter writer = new StringWriter();
102 try {
103 Velocity.evaluate(velocityContext, writer, "property-evaluator",
104 loadTest.getProperties());
105 String velOut = writer.toString();
106 LOGGER.debug("Properties after Velocity:" + velOut);
107 InputStream in = new ByteArrayInputStream(velOut.getBytes());
108 props.load(in);
109 props.putAll(scheduler.getProperties());
110
111
112 String libFolder = SpringContextUtil.getWebApplicationContext()
113 .getServletContext().getRealPath("/WEB-INF/lib");
114 LOGGER.debug("libFolder=" + libFolder);
115
116 StringBuffer classpathBuf = new StringBuffer();
117 for (String jar : grinderJars) {
118 LOGGER.debug("Adding jar: " + jar + " to grinder classpath.");
119 classpathBuf.append(libFolder);
120 classpathBuf.append("/");
121 classpathBuf.append(jar);
122 classpathBuf.append(System.getProperty( "path.separator" ));
123 }
124 String classpath = classpathBuf.toString();
125 LOGGER.debug("classpath=" + classpath);
126
127 props.setProperty("grinder.jvm.classpath", classpath);
128 props.setProperty("grinder.script", fileManager.getScriptFile(
129 loadTest.getScript()).getAbsolutePath());
130 props.setProperty("grinder.hostID", grinderHostId);
131 props.setProperty("grinder.logDirectory", fileManager
132 .getDataFolder().getAbsolutePath());
133 props.setProperty("grinder.logProcessStreams", "false");
134 props.store(FileUtils.openOutputStream(file), null);
135
136 } catch (ParseErrorException e) {
137 LOGGER.error("Unable to load runtime properties", e);
138 } catch (MethodInvocationException e) {
139 LOGGER.error("Unable to load runtime properties", e);
140 } catch (ResourceNotFoundException e) {
141 LOGGER.error("Unable to load runtime properties", e);
142 } catch (IOException e) {
143 LOGGER.error("Unable to load runtime properties", e);
144 }
145 }
146
147 public List<String> getContextVars() {
148 Object[] vars = velocityContext.getKeys();
149 List<String> varList = new ArrayList<String>();
150
151 for (int i = 0; i < vars.length; i++) {
152 String var = (String) vars[i];
153 varList.add(var);
154 }
155 return varList;
156 }
157
158 public static String[] splitLine(String line) {
159
160 String[] fields = new String[12];
161
162 int beginIndex = 0;
163 int endIndex = 13;
164 for (int i = 0; i < 11; i++) {
165 if (endIndex > line.length())
166 endIndex = line.length();
167 fields[i] = line.substring(beginIndex, endIndex);
168 LOGGER.debug("fields[" + i + "]: " + fields[i]);
169 fields[i] = fields[i].replaceAll("^\\s+", "");
170 fields[i] = fields[i].replaceAll("\\s+$", "");
171 if (fields[i].equals("?"))
172 fields[i] = "0";
173
174 beginIndex = endIndex;
175 endIndex = beginIndex + 13;
176 }
177
178 fields[11] = line.substring(beginIndex);
179
180 return fields;
181 }
182
183 public void loadStats(Long loadTestId, long time) {
184
185
186
187 LoadTest test = loadTestService.getLoadTest(loadTestId);
188
189
190 LoadTestRun run = new LoadTestRun(test, time);
191
192
193 setRunFiles(run);
194
195 File file = loadTestService.getSummaryFile(run);
196 List lines = null;
197 try {
198 lines = FileUtils.readLines(file);
199 } catch (IOException e) {
200 LOGGER.error("Trouble opening Grinder Summary File: "
201 + file.getAbsolutePath(), e);
202 }
203 String[] fields = new String[12];
204 LoadTestSummary sum = null;
205 for (Object line : lines) {
206 String s = (String) line;
207 fields = splitLine(s);
208 if (!fields[0].equals("") && !fields[10].equals("")) {
209 sum = new LoadTestSummary();
210 sum.setTestName(fields[0]);
211 sum.setTests(parseLong(fields[1]));
212 sum.setErrors(parseLong(fields[2]));
213 sum
214 .setMeanTestTime(parseDouble(fields[3]));
215 sum.setTestTimeStandardDeviation(parseFloat(fields[4]));
216 sum.setMeanResponseLength(parseDouble(fields[5]));
217 sum.setResponseBytesPerSecond(parseDouble(fields[6]));
218 sum.setResponseErrors(parseLong(fields[7]));
219 sum.setMeanTimeToResolveHost(parseDouble(fields[8]));
220 sum.setMeanTimeToEstablishConnection(parseDouble(fields[9]));
221 sum.setMeanTimeToFirstByte(parseDouble(fields[10]));
222 sum.setTestUri(fields[11]);
223 sum.setRun(run);
224 LOGGER.debug("Adding Summary...\n" + sum.toString());
225 if (sum.getTestName().equals("Totals")) {
226 sum.setSummaryType(LoadTestSummary.TOTALS_SUMMARY_TYPE);
227 }
228
229 run.getSummaries().add(sum);
230
231 }
232
233 }
234 test.getRuns().add(run);
235 loadTestService.saveLoadTest(test);
236 }
237
238 private long parseDouble(String sd) {
239 if (!sd.matches("[0-9\\.]*")) return 0;
240 long l = 0;
241 try {
242 l = Math.round(Double.parseDouble(sd));
243 } catch (NumberFormatException e) {
244 LOGGER.error(e);
245 }
246 return l;
247 }
248
249 private long parseFloat(String sf) {
250 long l = 0;
251 try {
252 l = Math.round(Float.parseFloat(sf));
253 } catch (NumberFormatException e) {
254 LOGGER.error(e);
255 }
256 return l;
257 }
258
259 private Long parseLong(String sd) {
260 Long d = new Long(0);
261 try {
262 d = Long.parseLong(sd);
263 } catch (NumberFormatException e) {
264 LOGGER.error(e);
265 }
266
267 return d;
268 }
269 private void setRunFiles(LoadTestRun run) {
270 File summaryFile = loadTestService.getSummaryFile(run);
271 if (summaryFile.exists())
272 run.setSummaryFile(summaryFile.getName());
273
274 File dataFile = loadTestService.getDataFile(run);
275 if (dataFile.exists())
276 run.setDataFile(dataFile.getName());
277
278 File errorFile = loadTestService.getErrorFile(run);
279 if (errorFile.exists())
280 run.setErrorFile(errorFile.getName());
281 }
282
283 public FileManager getFileManager() {
284 return fileManager;
285 }
286
287 public void setFileManager(FileManager fileManager) {
288 this.fileManager = fileManager;
289 }
290
291 public LoadTestRunDAO getRunDAO() {
292 return runDAO;
293 }
294
295 public void setRunDAO(LoadTestRunDAO runDAO) {
296 this.runDAO = runDAO;
297 }
298
299 public TestScheduler getScheduler() {
300 return scheduler;
301 }
302
303 public void setScheduler(TestScheduler scheduler) {
304 this.scheduler = scheduler;
305 }
306
307 public LoadTestSummaryDAO getSummaryDAO() {
308 return summaryDAO;
309 }
310
311 public void setSummaryDAO(LoadTestSummaryDAO summaryDAO) {
312 this.summaryDAO = summaryDAO;
313 }
314
315 public List getGrinderJars() {
316 return grinderJars;
317 }
318
319 public void setGrinderJars(List grinderJars) {
320 this.grinderJars = grinderJars;
321 }
322
323 public LoadTestService getLoadTestService() {
324 return loadTestService;
325 }
326
327 public void setLoadTestService(LoadTestService loadTestService) {
328 this.loadTestService = loadTestService;
329 }
330
331 public void runAsThread(LoadTest test) {
332
333 Runnable runnable = new RunNow(test);
334
335
336 Thread thread = new Thread(runnable);
337
338
339 thread.start();
340 }
341
342 class RunNow implements Runnable {
343 private LoadTest loadTest;
344
345 RunNow(LoadTest test) {
346 this.loadTest = test;
347
348 }
349
350 public void run() {
351 ResourceFactory.getGrinderTestRunner().run(loadTest);
352 }
353 }
354 }