
Thankfully a lot of posters don't use Google, otherwise I couldn't answer the same questions again!
The biggest mistake we could ever make in our lives is to think we work for anybody but ourselves. --Brian Tracy


int count = Integer.parseInt(vars.get("inputTerms_matchNr"));
for(int i=1;i<=count;i++) { //regex counts are 1 based
sampler.addArgument("hardcodedkey", vars.get("inputTerms_" + i));
}


import org.apache.poi.POITextExtractor;
import org.apache.poi.extractor.ExtractorFactory;
import java.io.ByteArrayInputStream;
ByteArrayInputStream bais = new ByteArrayInputStream(in);
POITextExtractor extractor = ExtractorFactory.createExtractor(bais);
prev.setResponseData(extractor.getText());
You should be able to parse word documents and excel and ppt's using this beanshell post processor. Remember to copy the POI libraries to JMeter/libimport java.io.ByteArrayInputStream;
import java.io.StringWriter;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.util.PDFTextStripper;
PDDocument document = null;
StringWriter sw = new StringWriter();
try {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
document = PDDocument.load(bais);
PDFTextStripper stripper = new PDFTextStripper("UTF-8");
stripper.setSortByPosition( false );
stripper.setShouldSeparateByBeads( true );
stripper.setStartPage( 1 );
stripper.setEndPage(Integer.MAX_VALUE );
stripper.writeText( document, sw );
} catch (Throwable t) {
t.printStackTrace();
sw.append("ERROR");
} finally {
sw.close();
document.close();
}
vars.put("extractedText", sw.toString());
Update - for PDFBox 2.0.26
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
PDDocument document = null;
StringWriter sw = new StringWriter();
try {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
document = PDDocument.load(bais);
PDFTextStripper stripper = new PDFTextStripper();
stripper.setSortByPosition( false );
stripper.setShouldSeparateByBeads( true );
stripper.setStartPage( 1 );
stripper.setEndPage(Integer.MAX_VALUE );
stripper.writeText( document, sw );
} catch (Throwable t) {
t.printStackTrace();
sw.append("ERROR");
} finally {
sw.close();
document.close();
}
vars.put("extractedText", sw.toString());
All this does is use the PDFBox API to extract text from the PDF (bytes are present in the data object) and write it to a variable in JMeter , extractedText.
The next sampler is a Java Sampler which sets the ResultData to ${extracted text}. This will echo back the contents of this variable as the response of the sampler.
prev.setResponseData(sw.toString());
This will set the response of the HTTPSampler to be the text value and allow you to directly specify the assertion (and you can also eliminate the transaction controller).
Update : if you are interested in MS office formats then follow the steps in this post, just change the BeanShell post processor to that mentioned in Asserting MS Office Formats
Future work
a. make a custom PDF(or any format sampler) with the options that are hardcoded (e.g. startPage or endPage)
b. experiment with ways to use less memory. Typically should extract and write to file and use that.
How do I analyse my application with JMeter?There are various reasons one might run a load test broadly detection, simulation, verification and analysis. The type of test or the scripts you might write, what you would measure or track would vary a bit depending on what you want to do. JMeter is good for simulation and verification. It is an aid to detection and analysis but you usually need other tools to help you out.
The custom report hyperlinks the titles of the normal summary tables to lead to graphs for the same.public class AggregateGraphTask extends Task {
private String outputDir;
private String outputFilePrefix;
private Boolean showThreshold = Boolean.TRUE;
private Double threshold = 500D;
private String jmeterResultFile;
private String jmeterHome;
public String getJmeterHome() {
return jmeterHome;
}
public void setJmeterHome(String jmeterHome) {
this.jmeterHome = jmeterHome;
}
public String getJmeterResultFile() {
return jmeterResultFile;
}
public void setJmeterResultFile(String jmeterResultFile) {
this.jmeterResultFile = jmeterResultFile;
}
public String getOutputDir() {
return outputDir;
}
public void setOutputDir(String outputDir) {
this.outputDir = outputDir;
}
public String getOutputFilePrefix() {
return outputFilePrefix;
}
public void setOutputFilePrefix(String outputFilePrefix) {
this.outputFilePrefix = outputFilePrefix;
}
public Boolean getShowThreshold() {
return showThreshold;
}
public void setShowThreshold(Boolean showThreshold) {
this.showThreshold = showThreshold;
}
public Double getThreshold() {
return threshold;
}
public void setThreshold(Double threshold) {
this.threshold = threshold;
}
@Override
public void execute() throws BuildException {
try {
GraphClient.init(jmeterHome);
String outputPrefix = outputDir + File.separator + outputFilePrefix;
if(Boolean.TRUE.equals(showThreshold)) {
GraphClient.writeAggregateChartWithThreshold(jmeterResultFile, outputPrefix, showThreshold, threshold);
} else {
GraphClient.writeAggregateChart(jmeterResultFile, outputPrefix) ;
}
} catch(Exception e) {
throw new BuildException(e);
}
}
}
<project name="Jmeter" basedir="." default="runOfflineGraph">
<property name="lib.dir" value="${basedir}/lib"/>
<property name="report.dir" value="${basedir}/report"/>
<property name="styles.dir" value="${basedir}/styles"/>
<property name="export.dir" value="${basedir}/export"/>
<property environment="env"/>
<property name="jmeter.home.dir" value="${env.JMETER_HOME}"/>
<property name="jfreechart.home.dir" value="${env.JFREECHART_HOME}"/>
<path id="run.classpath">
<fileset dir="${jmeter.home.dir}" includes="**/*.jar"/>
<fileset dir="${jfreechart.home.dir}" includes="**/*.jar"/>
<fileset dir="${lib.dir}" includes="*.jar"/>
</path>
<target name="clean">
<delete dir="${report.dir}" />
<delete dir="${export.dir}" />
</target>
<target name="init">
<mkdir dir="${report.dir}" />
<mkdir dir="${export.dir}" />
</target>
<target name="runJMeter" depends="init">
<taskdef
name="jmeter"
classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"/>
<taskdef name="aggregatechart" classname="org.md.jmeter.ant.AggregateGraphTask" classpathref="run.classpath"/>
<tstamp />
<property name="uniqueTStamp" value="${DSTAMP}${TSTAMP}" />
<property name="imageNamePrefix" value="AggregateChartThreshold-${uniqueTStamp}" />
<property name="jmeter.result.fileName" value="${run.test.report}-${uniqueTStamp}" />
<property name="jmeter.result.file" value="${report.dir}/${jmeter.result.fileName}.jtl" />
<jmeter
jmeterhome="${jmeter.home.dir}"
testplan="${run.test.plan}"
resultlog="${jmeter.result.file}">
<property name="jmeter.save.saveservice.output_format" value="xml"/>
<property name="run.threadcount" value="${run.threadcount}" />
<property name="run.loopcount" value="${run.loopcount}" />
<property name="sample_variables" value="${sample_variables}" />
</jmeter>
<xslt
in="${jmeter.result.file}"
out="${report.dir}/${jmeter.result.fileName}.html"
style="${styles.dir}/${xsl.file}">
<param name="imageNamePrefix" expression="${imageNamePrefix}"/>
<aggregatechart jmeterHome="${jmeter.home.dir}" jmeterResultFile="${jmeter.result.file}" outputDir="${report.dir}"
outputFilePrefix="${imageNamePrefix}" showThreshold="true" threshold="200"/>
</target>
<target name="runOfflineGraph" depends="init">
<antcall target="runJMeter">
<param name="run.test.plan" value="OfflineGraphs.jmx"/>
<param name="run.test.report" value="OfflineGraph"/>
<param name="sample_variables" value=""/>
<param name="run.threadcount" value="1"/>
<param name="run.loopcount" value="5"/>
<param name="xsl.file" value="OfflineGraph.xsl" />
</antcall>
</target>
</project>
Important points are(${jmeter.result.file}) is passed as an input to the task. The image file names to be generated are important ${imageNamePrefix} as we need to reference this in the stylesheet. The directory to which the Graph code writes must be the same as the XSLT output (or atleast the XSLT and Graph code must be consistent in where the images are referenced from in the HTML)<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="imageNamePrefix">AggregateChartThreshold</xsl:param>
We pass in a parameter that is to be used while generating the img tag in the HTML <xsl:call-template name="summary" />
<hr size="1" width="95%" align="left" />
<xsl:call-template name="pagelist" />
<hr size="1" width="95%" align="left" />
<xsl:call-template name="detail" />
<xsl:call-template name="graph-images" />
We call our custom template <th><a href="#AverageGraph">Average Time</a></th>
<th><a href="#MinimumGraph">Min Time</a></th>
<th><a href="#MaximumGraph">Max Time</a></th>
We change the titles to be anchors (named anchors that are next to the images)<xsl:template name="output-image">
<xsl:param name="suffix" />
<xsl:element name="img">
<xsl:attribute name="src"><xsl:value-of select="$imageNamePrefix" />-<xsl:value-of select="$suffix" />.png</xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template name="graph-images">
Graphs
<br />
<b>Minimum</b>
<br />
<a name="MinimumGraph"></a>
<xsl:call-template name="output-image">
<xsl:with-param name="suffix">Min</xsl:with-param>
</xsl:call-template>
<br />
<b>Maximum</b>
<br />
<a name="MaximumGraph"></a>
<xsl:call-template name="output-image">
<xsl:with-param name="suffix">Max</xsl:with-param>
</xsl:call-template>
<br />
<b>Average</b>
<br />
<a name="AverageGraph"></a>
<xsl:call-template name="output-image">
<xsl:with-param name="suffix">Avg</xsl:with-param>
</xsl:call-template>
<br />
<b>Median</b>
<br />
<a name="MedianGraph"></a>
<xsl:call-template name="output-image">
<xsl:with-param name="suffix">Median</xsl:with-param>
</xsl:call-template>
<br />
<b>90 percentile</b>
<br />
<a name="NinetyPerGraph"></a>
<xsl:call-template name="output-image">
<xsl:with-param name="suffix">90</xsl:with-param>
</xsl:call-template>
</xsl:template>
Finally we output img tags. The knowledge of how the Aggregate Graph generates the suffix for the images is hardcoded into the stylesheet. The passed parameter is used to form the filename as well (this is used to allow multiple runs , so that the images don't overwrite previous ones)
set JAVA_HOME=C:\bea102\jdk150_11
set JMETER_HOME=C:\projects\R1-Portal-CMS\test\jakarta-jmeter-2.3.4
set JFREECHART_HOME=C:\work\java\jfreechart-1.0.13
set ANT_HOME=C:\work\java\apache-ant-1.7.1
set PATH=%JAVA_HOME%\bin;%ANT_HOME%\bin;%PATH%
set CLASSPATH=%JMETER_HOME%\extras\ant-jmeter-1.0.9.jar;%CLASSPATH%
ant %*
We set some environment properties that the build needs and run it.




public static void writeAggregateChartWithThreshold() throws Exception {
File f = new File(JMETER_RESULT_FILE);
ResultCollector rc = new ResultCollector();
AggregateChartVisualizer v = new AggregateChartVisualizer(ConfigUtil
.getOutputGraphDir()
+ "/AggregateChartThreshold",true,500);
ResultCollectorHelper rch = new ResultCollectorHelper(rc, v);
XStreamJTLParser p = new XStreamJTLParser(f, rch);
p.parse();
v.writeOutput();
}





public static void writeAggregateChart() throws Exception {
File f = new File(JMETER_RESULT_FILE);
ResultCollector rc = new ResultCollector();
AggregateChartVisualizer v = new AggregateChartVisualizer(ConfigUtil
.getOutputGraphDir()
+ "/AggregateChart");
ResultCollectorHelper rch = new ResultCollectorHelper(rc, v);
XStreamJTLParser p = new XStreamJTLParser(f, rch);
p.parse();
v.writeOutput();
}
SaveService.loadTestResults(FileInputStream, ResultCollectorHelper);
where ResultCollectorHelper is passed a Visualizer. The Visualizer has one method that is important to usadd (SampleResult sampleResult)
The Visualizer interface is a simple strategy that can be implemented as we want. Since we also want to write some graphs, I created a new interface called OfflineVisualizer which adds a single methodpublic Object writeOutput() throws IOException
Here is the class diagram (generated using FUJABA)
//adds a sample. JFreechart uses a TimeSeries object into which we set each data item
public void add(SampleResult sampleResult) {
String label = sampleResult.getSampleLabel();
TimeSeries s1 = map.get(label);
if (s1 == null) {
s1 = new TimeSeries(label);
map.put(label, s1);
}
long responseTime = sampleResult.getTime();
Date d = new Date(sampleResult.getStartTime());
s1.addOrUpdate(new Millisecond(d), responseTime);
}
//uses JFreeChartAPI to write the data into an image file
public Object writeOutput() throws IOException {
TimeSeriesCollection dataset = new TimeSeriesCollection();
for (Map.Entry<String, TimeSeries> entry : map.entrySet()) {
dataset.addSeries(entry.getValue());
}
JFreeChart chart = createChart(dataset);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(fileName);
ChartUtilities.writeChartAsPNG(fos, chart, WIDTH, HEIGHT);
} finally {
if (fos != null) {
fos.close();
}
}
return null;
}
//use the JFreeChart API to generate a Line Chart
private static JFreeChart createChart(XYDataset dataset) {
JFreeChart chart = ChartFactory.createTimeSeriesChart("Response Chart", // title
"Date", // x-axis label
"Time(ms)", // y-axis label
dataset, // data
true, // create legend?
true, // generate tooltips?
false // generate URLs?
);
chart.setBackgroundPaint(Color.white);
XYPlot plot = (XYPlot) chart.getPlot();
plot.setBackgroundPaint(Color.lightGray);
plot.setDomainGridlinePaint(Color.white);
plot.setRangeGridlinePaint(Color.white);
plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
plot.setDomainCrosshairVisible(true);
plot.setRangeCrosshairVisible(true);
XYItemRenderer r = plot.getRenderer();
if (r instanceof XYLineAndShapeRenderer) {
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) r;
renderer.setBaseShapesVisible(true);
renderer.setBaseShapesFilled(true);
renderer.setDrawSeriesLineAsPath(true);
}
DateAxis axis = (DateAxis) plot.getDomainAxis();
axis.setDateFormatOverride(new SimpleDateFormat("dd-MMM-yyyy HH:mm"));
return chart;
}
/**
* decorates the visualizer by filtering out labels
*/
public void add(SampleResult sampleResult) {
boolean allow = labels.contains(sampleResult.getSampleLabel());
if (!pass) {
allow = !allow;
}
if (allow) {
visualizer.add(sampleResult);
}
}
/**
* delegates to the decorated visualizer
*
* @return whatever the decorated visualizer returns
*/
public Object writeOutput() throws IOException {
return visualizer.writeOutput();
}
/**
* adds the sample to each of the composed visualizers
*/
public void add(SampleResult sampleResult) {
for (OfflineVisualizer visualizer : visualizers) {
visualizer.add(sampleResult);
}
}
/**
* @return a List of each result from the composed visualizer
*/
public Object writeOutput() throws IOException {
List<Object> result = new ArrayList<Object>();
for (OfflineVisualizer visualizer : visualizers) {
result.add(visualizer.writeOutput());
}
return result;
}
/**
* parses each file
*
* @throws Exception
*/
public void parse() throws Exception {
// One day we might multithread this
for (String file : files) {
ResultCollector rc = new ResultCollector();
TotalThroughputVisualizer ttv = new TotalThroughputVisualizer();
visualizers.add(ttv);
ResultCollectorHelper rch = new ResultCollectorHelper(rc, ttv);
XStreamJTLParser p = new XStreamJTLParser(new File(file), rch);
p.parse();
}
}
/**
* Gets the resulting throughput from each file and combines them
*
* @return always returns null
* @throws IOException
*/
public Object writeOutput() throws IOException {
XYSeries xyseries = new XYSeries("throughput");
for (AbstractOfflineVisualizer visualizer : visualizers) {
Throughput throughput = (Throughput) visualizer.writeOutput();
xyseries.add(throughput.getThreadCount(), throughput
.getThroughput());
}
XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(xyseries);
JFreeChart chart = createChart(dataset);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(fileName);
ChartUtilities.writeChartAsPNG(fos, chart, WIDTH, HEIGHT);
} finally {
if (fos != null) {
fos.close();
}
}
return null;
}

