Author: [email protected]
Date: Thu Mar 17 09:32:26 2011
New Revision: 891

Log:
[AMDATU-332] Initial version of automated performance test, based on JMeter

Added:
   trunk/etc/performancetest/
   trunk/etc/performancetest/pom.xml
   trunk/etc/performancetest/src/
   trunk/etc/performancetest/src/main/
   trunk/etc/performancetest/src/main/java/
   trunk/etc/performancetest/src/main/java/org/
   trunk/etc/performancetest/src/main/java/org/amdatu/
   trunk/etc/performancetest/src/main/java/org/amdatu/test/
   trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/
   
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/Utils.java
   trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/
   
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/JMeterPlanReport.java
   
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/JMeterResultsParser.java
   
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ReportSummary.java
   
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/Statistics.java
   
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/XYSample.java
   
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ZSamples.java
   trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/main/
   
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/main/Main.java
   trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/
   
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/AmdatuLauncher.java
   
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/JMeterRunner.java
   trunk/etc/performancetest/src/main/resources/
   trunk/etc/performancetest/src/main/resources/jakarta-jmeter-2.4.zip   
(contents, props changed)

Added: trunk/etc/performancetest/pom.xml
==============================================================================
--- (empty file)
+++ trunk/etc/performancetest/pom.xml   Thu Mar 17 09:32:26 2011
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.amdatu</groupId>
+    <artifactId>org.amdatu.core</artifactId>
+    <version>0.2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.amdatu.test</groupId>
+  <artifactId>performance</artifactId>
+  <packaging>jar</packaging>
+  <name>Amdatu - Automated performance test</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>2.0.1</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-math</groupId>
+      <artifactId>commons-math</artifactId>
+      <version>1.2</version>
+      <scope>compile</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifest>
+              <addClasspath>true</addClasspath>
+              <mainClass>org.amdatu.test.performance.Main</mainClass>
+            </manifest>
+          </archive>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>

Added: 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/Utils.java
==============================================================================
--- (empty file)
+++ 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/Utils.java  
    Thu Mar 17 09:32:26 2011
@@ -0,0 +1,128 @@
+/*
+ Copyright (C) 2010 Amdatu.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.test.performance;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.apache.commons.io.IOUtils;
+
+public class Utils {
+    // Private constant for buffer size.
+    private static final int BUFFER_SIZE = 40 * 1024; // Blocks of 40 Kb each
+    
+    /**
+     * Unzips the file to the specified target directory.
+     * @param zipFile The zipfile to unzip
+     * @param targetDir The directory in which the resulting files should be 
Stored
+     * @exception IOException If not all files could be saved
+     */
+    public static void unzip(File zipFile, File targetDir) throws IOException {
+        // Create the buffer for streaming the contents
+
+        // Create the zip input stream
+        ZipInputStream zip = null;
+        try {
+            zip = new ZipInputStream(new BufferedInputStream(new 
FileInputStream(zipFile), BUFFER_SIZE));
+            // Process each entry
+            ZipEntry entry;
+            while ((entry = zip.getNextEntry()) != null) {
+                // Get and normalize the path
+                String path = entry.getName();
+                path = path.replace('/', File.separatorChar);
+                path = path.replace('\\', File.separatorChar);
+                File target = new File(targetDir, path);
+                // Check whether the target is a file or directory
+                if (entry.isDirectory()) {
+                    target.mkdirs();
+                } else {
+                    writeZipEntryToFile(zip, target);
+                }
+
+                // Set the last modified to the date specified in the zip
+                long time = entry.getTime();
+                if (time != -1) {
+                    target.setLastModified(time);
+                }
+            }
+        } finally {
+            if (zip != null) {
+                zip.close();
+            }
+        }
+    }
+
+    /**
+     * Writes the content of the zip entry to the specified file.
+     * @param zip The zip entry to write
+     * @param target The file to write to
+     * @throws IOException In case a IO exception occurs
+     */
+    public static void writeZipEntryToFile(ZipInputStream zip, File target) 
throws IOException {
+        // Create the parent directory
+        File parent = target.getParentFile();
+        if (!parent.exists()) {
+            parent.mkdirs();
+        }
+        // Stream the contents to the target file
+        int bytes;
+        byte[] buffer = new byte[BUFFER_SIZE];
+        OutputStream out = null;
+        try {
+            out = new BufferedOutputStream(new FileOutputStream(target), 
BUFFER_SIZE);
+            while ((bytes = zip.read(buffer, 0, buffer.length)) != -1) {
+                out.write(buffer, 0, bytes);
+            }
+        } catch (IOException ex) {
+            throw ex;
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+    
+    public static void dispatchOutput(final Process process) {
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    IOUtils.copy(process.getErrorStream(), System.err);     
+                } catch (IOException e) {
+                    System.err.println(e.toString());
+                    e.printStackTrace();
+                } 
+            } 
+        }).start();
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    IOUtils.copy(process.getInputStream(), System.out);
+                } catch (IOException e) {
+                    System.err.println(e.toString());
+                    e.printStackTrace();
+                }
+            } 
+        }).start();
+    }
+}

Added: 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/JMeterPlanReport.java
==============================================================================
--- (empty file)
+++ 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/JMeterPlanReport.java
  Thu Mar 17 09:32:26 2011