File f = new File(JMETER_RESULT_FILE);
ResultCollector rc = new ResultCollector();
LineChartVisualizer v = new LineChartVisualizer(OUTPUT_GRAPH_DIR + "/LineChart.png");
ResultCollectorHelper rch = new ResultCollectorHelper(rc, v);//this is the visualizer we want
XStreamJTLParser p = new XStreamJTLParser(f, rch);
p.parse();
v.writeOutput(); //write the output

File f = new File(JMETER_RESULT_FILE);
ResultCollector rc = new ResultCollector();
MinMaxAvgGraphVisualizer v = new MinMaxAvgGraphVisualizer(OUTPUT_GRAPH_DIR + "/MinMaxAvg.png");
String[] labels = {"Component reference"}; //we only want this label
LabelFilterVisualizer lv= new LabelFilterVisualizer(Arrays.asList(labels), v);//decorate the MinMaxAvgGraphVisualizer
ResultCollectorHelper rch = new ResultCollectorHelper(rc, lv);//use the decorated visualizer
XStreamJTLParser p = new XStreamJTLParser(f, rch);
p.parse();
lv.writeOutput();//write it out

File f = new File(JMETER_RESULT_FILE);
ResultCollector rc = new ResultCollector();
StackedBarChartVisualizer v = new StackedBarChartVisualizer(OUTPUT_GRAPH_DIR + "/StackedBarChart.png");
String[] labels = {"Component reference"};//we only want this label
LabelFilterVisualizer lv= new LabelFilterVisualizer(Arrays.asList(labels), v);//Decorate the StackedBarChartVisualizer
ResultCollectorHelper rch = new ResultCollectorHelper(rc, lv);
XStreamJTLParser p = new XStreamJTLParser(f, rch);
p.parse();
lv.writeOutput(); //write the output
File f = new File(JMETER_RESULT_FILE);
ResultCollector rc = new ResultCollector();
LineChartVisualizer lcv = new LineChartVisualizer(OUTPUT_GRAPH_DIR + "/AllLineChart.png");
StackedBarChartVisualizer sbv = new StackedBarChartVisualizer(OUTPUT_GRAPH_DIR + "/AllStackedBarChart.png");
MinMaxAvgGraphVisualizer mmav = new MinMaxAvgGraphVisualizer(OUTPUT_GRAPH_DIR + "/AllMinMaxAvg.png");
String[] labels = {"Component reference"};
LabelFilterVisualizer lv= new LabelFilterVisualizer(Arrays.asList(labels), sbv);//decorate
LabelFilterVisualizer lv2= new LabelFilterVisualizer(Arrays.asList(labels), mmav);//decorate
OfflineVisualizer[] vs = {lcv, lv,lv2};//use these 3 visualizers
CompositeVisualizer cv = new CompositeVisualizer(Arrays.asList(vs));//create a composite
ResultCollectorHelper rch = new ResultCollectorHelper(rc, cv);
XStreamJTLParser p = new XStreamJTLParser(f, rch);
p.parse();
cv.writeOutput();//the composite will delegate to each visualizer

String [] files = {JMETER_RESULT_DIR + "/OfflineGraphs-dev-200912311310.jtl", JMETER_RESULT_DIR + "/OfflineGraphs-dev-200912311312.jtl",JMETER_RESULT_DIR + "/OfflineGraphs-dev-200912311315.jtl",JMETER_RESULT_DIR + "/OfflineGraphs-dev-200912311316.jtl",JMETER_RESULT_DIR + "/OfflineGraphs-dev-200912311318.jtl"};
MultiFileThroughput mft = new MultiFileThroughput(Arrays.asList(files),OUTPUT_GRAPH_DIR + "/Throughput.png");
mft.parse();
mft.writeOutput();
Script available at Spider.jmxStringBuffer sb = new StringBuffer();
int i;
while ((i = r.read()) != -1)
sb.append((char)i);
int i;
char[] data = new char[1024];while ((i = r.read(data,0,data.length)) != -1)
sb.append(data);
Edit: Embarassingly the above code is wrong, but the code is only illustrative , reading a character at a time v/s reading chunks of it
StringBuffer sb = new StringBuffer();would you even bother?
int i;
// under JIT, testing seems to show this simple loop is as fast
// as any of the alternatives
while ((i = r.read()) != -1)
sb.append((char)i);
The test plan is configured to run the Thread Groups serially