@@ -0,0 +1,44 @@
+package org.amdatu.test.performance.analysis;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class JMeterPlanReport {
+    private List<ZSamples> m_samples = new ArrayList<ZSamples>();
+    private String m_name;
+    
+    private double m_throughputX;
+    private double m_throughputY;
+    
+    public JMeterPlanReport(String name) {
+        m_name = name;
+    }
+    
+    public void addSampleResult(ZSamples sample) {
+        m_samples.add(sample);
+    }
+    
+    /**
+     * Throughput can only be calculates over all samples in a single report
+     */
+    public void setThroughput(double x, double y) {
+        m_throughputX = x;
+        m_throughputY = y;
+    }
+    
+    public String getName() {
+        return m_name;
+    }
+    
+    public double getThroughputX() {
+        return m_throughputX;
+    }
+    
+    public double getThroughputY() {
+        return m_throughputY;
+    }
+    
+    public List<ZSamples> getSamples() {
+        return m_samples;
+    }
+}

Added: 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/JMeterResultsParser.java
==============================================================================
--- (empty file)
+++ 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/JMeterResultsParser.java
       Thu Mar 17 09:32:26 2011
@@ -0,0 +1,84 @@
+/*
+ Copyright (C) 2010 Amdatu.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.test.performance.analysis;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Parses the results of a JMeter execution
+ * @author ivol
+ *
+ */
+public class JMeterResultsParser extends DefaultHandler {
+    // Name of the JMeter plan
+    private String m_jmeterPlanName;
+    
+    private Map<String, List<XYSample>> m_results = new HashMap<String, 
List<XYSample>>();
+    
+    private double m_sampleCount;
+    private long m_tsMin = System.currentTimeMillis();
+    private long m_tsMax = 0;
+    
+    public JMeterResultsParser(String name) {
+        m_jmeterPlanName = name;
+    }
+    
+    public String getName() {
+        return m_jmeterPlanName;
+    }
+    
+    public double getThroughput() {
+        long diff = m_tsMax - m_tsMin;
+        double throughput = m_sampleCount/(diff/1000.0);
+        return throughput;
+    }
+    
+    @Override
+    public void startElement(String uri, String localName, String qName, 
Attributes attributes) throws SAXException {
+        if ("httpSample".equals(qName)) {
+            String stepName = attributes.getValue("lb");
+            boolean success = 
"true".equalsIgnoreCase(attributes.getValue("s"));
+            long timeStamp = Long.parseLong(attributes.getValue("ts"));
+            m_tsMin = Math.min(m_tsMin, timeStamp);
+            m_tsMax = Math.max(m_tsMax, timeStamp);
+            int responseTime = Integer.parseInt(attributes.getValue("t"));
+            m_sampleCount++;
+            
+            XYSample result = new XYSample();
+            result.name = stepName;
+            result.responseTime = responseTime;
+            result.timeStamp = timeStamp;
+            result.success = success;
+
+            if (!m_results.containsKey(stepName)) {
+                m_results.put(stepName, new ArrayList<XYSample>());
+            }
+            m_results.get(stepName).add(result);   
+        }
+    }
+    
+    public Map<String, List<XYSample>> getResults() {
+        return m_results;
+    }
+}

Added: 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ReportSummary.java
==============================================================================
--- (empty file)
+++ 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ReportSummary.java
     Thu Mar 17 09:32:26 2011
@@ -0,0 +1,179 @@
+/*
+ Copyright (C) 2010 Amdatu.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.test.performance.analysis;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.math.MathException;
+
+/**
+ * This class represents the overall analysis report.
+ * 
+ * @author ivol
+ */
+public class ReportSummary {
+    private String m_resultDir;
+    private List<JMeterPlanReport> m_reports = new 
ArrayList<JMeterPlanReport>();
+    
+    public ReportSummary(String resultDir) {
+        m_resultDir = resultDir;
+    }
+    
+    public void mergeAndAdd(JMeterResultsParser x, JMeterResultsParser y) 
throws IOException, MathException {
+        JMeterPlanReport report = new JMeterPlanReport(x.getName());
+        Map<String, List<XYSample>> resultsX = x.getResults();
+        Map<String, List<XYSample>> resultsY = y.getResults();
+        for (String key : resultsX.keySet()) {
+            List<XYSample> samplesX = resultsX.get(key);
+            List<XYSample> samplesY = resultsY.get(key);
+            ZSamples mergedReport = new ZSamples(m_resultDir, samplesX, 
samplesY);
+            report.addSampleResult(mergedReport);
+        }
+        report.setThroughput(x.getThroughput(), y.getThroughput());
+        m_reports.add(report);
+    }
+
+    public void toConsole() {
+        System.out.println("------------------------");
+        System.out.println("Performance test results");
+        System.out.println("------------------------\n");
+
+        for (JMeterPlanReport report : m_reports) {
+            
System.out.println("--------------------------------------------------");
+            System.out.println("Results for JMeter plan '" + report.getName() 
+ "'");
+            
System.out.println("--------------------------------------------------");
+            System.out.println("Throughput X: " + report.getThroughputX() + " 
req/s");
+            System.out.println("Throughput Y: " + report.getThroughputY() + " 
req/s\n");
+
+            for (ZSamples sample : report.getSamples()) {
+                System.out.println("Results for Sample '" + sample.name + "'");
+                System.out.println("Sample size : " + sample.sampleSize);
+                System.out.println("mean(Z) : " + sample.sampleMean);
+                System.out.println("SD(Z) : " + sample.sampleSD);
+                System.out.println("Null hypothesis validation 'mean(Z) = 0' : 
" + (sample.H0 ? "accepted" : "rejected"));
+                System.out.println("Success rate X : " + 
100*sample.successRateX + "%");
+                System.out.println("Success rate Y : " + 
100*sample.successRateY + "%\n");
+            }
+            
System.out.println("--------------------------------------------------\n");
+        }
+    }
+
+    public void toHtmlFile(String fileName) throws IOException {
+        FileOutputStream fos = null;
+        PrintWriter pw = null;
+        try {
+            fos = new FileOutputStream(new File(fileName));
+            pw = new PrintWriter(fos);
+            pw.println("<html>");
+            pw.println("<head>");
+            pw.println("<style type=\"text/css\">");
+            pw.println("body { background:#19233E; font-family: Arial, 
Helvetica, sans-serif; font-size: 14px; color: #ffffff;}");
+            pw.println("h1, h2 { margin: 0; font-weight: normal; color: 
#ffffff;}");
+            pw.println("table { border-bottom: 1px solid #24130F; border-left: 
1px solid #24130F; border-right: 1px solid #24130F; background: #323C52;}");
+            pw.println("td.good {background: #325552;}");
+            pw.println("td.reallygood {background: #007F0E;}");
+            pw.println("td.bad {background: #5D3C52;}");
+            pw.println("td.reallybad {background: #FF0000;}");
+            pw.println("h1 { font-size: 44px; }");
+            pw.println("h2 { font-size: 18px; }");
+            pw.println("</style>");
+            pw.println("</head><body>");
+            pw.println("<h1>Performance test results</h1>");
+            pw.println("<h2>Legend</h2>");
+            pw.println("<p>JMeter samples drawn from version X: 
X<sub>0</sub>,...,X<sub>n</sub><br/>");
+            pw.println("JMeter samples drawn from version Y: 
Y<sub>0</sub>,...,Y<sub>n</sub><br/>");
+            pw.println("Version X represents the new version, compared against 
the original version Y</br/>");
+            pw.println("Z<sub>0</sub>,...,Z<sub>n</sub> := 
(X<sub>0</sub>-Y<sub>0</sub>),...,(X<sub>n</sub>-Y<sub>0</sub>)<br/>");
+            pw.println("Null hypothesis H<sub>0</sub> := &micro;<sub>z</sub> = 
0</p><p>");
+            pw.println("<table><tr>" + good("good") + "</tr>");
+            pw.println("<tr>" + reallyGood("really good") + "</tr>");
+            pw.println("<tr>" + bad("bad") + "</tr>");
+            pw.println("<tr>" + reallyBad("really bad") + "</tr></table>");
+            pw.println("</p><hr>");
+            for (JMeterPlanReport report : m_reports) {
+                pw.println("<h2>Results for JMeter plan '" + report.getName() 
+ "'</h2>");
+                pw.println("<p><table><tr><td>Throughput X</td><td>" + 
report.getThroughputX() + " req/s</td></tr>");
+                pw.println("<tr><td>Throughput Y</td><td>" + 
report.getThroughputY() + " req/s</td></tr></table>");
+                pw.println("<br/>");
+                pw.println("<table>");
+                for (ZSamples sample : report.getSamples()) {
+                    boolean H0 = sample.H0;
+                    pw.println("<tr><th align=\"left\" colspan=\"2\">Results 
for Sample '" + sample.name + "'</th></tr>");
+                    pw.println("<tr><td><td>Sample size</td><td>" + 
sample.sampleSize + "</td></tr>");
+                    pw.println("<tr><td><td>&micro;<sub>z</sub></td>" + 
(sample.sampleMean > 0 ? bad(sample.sampleMean) : good(sample.sampleMean)) + 
"</tr>");
+                    pw.println("<tr><td><td>&sigma<sub>z</sub></td><td>" + 
sample.sampleSD + "</td></tr>");
+                    pw.println("<tr><td><td>t<sub>z</sub></td><td>" + sample.t 
+ "</td></tr>");
+                    pw.println("<tr><td><td>Pt<sub>z</sub></td><td>" + 
sample.p + "</td></tr>");
+                    pw.println("<tr><td><td>D<sub>z</sub></td><td>" + sample.D 
+ "</td></tr>");
+                    if (H0) {
+                        pw.println("<tr><td><td>H<sub>0</sub></td>" + 
reallyGood("accepted") + "</tr>");
+                    } else {
+                        pw.println("<tr><td><td>H<sub>0</sub></td>" + 
reallyBad("rejected") + "</td></tr>");
+                    }
+                    if (sample.successRateX != 1) {
+                        pw.println("<tr><td><td>Success rate X</td>" + 
reallyBad(100*sample.successRateX + "%") + "</tr>");
+                    } else {
+                        pw.println("<tr><td><td>Success rate X</td><td>" + 
100*sample.successRateX + "%</td></tr>");
+                    }
+                    if (sample.successRateY != 1) {
+                        pw.println("<tr><td><td>Success rate Y</td>" + 
reallyBad(00*sample.successRateY + "%") + "</tr>");
+                    } else {
+                        pw.println("<tr><td><td>Success rate Y</td><td>" + 
100*sample.successRateY + "%</td></tr>");
+                    }
+                        
+                    pw.println("<tr><td colspan=\"2\"></td></tr>");
+                    pw.println("<tr><td colspan=\"2\"></td></tr>");
+                }
+                pw.println("</table></p>");
+            }    
+            pw.println("</body></html>");
+        } finally {
+            try {
+                if (pw != null) {
+
+                    pw.close();
+                }
+            } finally {
+                if (fos != null) {
+                    fos.close();
+                }
+            }
+        }
+    }
+
+    private String good(Object value) {
+        return "<td class=\"good\">" + value + "</td>";
+    }
+    
+    private String bad(Object value) {
+        return "<td class=\"bad\">" + value + "</td>";
+    }
+    
+    private String reallyGood(Object value) {
+        return "<td class=\"reallygood\">" + value + "</td>";
+    }
+    
+    private String reallyBad(Object value) {
+        return "<td class=\"reallybad\">" + value + "</td>";
+    }
+}

Added: 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/Statistics.java
==============================================================================
--- (empty file)
+++ 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/Statistics.java
        Thu Mar 17 09:32:26 2011
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 2010 Amdatu.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.test.performance.analysis;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.amdatu.test.performance.runtest.JMeterRunner;
+import org.apache.commons.math.MathException;
+import org.xml.sax.SAXException;
+
+/**
+ * This class analyzes the results of JMeter plans executed against version X 
and Y. It assumes that Z=X-Y is 
+ * normally distributed, and under this assumption we test the null hypotheses 
mean(Z) = 0. Under this assumption,
+ * T=mean(Z)sqrt(n)/S has a T distribution with (n-1) degrees of freedom.
+ * TODO: Note that the assumption that Z is normally distributed is very 
likely, but it remains an assumption. To 
+ * verify the distribution, we should perform a goodness of fit check; room 
for improvement.
+ * @author ivol
+ */
+public class Statistics {
+    private String m_resultsDir;
+    
+    public Statistics(String resultsDir) {
+        m_resultsDir = resultsDir;
+    }
+    
+    public void analyze() throws ParserConfigurationException, SAXException, 
IOException, MathException {
+        // Analyze
+        ReportSummary report = new ReportSummary(m_resultsDir);
+        for (String sample : getJMeterReports()) {
+            File reportX = new File(sample + "X");
+            File reportY = new File(sample + "Y");
+            String reportName = reportX.getName().substring(0, 
reportX.getName().length()-JMeterRunner.SAMPLES_X.length());
+            SAXParser sax = SAXParserFactory.newInstance().newSAXParser();
+            JMeterResultsParser resultsX = new JMeterResultsParser(reportName);
+            JMeterResultsParser resultsY = new JMeterResultsParser(reportName);
+            sax.parse(reportX, resultsX);
+            sax.parse(reportY, resultsY);
+            
+            // Merge and add the JMeter results to the overall report
+            report.mergeAndAdd(resultsX, resultsY);
+        }
+        
+        // Print the result to the console
+        report.toConsole();
+        report.toHtmlFile(m_resultsDir + File.separator + "report.html");
+    }
+    
+    private List<String> getJMeterReports() {
+        List<String> samples = new ArrayList<String>();
+        File resultDir = new File(m_resultsDir);
+        for (File file : resultDir.listFiles()) {
+            if (file.getName().endsWith(JMeterRunner.SAMPLES_X)) {
+                String path = file.getParent();
+                String fileName = file.getName().substring(0, 
file.getName().indexOf(JMeterRunner.SAMPLES_X));
+                samples.add(path + File.separator + fileName + 
JMeterRunner.SAMPLES);
+            }
+        }
+        return samples;
+    }
+}

Added: 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/XYSample.java
==============================================================================
--- (empty file)
+++ 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/XYSample.java
  Thu Mar 17 09:32:26 2011
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2010 Amdatu.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.test.performance.analysis;
+
+/**
+ * Contains the result of a single sample (from report X or Y) in a JMeter 
execution report.
+ * 
+ * @author ivol
+ */
+public class XYSample {
+    String name; // Name of the sample, i.e. 'Open dashboard'
+    int responseTime; // Response time
+    long timeStamp; // Timestamp, used for throughput calculation
+    boolean success; // Success indication
+}

Added: 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ZSamples.java
==============================================================================
--- (empty file)
+++ 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/analysis/ZSamples.java
  Thu Mar 17 09:32:26 2011
@@ -0,0 +1,163 @@
+/*
+ Copyright (C) 2010 Amdatu.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.test.performance.analysis;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.distribution.TDistributionImpl;
+
+/**
+ * This class holds the samples of the sample observations (Z0=X0-Y0), 
(Z1=X1-Y1), ..., (Zn=Xn-Yn)
+ * @author ivol
+ *
+ */
+public class ZSamples {
+    private List<XYSample> m_samplesX;
+    private List<XYSample> m_samplesY;
+    
+    String name;
+    boolean H0; // H0 := mean(X) = mean(Y)
+    boolean H1; // H1 := mean(X) < mean(Y)
+    boolean H2; // H2 := mean(X) > mean(Y)
+    boolean H3; // H3 := mean(X) - mean(Y) >= mean with P 99%
+    double successRateX;
+    double successRateY;
+    double sampleMean;
+    double sampleSD;
+    double t;
+    double p;
+    double D;
+    int sampleSize = 0;
+    private String m_resultsDir;
+    
+    public ZSamples(String resultsDir, List<XYSample> x, List<XYSample> y) 
throws IOException, MathException {
+        m_samplesX = x;
+        m_samplesY = y;
+        name =  m_samplesX.get(0).name;
+        m_resultsDir = resultsDir;
+        
+        calculate();
+    }
+    
+    public void calculate() throws IOException, MathException {
+        // First calculate the sample observation Z
+        List<Integer> diffs = getZ();
+        double total = 0;
+        for (int diff : diffs) {
+            total += diff;
+            sampleSize++;
+        }
+        sampleMean = total/sampleSize;
+        
+        sampleSD = 0;
+        for (int diff : diffs) {
+            sampleSD += Math.pow(diff - sampleMean, 2);
+        }
+        sampleSD = Math.sqrt(sampleSD/(sampleSize -1));
+        
+        // Calculate the measured value of the test statistic T
+        t = (sampleMean/sampleSD)*Math.sqrt(sampleSize);
+        
+        // Now T has a Student distribution with (sampleSize-1) degrees of 
freedom
+        TDistributionImpl tDistribution = new TDistributionImpl(sampleSize-1);
+        if (t <= 0) {
+            // If t < 0 we define H1: mean(x) < mean(y) 
+            // Calculate P[T(n-1)] <= t
+            p = tDistribution.cumulativeProbability(t);
+        } else {
+            // If t > 0 we define H2: mean(x) < mean(y) 
+            // Calculate P[T(n-1)] >= t
+            p = (1-tDistribution.cumulativeProbability(t));
+        }
+        
+        // Null hypotheses is that X and Y are equally distributed. Under the 
assumption that the diffs
+        // are normally distributed, a 95% probability interval of the mean is 
[mean-2sd, mean+2sd].
+        H0 = sampleMean-2*sampleSD <= 0 && sampleMean+2*sampleSD >=0;
+        
+        H1 = (tDistribution.cumulativeProbability(t) >= 0.01);
+        H2 = (1-tDistribution.cumulativeProbability(t) >= 0.01);
+
+        // Now we calculate the maximum value of D, for which is true that:
+        // P[((mean(X)-D)/sd)*sqrt(n)] >= 0.01
+        // Effectively this tell us that 'with probability 99% we can ensure 
that the difference in means 
+        // between X and Y is at least D'
+        if (t <= 0) {
+            double valueOfT = tDistribution.inverseCumulativeProbability(0.01);
+            D = sampleMean-(sampleSD*valueOfT)/Math.sqrt(sampleSize);
+        } else {
+            double valueOfT = tDistribution.inverseCumulativeProbability(0.99);
+            D = sampleMean-(sampleSD*valueOfT)/Math.sqrt(sampleSize);
+        }
+        
+        // Calculate success rate
+        successRateX = 0;
+        successRateY = 0;
+        for (XYSample r : m_samplesX) {
+            successRateX += r.success ? 1 : 0;
+        }
+        successRateX /= m_samplesX.size();
+        
+        for (XYSample r : m_samplesY) {
+            successRateY += r.success ? 1 : 0;
+        }
+        successRateY /= m_samplesY.size();
+    }
+    
+    private List<Integer> getZ() throws IOException {
+        List<Integer> results = new ArrayList<Integer>();
+        
+        File samplesZOutput = new File(m_resultsDir, name + "-samples.Z");
+        FileOutputStream fos = null;
+        PrintWriter pw = null;
+        try {
+            fos = new FileOutputStream(samplesZOutput);
+            pw = new PrintWriter(fos);
+           
+            for (int i=0; i<m_samplesX.size(); i++) {
+                if (i < m_samplesY.size()) {
+                    int zi = m_samplesX.get(i).responseTime - 
m_samplesY.get(i).responseTime;
+                    results.add(zi);
+                    pw.write(zi + "\n");
+                } else {
+                    System.err.println("Mismatch in sample sizes: X=" + 
m_samplesX.size() + ", Y=" + m_samplesY.size());
+                }
+            }
+        } finally {
+            try {
+                if (pw != null) {
+                    pw.close();
+                }
+            } finally {
+                if (fos != null) {
+                    fos.close();
+                }
+            }
+        }
+        
+        
+        return results;
+    } 
+    
+    
+   
+}

Added: 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/main/Main.java
==============================================================================
--- (empty file)
+++ 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/main/Main.java
  Thu Mar 17 09:32:26 2011
@@ -0,0 +1,206 @@
+/*
+ Copyright (C) 2010 Amdatu.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.test.performance.main;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.amdatu.test.performance.analysis.Statistics;
+import org.amdatu.test.performance.runtest.AmdatuLauncher;
+import org.amdatu.test.performance.runtest.JMeterRunner;
+
+/**
+ * Main command line client class. 
+ * 
+ * @author ivol
+ */
+public class Main {
+    private static String VERBOSE_ARG = "-verbose";
+    private static String ANALYZE_ARG = "-analyze";
+    private static String RUNTEST_ARG = "-runtest";
+    private static String AMDATU_X_ARG = "-amdatuVersionX";
+    private static String AMDATU_Y_ARG = "-amdatuVersionY";
+    private static String JMETERPLANSDIR_ARG = "-jmeterplansdir";
+    private static String RESULTSDIR_ARG = "-resultsdir";
+
+    // Wordy arguments
+    private final static List<String> BOOLEAN_ARGS = new ArrayList<String>();
+    static {
+        BOOLEAN_ARGS.add(VERBOSE_ARG);
+        BOOLEAN_ARGS.add(ANALYZE_ARG);
+        BOOLEAN_ARGS.add(RUNTEST_ARG);
+    }
+
+    // File arguments
+    private final static List<String> FILE_ARGS = new ArrayList<String>();
+    static {
+        FILE_ARGS.add(AMDATU_X_ARG);
+        FILE_ARGS.add(AMDATU_Y_ARG);
+    }
+
+    // Directory arguments
+    private final static List<String> DIR_ARGS = new ArrayList<String>();
+    static {
+        DIR_ARGS.add(JMETERPLANSDIR_ARG);
+        DIR_ARGS.add(RESULTSDIR_ARG);
+    }
+
+    public static void main(String[] args) {
+        try {
+            int i=0;
+            String arg;
+            Map<String, Object> arguments = new HashMap<String, Object>(); 
+            while (i < args.length) {
+                arg = args[i++];
+
+                // use this type of check for "wordy" arguments
+                if (BOOLEAN_ARGS.contains(arg)) {
+                    arguments.put(arg, true);
+                } else if (FILE_ARGS.contains(arg)) {
+                    if (i < args.length) {
+                        String value = args[i];
+                        if (!new File(value).exists() || !new 
File(value).isFile()) {
+                            System.err.println("File " + value + " does not 
exist or is not a file");
+                        } else {
+                            arguments.put(arg, value);
+                            i++;
+                        }
+                    } else {
+                        System.err.println(arg + " requires an existing 
filename");
+                    }
+                } else if (DIR_ARGS.contains(arg)) {
+                    if (i < args.length) {
+                        String value = args[i];
+                        if (!new File(value).exists() || !new 
File(value).isDirectory()) {
+                            System.err.println("Directory " + value + " does 
not exist or is not a directory");
+                        } else {
+                            arguments.put(arg, value);
+                            i++;
+                        }
+                    } else {
+                        System.err.println(arg + " requires an existing 
directory name");
+                    }
+                }
+            }
+
+            if (Boolean.TRUE.equals(arguments.get(VERBOSE_ARG))) {
+                for (String key : arguments.keySet()) {
+                    System.out.println(key + "=" + arguments.get(key)) ;
+                }
+            }
+            
+            if (!Boolean.TRUE.equals(arguments.get(RUNTEST_ARG)) && 
!Boolean.TRUE.equals(arguments.get(ANALYZE_ARG))) {
+                System.err.println("Invalid arguments provided");
+                printUsage();
+            }
+            
+            File rootTmpDir = getRootTmpDir();
+
+            if (Boolean.TRUE.equals(arguments.get(RUNTEST_ARG))) {
+                if (verifyRunTest(arguments)) {
+                    String amdatuX = arguments.get(AMDATU_X_ARG).toString();
+                    String amdatuY = arguments.get(AMDATU_Y_ARG).toString();
+                    String jmeterPlanDir = 
arguments.get(JMETERPLANSDIR_ARG).toString();
+                    String resultsDir = 
arguments.get(RESULTSDIR_ARG).toString();
+                    AmdatuLauncher launcherX = new AmdatuLauncher(amdatuX, 
rootTmpDir);
+                    try {
+                        launcherX.start();
+                        JMeterRunner jmeter = new JMeterRunner(jmeterPlanDir, 
"X", resultsDir, rootTmpDir);
+                        jmeter.run();
+                    } finally {
+                        launcherX.stop();
+                    }
+
+                    AmdatuLauncher launcherY = new AmdatuLauncher(amdatuY, 
rootTmpDir);
+                    try {
+                        launcherY.start();
+                        JMeterRunner jmeter = new JMeterRunner(jmeterPlanDir, 
"Y", resultsDir, rootTmpDir);
+                        jmeter.run();
+                    } finally {
+                        launcherY.stop();
+                    }
+                    System.out.println("Amdatu performance test completed.");
+                }
+            }
+            if (Boolean.TRUE.equals(arguments.get(ANALYZE_ARG))) {
+                if (!arguments.containsKey(RESULTSDIR_ARG)) {
+                    printUsage();
+                } else {
+                    String resultsDir = 
arguments.get(RESULTSDIR_ARG).toString();
+                    new Statistics(resultsDir).analyze();
+                    System.out.println("Amdatu performance test analysis 
completed.");
+                }
+            }
+        } catch (Throwable t) {
+            System.err.println(t.toString());
+            t.printStackTrace();
+        }
+    }
+    
+    private static File getRootTmpDir() {
+        String javaTmpDir = System.getProperty("java.io.tmpdir");
+        File tmpDir = new File(javaTmpDir, "amdatu-performancetest");
+        tmpDir.mkdir();
+        return tmpDir;
+    }
+
+    private static boolean verifyRunTest(Map<String, Object> arguments) {
+        boolean ok = true;
+        if (!arguments.containsKey(AMDATU_X_ARG) || 
arguments.get(AMDATU_X_ARG).toString().isEmpty()) {
+            System.err.println("No argument " + AMDATU_X_ARG + " provided");
+            ok = false;
+        }
+        if (!arguments.containsKey(AMDATU_Y_ARG) || 
arguments.get(AMDATU_Y_ARG).toString().isEmpty()) {
+            System.err.println("No argument " + AMDATU_Y_ARG + " provided");
+            ok = false;
+        }
+        if (!arguments.containsKey(JMETERPLANSDIR_ARG) || 
arguments.get(JMETERPLANSDIR_ARG).toString().isEmpty()) {
+            System.err.println("Argument " + JMETERPLANSDIR_ARG + " not 
provided");
+            ok = false;
+        }
+        else if (!new 
File(arguments.get(JMETERPLANSDIR_ARG).toString()).exists() || !new 
File(arguments.get(JMETERPLANSDIR_ARG).toString()).isDirectory()) {
+            System.err.println("Directory '" + 
arguments.get(JMETERPLANSDIR_ARG) + "' is not a valid, existing, directory");
+            ok = false;
+        }
+        if (!arguments.containsKey(RESULTSDIR_ARG) || 
arguments.get(RESULTSDIR_ARG).toString().isEmpty()) {
+            System.err.println("Argument " + RESULTSDIR_ARG + " not provided");
+            ok = false;
+        }
+        else if (!new File(arguments.get(RESULTSDIR_ARG).toString()).exists() 
|| !new File(arguments.get(RESULTSDIR_ARG).toString()).isDirectory()) {
+            System.err.println("Directory '" + arguments.get(RESULTSDIR_ARG) + 
"' is not a valid, existing, directory");
+            ok = false;
+        }
+        if (!ok) {
+            printUsage();
+        } 
+        return ok;
+    }
+
+    private static void printUsage() {
+        System.err.println("Usage: ");
+        System.err.println("-verbose         Set verbose mode on");
+        System.err.println("-runtest         Runs a performance test");
+        System.err.println("-analyze         Runs a performance test 
analysis");
+        System.err.println("-amdatuVersionX  Absolute path to a binary 
distribution zip of an Amdatu release, called version X. This represents the 
new version to be compared with the previous version Y (required only for 
-runtest)");
+        System.err.println("-amdatuVersionY  Absolute path to a binary 
distribution zip of an Amdatu release, called version Y. This represents the 
original version. (required only for -runtest)");
+        System.err.println("-jmeterplansdir  Absolute path to a directory that 
holds JMeter plans to execute during the performance test (required only for 
-runtest)");
+        System.err.println("-resultsdir      Absolute path to a directory to 
which the results of JMeter executions will be written as well as analysis 
results (required for both -runtest and -analyze)");
+    }
+}
\ No newline at end of file

Added: 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/AmdatuLauncher.java
==============================================================================
--- (empty file)
+++ 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/AmdatuLauncher.java
     Thu Mar 17 09:32:26 2011
@@ -0,0 +1,152 @@
+/*
+ Copyright (C) 2010 Amdatu.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.test.performance.runtest;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.amdatu.test.performance.Utils;
+import org.apache.commons.io.FileUtils;
+
+/**
+ * This class is responsible for starting/stopping Amdatu distributions from a 
single zip file.
+ * 
+ * @author ivol
+ */
+public class AmdatuLauncher {
+    // For now hardcoded, could be read from deploy/org.apache.felix.http.cfg
+    private static final String[] CHECK_URLS = new String[] {
+        "http://localhost:8080/dashboard/jsp/dashboard.jsp";,
+        "http://localhost:8080/rest/authorization/status";
+    };
+    
+    private String m_releaseZip;
+    private File m_rootTmpDir;
+    private File m_tmpDir;
+    private Process m_amdatuProcess;
+
+    public AmdatuLauncher(String releaseZip, File rootTmpDir) {
+        m_releaseZip = releaseZip;
+        m_rootTmpDir = rootTmpDir;
+    }
+
+    public void start() throws IOException {
+        // First unzip the file to the java io tmpdir
+        File zipFile = new File(m_releaseZip);
+        
+        String amdatuDir = zipFile.getName();
+        m_tmpDir = new File(m_rootTmpDir, amdatuDir);
+        m_tmpDir.mkdir();
+        System.out.println("Extracting '" + zipFile.getName() + "' to '" + 
m_tmpDir.getAbsolutePath() + "'");
+
+        Utils.unzip(zipFile, m_tmpDir);
+
+        // Start the Amdatu server in a separate process
+        List<String> command = new ArrayList<String>();
+        command.add("java");
+        command.add("-Xms256m");
+        command.add("-Xmx1024m");
+        command.add("-XX:MaxPermSize=256m");
+        
command.add("-Dfelix.config.properties=file:conf/felix-config.properties");
+        command.add("-Dfile.encoding=utf-8");
+        command.add("-jar");
+        command.add("amdatu-system/" + getFelixMainJar());
+
+        ProcessBuilder processBuilder = new ProcessBuilder(command);
+        processBuilder.directory(m_tmpDir);
+        m_amdatuProcess = processBuilder.start();
+
+        Utils.dispatchOutput(m_amdatuProcess);
+
+        // Now wait until the dashboard returns a 200 for a maximum of 3 
minutes
+        System.out.println("Starting Amdatu, this may take some time...");
+        long before = System.currentTimeMillis();
+        boolean ok = false;
+        for (String checkUrl : CHECK_URLS) {
+            System.out.println("Waiting for " + checkUrl + " to return a 200");
+            ok |= waitForURL(new URL(checkUrl), 200, 180000);
+        }
+        if (!ok) {
+            throw new IllegalStateException("Amdatu could not be launched from 
'" + m_tmpDir + "'. Aborting test.");
+        }
+        long diff = System.currentTimeMillis() - before;
+        System.out.println("Amdatu startup completed in " + diff + " ms, 
running JMeter test plans");
+    }
+
+    public void stop() throws IOException, InterruptedException {
+        // Kill it instantly, no nice shutdown needed
+        System.out.println("Stopping Amdatu...");
+        if (m_amdatuProcess != null && amdatuProcessRunning()) {
+            m_amdatuProcess.destroy();
+            m_amdatuProcess.waitFor();
+        }
+       
+        FileUtils.deleteDirectory(m_tmpDir);
+        
+        System.out.println("Amdatu shutdown completed");
+    }
+
+    private boolean waitForURL(URL url, int responseCode, int timeout) throws 
IOException {
+        long deadline = System.currentTimeMillis() + timeout;
+        while (System.currentTimeMillis() < deadline && 
amdatuProcessRunning()) {
+            try {
+                if (checkURL(url) == responseCode) {
+                    return true;
+                }
+                Thread.sleep(1000);
+            }
+            catch (InterruptedException ie) {}
+            catch (IOException e) {}
+        }
+        return false;
+    }
+
+    private int checkURL(URL url) throws IOException {
+        HttpURLConnection connection = (HttpURLConnection) 
url.openConnection();
+        int responseCode;
+        try {
+            connection.connect();
+            responseCode = connection.getResponseCode();
+        } finally {
+            connection.disconnect();
+        }
+        return responseCode;
+    }
+
+    private boolean amdatuProcessRunning() {
+        try {
+            m_amdatuProcess.exitValue();
+            return false;
+        } catch (IllegalThreadStateException e) {
+            return true;
+        }
+    }
+    
+    private String getFelixMainJar() {
+        File systemDir = new File(m_tmpDir, "amdatu-system");
+        for (File file : systemDir.listFiles()) {
+            if (file.getName().startsWith("org.apache.felix.main")) {
+                return file.getName();
+            }
+        }
+        return null;
+    }
+}

Added: 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/JMeterRunner.java
==============================================================================
--- (empty file)
+++ 
trunk/etc/performancetest/src/main/java/org/amdatu/test/performance/runtest/JMeterRunner.java
       Thu Mar 17 09:32:26 2011
@@ -0,0 +1,113 @@
+/*
+ Copyright (C) 2010 Amdatu.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.amdatu.test.performance.runtest;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.amdatu.test.performance.Utils;
+import org.apache.commons.io.IOUtils;
+
+/**
+ * This class is responsible to run all JMeter plans present in the configured 
directory.
+ * 
+ * @author ivol
+ */
+public class JMeterRunner {
+    public final static String SAMPLES = "-samples.";
+    public final static String SAMPLES_X = SAMPLES + "X";
+    public final static String SAMPLES_Y = SAMPLES + "Y";
+    
+    private File m_rootTmpDir;
+    private String m_jmeterPlansDir;
+    private String m_resultsDir;
+    private String m_version;
+    private Process m_jmeterProcess;
+    
+    public JMeterRunner(String jmeterPlansDir, String version, String 
resultsDir, File rootTmpDir) {
+        m_jmeterPlansDir = jmeterPlansDir;
+        m_version = version;
+        m_resultsDir = resultsDir;
+        m_rootTmpDir = rootTmpDir;
+    }
+
+    public void run() throws FileNotFoundException, IOException, 
InterruptedException {
+        File resultDir = new File(m_resultsDir);
+        File jmeterLogFile = new File(resultDir, "jmeter.log");
+        
+        File jmeterDir = installJMeter();
+        jmeterDir = new File(jmeterDir, "bin");
+        File plans = new File(m_jmeterPlansDir);
+        
+        for (File jmeterPlan : plans.listFiles()) {
+            if (jmeterPlan.isFile()) {
+                // Run the JMeter plan!
+                File sampleFile = new File(resultDir, jmeterPlan.getName() + 
SAMPLES + m_version);
+                List<String> command = new ArrayList<String>();
+                command.add("cmd.exe");
+                command.add("/C");
+                command.add("jmeter");
+                command.add("-n"); // Non-GUI mode
+                command.add("-t");
+                command.add(jmeterPlan.getAbsolutePath());
+                command.add("-l");
+                command.add(sampleFile.getAbsolutePath());
+                command.add("-j");
+                command.add(jmeterLogFile.getAbsolutePath());
+                ProcessBuilder processBuilder = new ProcessBuilder(command);
+                processBuilder.directory(jmeterDir);
+                System.out.println("Running JMeter plan '" + 
jmeterPlan.getAbsolutePath() + "' from directory '" + 
jmeterDir.getAbsolutePath() + "'");
+                m_jmeterProcess = processBuilder.start();
+                Utils.dispatchOutput(m_jmeterProcess);
+                
+                m_jmeterProcess.waitFor();
+            }
+        }
+    }
+
+    private File installJMeter() throws FileNotFoundException, IOException {
+        // Install only once
+        File jmeter = new File(new File(m_rootTmpDir, "jmeter-2.4"), 
"jakarta-jmeter-2.4");
+        if (!jmeter.exists()) {
+            // Copy the jmeter.zip from this jar to the temp dir
+            URL url = 
getClass().getClassLoader().getResources("jakarta-jmeter-2.4.zip").nextElement();
+            InputStream is = null;
+            try {
+                is = url.openStream();
+                File jmeterFile = new File(m_rootTmpDir, "jmeter-2.4.zip");
+                IOUtils.copy(is, new FileOutputStream(jmeterFile));
+                
+                // And unzip
+                File target = new File(m_rootTmpDir, "jmeter-2.4");
+                Utils.unzip(jmeterFile, target);
+                return new File(target, "jakarta-jmeter-2.4");
+            } finally {
+                if (is != null) {
+                    is.close();
+                }
+            }
+        } else {
+            return jmeter;
+        }
+    }
+}

Added: trunk/etc/performancetest/src/main/resources/jakarta-jmeter-2.4.zip
==============================================================================
Binary file. No diff available.
_______________________________________________
Amdatu-commits mailing list
[email protected]
http://lists.amdatu.org/mailman/listinfo/amdatu-commits

Reply via email to