Index: chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/history/SaveHistoryMojo.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/history/SaveHistoryMojo.java (revision 15979) +++ chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/history/SaveHistoryMojo.java (revision ) @@ -39,7 +39,10 @@ import org.codehaus.mojo.chronos.common.model.GCSamples; import org.codehaus.mojo.chronos.common.model.GroupedResponsetimeSamples; import org.codehaus.mojo.chronos.common.model.HistoricSample; +import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLStreamException; import java.io.File; import java.io.IOException; @@ -92,9 +95,14 @@ HistoricSample history = new HistoricSample( responseSamples, gcSamples ); historicDataDirectory.writeHistorySample( history ); } - catch ( IOException ex ) - { - throw new MojoExecutionException( "unable to find gcsamples with dataid=" + dataid, ex ); + catch ( IOException e ) { + throw new MojoExecutionException( "unable to find gcsamples with dataid=" + dataid, e ); + } catch (SAXException e) { + throw new MojoExecutionException( "unable to find gcsamples with dataid=" + dataid, e ); + } catch (ParserConfigurationException e) { + throw new MojoExecutionException( "unable to find gcsamples with dataid=" + dataid, e ); + } catch (XMLStreamException e) { + throw new MojoExecutionException( "unable to find gcsamples with dataid=" + dataid, e ); } } } \ No newline at end of file Index: chronos-report-maven-plugin/src/test/java/org/codehaus/mojo/chronos/report/jmeter/JMeterLogParserTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-report-maven-plugin/src/test/java/org/codehaus/mojo/chronos/report/jmeter/JMeterLogParserTest.java (revision 15979) +++ chronos-report-maven-plugin/src/test/java/org/codehaus/mojo/chronos/report/jmeter/JMeterLogParserTest.java (revision ) @@ -30,6 +30,7 @@ package org.codehaus.mojo.chronos.report.jmeter; import junit.framework.TestCase; +import org.codehaus.mojo.chronos.common.ResponsetimeXMLFileHandler; import org.codehaus.mojo.chronos.common.model.GroupedResponsetimeSamples; import org.codehaus.mojo.chronos.common.model.ResponsetimeSampleGroup; @@ -51,7 +52,7 @@ */ public void testParseJMeterLog() throws Exception { File file = new File("src/test/resources/test1-junitsamples.xml"); - GroupedResponsetimeSamples samples = GroupedResponsetimeSamples.fromXmlFile(file); + GroupedResponsetimeSamples samples = new ResponsetimeXMLFileHandler(file).getGroupedResponsetimeSamples(); assertEquals(6, samples.getSampleGroups().size()); for (Iterator it = samples.getSampleGroups().iterator(); it.hasNext();) { ResponsetimeSampleGroup sampleGroup = (ResponsetimeSampleGroup)it.next(); @@ -67,7 +68,7 @@ */ public void testParseJmeter23WebLog() throws Exception { File file = new File("src/test/resources/webtest-jmeter22-resulttable.xml"); - GroupedResponsetimeSamples samples = GroupedResponsetimeSamples.fromXmlFile(file); + GroupedResponsetimeSamples samples = new ResponsetimeXMLFileHandler(file).getGroupedResponsetimeSamples(); assertEquals(2, samples.getSampleGroups().size()); for (Iterator it = samples.getSampleGroups().iterator(); it.hasNext();) { ResponsetimeSampleGroup sampleGroup = (ResponsetimeSampleGroup)it.next(); @@ -83,7 +84,7 @@ */ public void testJtl20Combined() throws Exception { File file = new File("src/test/resources/combinedtest-jtl20-summaryreport.xml"); - GroupedResponsetimeSamples samples = GroupedResponsetimeSamples.fromXmlFile(file); + GroupedResponsetimeSamples samples = new ResponsetimeXMLFileHandler(file).getGroupedResponsetimeSamples(); assertEquals(4, samples.getSampleGroups().size()); for (Iterator it = samples.getSampleGroups().iterator(); it.hasNext();) { ResponsetimeSampleGroup sampleGroup = (ResponsetimeSampleGroup)it.next(); @@ -99,7 +100,7 @@ */ public void testJtl21Combined() throws Exception { File file = new File("src/test/resources/combinedtest-jtl21-summaryreport.xml"); - GroupedResponsetimeSamples samples = GroupedResponsetimeSamples.fromXmlFile(file); + GroupedResponsetimeSamples samples = new ResponsetimeXMLFileHandler(file).getGroupedResponsetimeSamples(); assertEquals(4, samples.getSampleGroups().size()); for (Iterator it = samples.getSampleGroups().iterator(); it.hasNext();) { ResponsetimeSampleGroup sampleGroup = (ResponsetimeSampleGroup)it.next(); @@ -115,7 +116,7 @@ */ public void testJtl22Combined2() throws Exception { File file = new File("src/test/resources/combinedtest-jtl22-summaryreport.xml"); - GroupedResponsetimeSamples samples = GroupedResponsetimeSamples.fromXmlFile(file); + GroupedResponsetimeSamples samples = new ResponsetimeXMLFileHandler(file).getGroupedResponsetimeSamples(); assertEquals(4, samples.getSampleGroups().size()); for (Iterator it = samples.getSampleGroups().iterator(); it.hasNext();) { ResponsetimeSampleGroup sampleGroup = (ResponsetimeSampleGroup)it.next(); @@ -132,7 +133,7 @@ */ public void testJtlNestedHttpSample() throws Exception { File file = new File("src/test/resources/jmeter-nested-httpsample.xml"); - GroupedResponsetimeSamples samples = GroupedResponsetimeSamples.fromXmlFile(file); + GroupedResponsetimeSamples samples = new ResponsetimeXMLFileHandler(file).getGroupedResponsetimeSamples(); assertEquals(4, samples.getSampleGroups().size()); for (Iterator it = samples.getSampleGroups().iterator(); it.hasNext();) { ResponsetimeSampleGroup sampleGroup = (ResponsetimeSampleGroup)it.next(); Index: chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/chart/ChronosHistogramPlugin.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/chart/ChronosHistogramPlugin.java (revision 15979) +++ chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/chart/ChronosHistogramPlugin.java (revision ) @@ -39,6 +39,7 @@ * Plugin adding histogram charts to the reports. * * @author ksr@lakeside.dk + * @author Dragisa Krsmanovic */ public final class ChronosHistogramPlugin implements ChronosReportPlugin @@ -58,12 +59,11 @@ public Map getDetailChartSources() { Map testname2ChartSource = new LinkedHashMap(); - if ( !( samples instanceof GroupedResponsetimeSamples ) ) + if ( samples == null ) { return testname2ChartSource; } - GroupedResponsetimeSamples groupedSamples = samples; - for ( ResponsetimeSampleGroup sampleGroup : groupedSamples.getSampleGroups() ) + for ( ResponsetimeSampleGroup sampleGroup : samples.getSampleGroups() ) { testname2ChartSource.put( sampleGroup.getName(), new DetailsHistogramChartSource( sampleGroup ) ); } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/GCSample.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/GCSample.java (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/GCSample.java (revision ) @@ -29,12 +29,14 @@ */ package org.codehaus.mojo.chronos.common.model; -import org.cdmckay.coffeedom.Element; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; /** * Contains info from a garbagecollection logentry. * * @author ksr@lakeside.dk + * @author Dragisa Krsmanovic */ public final class GCSample { @@ -107,20 +109,12 @@ return processingTime; } - /** - * Parse an org.jdom.Element into an GCSample instance. - * - * @param gcXML org.jdom.Element containing the XML representation of an GCSample object. - * @return The resulting GCSample. - */ - public static GCSample fromXML( Element gcXML ) - { - double timestamp = Double.parseDouble( gcXML.getAttributeValue( "timestamp" ) ); - int heapBefore = Integer.parseInt( gcXML.getAttributeValue( "heapBefore" ) ); - int heapAfter = Integer.parseInt( gcXML.getAttributeValue( "heapAfter" ) ); - int heapTotal = Integer.parseInt( gcXML.getAttributeValue( "heapTotal" ) ); - double processingTime = Double.parseDouble( gcXML.getAttributeValue( "processingTime" ) ); - - return new GCSample( timestamp, heapBefore, heapAfter, heapTotal, processingTime ); + public void writeTo(XMLStreamWriter writer) throws XMLStreamException { + writer.writeEmptyElement("gcsample"); + writer.writeAttribute("timestamp", Double.toString(timestamp)); + writer.writeAttribute("heapBefore", Integer.toString(heapBefore)); + writer.writeAttribute("heapAfter", Integer.toString(heapAfter)); + writer.writeAttribute("heapTotal", Integer.toString(heapTotal)); + writer.writeAttribute("processingTime", Double.toString(processingTime)); } } \ No newline at end of file Index: pom.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- pom.xml (revision 15979) +++ pom.xml (revision ) @@ -1,5 +1,6 @@ - + 4.0.0 Chronos org.codehaus.mojo @@ -59,8 +60,8 @@ chronos-plugincommon chronos-report-maven-plugin chronos-jmeter-maven-plugin - chronos-jmeterapi - chronos-samples + chronos-jmeterapi + chronos-samples @@ -88,18 +89,7 @@ - - - org.cdmckay.coffeedom - coffeedom - 1.0.0 - + jfree jfreechart Index: chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/ReportMojo.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/ReportMojo.java (revision 15979) +++ chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/ReportMojo.java (revision ) @@ -44,7 +44,9 @@ import org.codehaus.mojo.chronos.report.chart.ChartUtil; import org.codehaus.mojo.chronos.report.chart.ChronosReportPlugin; import org.codehaus.mojo.chronos.report.chart.GraphGenerator; +import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.util.List; import java.util.Locale; @@ -289,7 +291,7 @@ GCSamples gcSamples = testDataDirectory.readGCSamples(); List defaultPlugins = ChartUtil.createDefaultPlugins( jmeterSamples, gcSamples ); - GraphGenerator graphGenerator = new GraphGenerator( defaultPlugins ); + GraphGenerator graphGenerator = new GraphGenerator( defaultPlugins, getLog() ); ChartRenderer renderer = new ChartRendererImpl( getOutputDirectory() ); graphGenerator.generateGraphs( renderer, getBundle( locale ), getConfig() ); @@ -302,6 +304,10 @@ } catch ( IOException e ) { + throw new MavenReportException( "ReportGenerator failed", e ); + } catch (SAXException e) { + throw new MavenReportException( "ReportGenerator failed", e ); + } catch (ParserConfigurationException e) { throw new MavenReportException( "ReportGenerator failed", e ); } } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/HistoricSample.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/HistoricSample.java (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/HistoricSample.java (revision ) @@ -29,13 +29,8 @@ */ package org.codehaus.mojo.chronos.common.model; -import org.cdmckay.coffeedom.CoffeeDOMException; -import org.cdmckay.coffeedom.Document; -import org.cdmckay.coffeedom.Element; -import org.cdmckay.coffeedom.input.SAXBuilder; - -import java.io.File; -import java.io.IOException; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -46,9 +41,9 @@ * This is a historic sample representing the statistics from a previous run. * * @author ksr@lakeside.dk + * @author Dragisa Krsmanovic */ -public class HistoricSample -{ +public class HistoricSample { private static final int DEFAULT_DURATION = 20000; private long timestamp; @@ -67,171 +62,147 @@ private double maxAverageThroughput = -1d; - public HistoricSample( GroupedResponsetimeSamples responseSamples, GCSamples gcSamples ) - { + public HistoricSample(GroupedResponsetimeSamples responseSamples, GCSamples gcSamples) { timestamp = responseSamples.getTestStartTime(); responsetimeAverage = responseSamples.getAverage(); responsetime95Percentile = responseSamples.getPercentile95(); individualAverages = new HashMap(); individualPercentiles = new HashMap(); Iterator it = responseSamples.getSampleGroups().iterator(); - while ( it.hasNext() ) - { + while (it.hasNext()) { ResponsetimeSampleGroup group = (ResponsetimeSampleGroup) it.next(); - individualAverages.put( group.getName(), group.getAverage() ); + individualAverages.put(group.getName(), group.getAverage()); - individualPercentiles.put( group.getName(), group.getPercentile95() ); + individualPercentiles.put(group.getName(), group.getPercentile95()); } - if ( gcSamples != null ) - { + if (gcSamples != null) { - gcRatio = gcSamples.getGarbageCollectionRatio( responseSamples.getTotalTime() ); + gcRatio = gcSamples.getGarbageCollectionRatio(responseSamples.getTotalTime()); - collectedPrSecond = gcSamples.getCollectedKBPerSecond( responseSamples.getTotalTime() ); + collectedPrSecond = gcSamples.getCollectedKBPerSecond(responseSamples.getTotalTime()); } - int averageDuration = Math.max( DEFAULT_DURATION, (int) responsetime95Percentile ); + int averageDuration = Math.max(DEFAULT_DURATION, (int) responsetime95Percentile); - maxAverageThroughput = responseSamples.getMaxAverageThroughput( averageDuration ); + maxAverageThroughput = responseSamples.getMaxAverageThroughput(averageDuration); } - private HistoricSample() - { + public HistoricSample() { // Do nothing } /** * @return Returns the timestamp. */ - public final long getTimestamp() - { + public final long getTimestamp() { return timestamp; } /** * @return Returns the gcRatio. */ - public final double getGcRatio() - { + public final double getGcRatio() { return gcRatio; } /** * @return Returns the collectedPrSecond. */ - public final double getCollectedPrSecond() - { + public final double getCollectedPrSecond() { return collectedPrSecond; } /** * @return Returns the responsetimeAverage. */ - public final double getResponsetimeAverage() - { + public final double getResponsetimeAverage() { return responsetimeAverage; } /** * @return Returns the responsetime95Percrntile. */ - public final double getResponsetime95Percentile() - { + public final double getResponsetime95Percentile() { return responsetime95Percentile; } - public final Set getGroupNames() - { + public final Set getGroupNames() { return individualAverages.keySet(); } - public final double getResponsetimeAverage( String groupName ) - { + public final double getResponsetimeAverage(String groupName) { - return individualAverages.get( groupName ); + return individualAverages.get(groupName); } - public final double getResponsetimePercentiles( String groupName ) - { + public final double getResponsetimePercentiles(String groupName) { - return individualPercentiles.get( groupName ); + return individualPercentiles.get(groupName); } /** * @return Returns the maxAverageThroughput. */ - public final double getMaxAverageThroughput() - { + public final double getMaxAverageThroughput() { return maxAverageThroughput; } - public final Element toXML() - { - Element historyXML = new Element( "history" ); + public final void writeTo(XMLStreamWriter writer) throws XMLStreamException { - historyXML.setAttribute( "timestamp", Long.toString( timestamp ) ); - historyXML.setAttribute( "gcRatio", Double.toString( gcRatio ) ); - historyXML.setAttribute( "collectedPrSecond", Double.toString( collectedPrSecond ) ); + writer.writeStartElement("history"); + writer.writeAttribute("timestamp", Long.toString(timestamp)); - historyXML.setAttribute( "responsetimeAverage", Double.toString( responsetimeAverage ) ); - historyXML.setAttribute( "responsetime95Percentile", Double.toString( responsetime95Percentile ) ); - historyXML.setAttribute( "maxAverageThroughput", Double.toString( maxAverageThroughput ) ); + writer.writeAttribute("gcRatio", Double.toString(gcRatio)); + writer.writeAttribute("collectedPrSecond", Double.toString(collectedPrSecond)); - Element individualPercentilesXML = new Element( "individualPercentiles" ); - for ( Iterator> iterator = individualPercentiles.entrySet().iterator(); - iterator.hasNext(); ) - { - Entry entry = iterator.next(); + writer.writeAttribute("responsetimeAverage", Double.toString(responsetimeAverage)); + writer.writeAttribute("responsetime95Percentile", Double.toString(responsetime95Percentile)); + writer.writeAttribute("maxAverageThroughput", Double.toString(maxAverageThroughput)); - individualPercentilesXML.addContent( - new Element( "entry" ).setAttribute( "key", entry.getKey() ).setAttribute( "value", - entry.getValue().toString() ) ); + writer.writeStartElement("individualPercentiles"); + + for (Entry entry : individualPercentiles.entrySet()) { + writer.writeEmptyElement("entry"); + writer.writeAttribute("key", entry.getKey()); + writer.writeAttribute("value", entry.getValue().toString()); } - historyXML.addContent( individualPercentilesXML ); - Element individualAveragesXML = new Element( "individualAverages" ); - for ( Iterator> iterator = individualAverages.entrySet().iterator(); iterator.hasNext(); ) - { - Entry entry = iterator.next(); + writer.writeEndElement(); // end individualPercentiles - individualAveragesXML.addContent( - new Element( "entry" ).setAttribute( "key", entry.getKey() ).setAttribute( "value", - entry.getValue().toString() ) ); + writer.writeStartElement("individualAverages"); + + for (Entry entry : individualAverages.entrySet()) { + writer.writeEmptyElement("entry"); + writer.writeAttribute("key", entry.getKey()); + writer.writeAttribute("value", entry.getValue().toString()); } - historyXML.addContent( individualAveragesXML ); + writer.writeEndElement(); // end individualAverages - return historyXML; + writer.writeEndElement(); // end history } - public static HistoricSample fromXML( File file ) - throws IOException - { - SAXBuilder builder = new SAXBuilder(); - Document document = builder.build( file ); + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } - Element historyXML = document.getRootElement(); - if ( !"history".equals( historyXML.getName() ) ) - { - throw new CoffeeDOMException( - "Invalid XML structure - history tag expected, but was " + historyXML.getName() ); + public void setGcRatio(double gcRatio) { + this.gcRatio = gcRatio; - } + } - HistoricSample hs = new HistoricSample(); - hs.timestamp = Long.parseLong( historyXML.getAttributeValue( "timestamp" ) ); - hs.gcRatio = Double.parseDouble( historyXML.getAttributeValue( "gcRatio" ) ); - hs.collectedPrSecond = Double.parseDouble( historyXML.getAttributeValue( "collectedPrSecond" ) ); + public void setCollectedPrSecond(double collectedPrSecond) { + this.collectedPrSecond = collectedPrSecond; + } - hs.responsetimeAverage = Double.parseDouble( historyXML.getAttributeValue( "responsetimeAverage" ) ); - hs.responsetime95Percentile = Double.parseDouble( historyXML.getAttributeValue( "responsetime95Percentile" ) ); - hs.maxAverageThroughput = Double.parseDouble( historyXML.getAttributeValue( "maxAverageThroughput" ) ); + public void setResponsetimeAverage(double responsetimeAverage) { + this.responsetimeAverage = responsetimeAverage; + } - hs.individualPercentiles = populateMap( historyXML.getChild( "individualPercentiles" ) ); - hs.individualAverages = populateMap( historyXML.getChild( "individualAverages" ) ); + public void setResponsetime95Percentile(double responsetime95Percentile) { + this.responsetime95Percentile = responsetime95Percentile; + } - return hs; + public void setMaxAverageThroughput(double maxAverageThroughput) { + this.maxAverageThroughput = maxAverageThroughput; } - private static Map populateMap( Element xml ) - { - Map res = new HashMap(); - - for ( Element entryXML : xml.getChildren() ) - { - res.put( entryXML.getAttributeValue( "key" ), Double.valueOf( entryXML.getAttributeValue( "value" ) ) ); + public void setIndividualPercentiles(Map individualPercentiles) { + this.individualPercentiles = individualPercentiles; - } + } - return res; + + public void setIndividualAverages(Map individualAverages) { + this.individualAverages = individualAverages; } } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/GroupedResponsetimeSamples.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/GroupedResponsetimeSamples.java (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/GroupedResponsetimeSamples.java (revision ) @@ -29,15 +29,15 @@ */ package org.codehaus.mojo.chronos.common.model; -import org.cdmckay.coffeedom.Document; -import org.cdmckay.coffeedom.Element; -import org.cdmckay.coffeedom.input.SAXBuilder; import org.codehaus.mojo.chronos.common.IOUtil; +import org.codehaus.mojo.chronos.common.ResponsetimeXMLFileHandler; import org.jfree.data.time.Millisecond; import org.jfree.data.time.MovingAverage; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesDataItem; +import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -54,68 +54,42 @@ * A grouping collection of samples (grouped by the name of the samples). * * @author ksr@lakeside.dk + * @author Dragisa Krsmanovic */ public final class GroupedResponsetimeSamples extends ResponsetimeSamples { - private static final String GROUPEDRESPONSETIMESAMPLES_TAG = "groupedresponsetimesamples"; + private final SortedMap sampleGroupsByName = new TreeMap(); - private static final String SUCCEEDED_ATTRIBUTE = "succeeded"; - - private static final String GROUP_TAG = "responsetimesamplegroup"; - - private final SortedMap sampleGroupsByName = - new TreeMap(); - - public static GroupedResponsetimeSamples fromXmlFile( File file ) - throws IOException - { - GroupedResponsetimeSamples res = new GroupedResponsetimeSamples(); - res.addXml( file ); - return res; - } - public GroupedResponsetimeSamples() { super(); this.responseTimeSeries = new TimeSeries( "Individual samples" ); // label shown for the series in the responsetime graph - } - public void addXml( File file ) - throws IOException - { - IOUtil.copyDTDToDir( "chronos-responsetimesamples.dtd", file.getParentFile() ); - Document doc = new SAXBuilder().build( file ); - - String rootElementName = doc.getRootElement().getName(); - if ( !rootElementName.equals( GROUPEDRESPONSETIMESAMPLES_TAG ) ) - { - String message = "Unexpected rootelement: " + rootElementName; - throw new IllegalArgumentException( message ); - } + } - Element xml = doc.getRootElement(); - succeeded = Integer.parseInt( xml.getAttributeValue( SUCCEEDED_ATTRIBUTE ) ); - - List groups = xml.getChildren( GROUP_TAG ); - for ( Element gXml : groups ) + public GroupedResponsetimeSamples(int succeeded) - { + { - if ( !gXml.getName().equals( "responsetimesamplegroup" ) ) - { - throw new IllegalArgumentException( "Unknown tag: " + gXml.getName() ); + this(); + this.succeeded = succeeded; - } + } - String groupName = gXml.getAttributeValue( "name" ); - ResponsetimeSampleGroup sampleGroup = new ResponsetimeSampleGroup( groupName ); - sampleGroupsByName.put( groupName, sampleGroup ); - - for ( Element sXml : gXml.getChildren() ) - { - ResponsetimeSample sample = ResponsetimeSample.fromXml( sXml ); - sampleGroup.add( sample ); + public void add(ResponsetimeSampleGroup group) { + sampleGroupsByName.put( group.getName(), group); + for (ResponsetimeSample sample : group.getSamples()) { - add( sample ); + add(sample); - } + } + } + + public void addXml( File file ) + throws IOException, SAXException, ParserConfigurationException { + + IOUtil.copyDTDToDir( "chronos-responsetimesamples.dtd", file.getParentFile() ); + GroupedResponsetimeSamples groupedResponsetimeSamples = new ResponsetimeXMLFileHandler(file).getGroupedResponsetimeSamples(); + + for (ResponsetimeSampleGroup sampleGroup : groupedResponsetimeSamples.getSampleGroups()) { + add(sampleGroup); } } \ No newline at end of file Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/GCSampleXMLFileHandler.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/GCSampleXMLFileHandler.java (revision ) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/GCSampleXMLFileHandler.java (revision ) @@ -0,0 +1,109 @@ +/* + * The MIT License + * + * Original work sponsored and donated by National Board of e-Health (NSI), Denmark (http://www.nsi.dk) + * Further enhancement before move to Codehaus sponsored and donated by Lakeside A/S (http://www.lakeside.dk) + * + * Copyright (c) to all contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $HeadURL:$ + * $Id:$ + */ +package org.codehaus.mojo.chronos.common; + + +import org.codehaus.mojo.chronos.common.model.GCSample; +import org.codehaus.mojo.chronos.common.model.GCSamples; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.File; +import java.io.IOException; + +/** + * SAX Parser for GC sample file. Not thread safe. + * + * @author Dragisa Krsmanovic + */ + +public class GCSampleXMLFileHandler extends DefaultHandler { + + private boolean inSamples = false; + + + private GCSamples samples = null; + + + public GCSampleXMLFileHandler(File file) throws IOException, SAXException, ParserConfigurationException { + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxParserFactory.newSAXParser(); + saxParser.parse(file, this); + } + + /** + * @param uri See {@link org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @param localName See {@link org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @param qName See {@link org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @param attributes See {@link org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @throws org.xml.sax.SAXException See {@link org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @see org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes) + */ + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + if ("gcsamples".equals(qName)) { + samples = new GCSamples(); + inSamples = true; + } else if ("gcsample".equals(qName)) { + if (!inSamples) { + throw new SAXException("gcsample should be inside gcsamples"); + } + double timestamp = Double.parseDouble(attributes.getValue("timestamp")); + int heapBefore = Integer.parseInt(attributes.getValue(("heapBefore"))); + int heapAfter = Integer.parseInt(attributes.getValue(("heapAfter"))); + int heapTotal = Integer.parseInt(attributes.getValue(("heapTotal"))); + double processingTime = Double.parseDouble(attributes.getValue(("processingTime"))); + samples.add(new GCSample(timestamp, heapBefore, heapAfter, heapTotal, processingTime)); + } + } + + /** + * @param uri See {@link org.xml.sax.helpers.DefaultHandler#endElement(String, String, String)} + * @param localName See {@link org.xml.sax.helpers.DefaultHandler#endElement(String, String, String)} + * @param qName See {@link org.xml.sax.helpers.DefaultHandler#endElement(String, String, String)} + * @throws org.xml.sax.SAXException See {@link org.xml.sax.helpers.DefaultHandler#endElement(String, String, String)} + * @see org.xml.sax.helpers.DefaultHandler#endElement(String, String, String) + */ + public void endElement(String uri, String localName, String qName) + throws SAXException { + if ("gcsamples".equals(qName)) { + inSamples = false; + } + } + + + public GCSamples getSamples() { + return samples; + } +} Index: chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterTestMojo.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterTestMojo.java (revision 15979) +++ chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterTestMojo.java (revision ) @@ -222,8 +222,8 @@ } final ProjectBaseDir projectBaseDir = new ProjectBaseDir( project, getLog() ); final TestDataDirectory testDataDirectory = projectBaseDir.getDataDirectory( dataid ); - JMeterOutputMojo.parseJMeterOutput( testDataDirectory, outputJtlFile ); - JMeterOutputMojo.parseGCLog( testDataDirectory, getGcLogFile() ); + JMeterOutputMojo.convertJMeterOutput(testDataDirectory, outputJtlFile, getLog()); + JMeterOutputMojo.convertGCLog(testDataDirectory, getGcLogFile(), getLog()); } interface CommandLauncher Index: chronos-report-maven-plugin/src/test/java/org/codehaus/mojo/chronos/report/ReportGeneratorTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-report-maven-plugin/src/test/java/org/codehaus/mojo/chronos/report/ReportGeneratorTest.java (revision 15979) +++ chronos-report-maven-plugin/src/test/java/org/codehaus/mojo/chronos/report/ReportGeneratorTest.java (revision ) @@ -30,8 +30,10 @@ package org.codehaus.mojo.chronos.report; import junit.framework.TestCase; +import org.apache.maven.plugin.logging.SystemStreamLog; import org.apache.maven.reporting.MavenReportException; import org.codehaus.doxia.sink.Sink; +import org.codehaus.mojo.chronos.common.ResponsetimeXMLFileHandler; import org.codehaus.mojo.chronos.common.model.GroupedResponsetimeSamples; import org.codehaus.mojo.chronos.report.chart.ChronosHistogramPlugin; import org.codehaus.mojo.chronos.report.chart.GraphGenerator; @@ -55,7 +57,7 @@ protected void setUp() throws Exception { File file = new File("src/test/resources/test1-junitsamples.xml"); - samples = GroupedResponsetimeSamples.fromXmlFile(file); + samples = new ResponsetimeXMLFileHandler(file).getGroupedResponsetimeSamples(); bundle = Utils.getBundle(Locale.getDefault()); } @@ -67,7 +69,7 @@ public void testDoGenerateReport() throws MavenReportException { ReportConfigStub config = new ReportConfigStub(); List plugins = Collections.singletonList(new ChronosHistogramPlugin(samples)); - ReportGenerator gen = new ReportGenerator(bundle, config, new GraphGenerator(plugins)); + ReportGenerator gen = new ReportGenerator(bundle, config, new GraphGenerator(plugins, new SystemStreamLog())); gen.doGenerateReport(new SinkStub(), samples); } } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/ResponsetimeSample.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/ResponsetimeSample.java (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/ResponsetimeSample.java (revision ) @@ -29,7 +29,8 @@ */ package org.codehaus.mojo.chronos.common.model; -import org.cdmckay.coffeedom.Element; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; /** * Contains info from a jmeter logentry. @@ -97,19 +98,12 @@ } - /** - * Transforms the xml into a ResponsetimeSample entity. - * - * @param xml The xml to parse. - * @return The corresponding ResponsetimeSample instance. - */ - public static ResponsetimeSample fromXml( Element xml ) - { - int responsetime = Integer.parseInt( xml.getAttributeValue( "responsetime" ) ); - long timestamp = Long.parseLong( xml.getAttributeValue( "timestamp" ) ); - boolean success = Boolean.parseBoolean( xml.getAttributeValue( "success" ) ); - String threadId = xml.getAttributeValue( "threadId" ); - return new ResponsetimeSample( responsetime, timestamp, success, threadId ); + public void writeTo(XMLStreamWriter writer) throws XMLStreamException { + writer.writeEmptyElement("sample"); + writer.writeAttribute("responsetime", Integer.toString(responsetime)); + writer.writeAttribute("timestamp", Long.toString(timestamp)); + writer.writeAttribute("success", Boolean.toString(success)); + writer.writeAttribute("threadId", threadId); } } \ No newline at end of file Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/TestDataDirectory.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/TestDataDirectory.java (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/TestDataDirectory.java (revision ) @@ -30,11 +30,11 @@ package org.codehaus.mojo.chronos.common; import org.apache.maven.plugin.logging.Log; -import org.cdmckay.coffeedom.DocType; -import org.cdmckay.coffeedom.Element; import org.codehaus.mojo.chronos.common.model.GCSamples; import org.codehaus.mojo.chronos.common.model.GroupedResponsetimeSamples; +import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; @@ -42,8 +42,7 @@ /** * Representation of the datadirectory for a single performancetest. */ -public class TestDataDirectory -{ +public class TestDataDirectory { private File dataDirectory; /** @@ -62,15 +61,13 @@ private Log log; - TestDataDirectory( File chronosDir, String dataId, Log log ) - { + TestDataDirectory(File chronosDir, String dataId, Log log) { - this.dataDirectory = new File( chronosDir, dataId ); + this.dataDirectory = new File(chronosDir, dataId); this.name = dataId; this.log = log; } - public String getDataId() - { + public String getDataId() { return name; } @@ -81,17 +78,15 @@ * @throws java.io.IOException Thrown if loading the contents fails. */ public GCSamples readGCSamples() - throws IOException - { + throws IOException, SAXException, ParserConfigurationException { - File[] gcFiles = listFilesWith( GC_FILE_PREFIX, XML_FILE_EXTENSION ); + File[] gcFiles = listFilesWith(GC_FILE_PREFIX, XML_FILE_EXTENSION); GCSamples samples = new GCSamples(); - if ( gcFiles != null ) - { - for ( File gcFile : gcFiles ) - { - GCSamples tmp = GCSamples.fromXML( gcFile ); + if (gcFiles != null) { + for (File gcFile : gcFiles) { + log.debug("Reading " + gcFile.getName()); + GCSamples tmp = new GCSampleXMLFileHandler(gcFile).getSamples(); - samples.addAll( tmp ); + samples.addAll(tmp); } } return samples; @@ -104,13 +99,11 @@ * @throws java.io.IOException Thrown if loading the contents fails. */ public GroupedResponsetimeSamples readResponsetimeSamples() - throws IOException - { + throws IOException, SAXException, ParserConfigurationException { - File[] dirContent = listFilesWith( PERFORMANCESAMPLE_FILE_PREFIX, XML_FILE_EXTENSION ); + File[] dirContent = listFilesWith(PERFORMANCESAMPLE_FILE_PREFIX, XML_FILE_EXTENSION); GroupedResponsetimeSamples result = new GroupedResponsetimeSamples(); - for ( File file : dirContent ) - { + for (File file : dirContent) { - result.addXml( file ); + result.addXml(file); } return result; } @@ -123,47 +116,34 @@ * @param extension The extension to retrieve. * @return File[] containing the matching files - or null if the directory is invalid. */ - public File[] listFilesWith( final String prefix, final String extension ) - { - final FilenameFilter filenameFilter = new FilenameFilter() - { - public boolean accept( File parentDir, String name ) - { + public File[] listFilesWith(final String prefix, final String extension) { + final FilenameFilter filenameFilter = new FilenameFilter() { + public boolean accept(File parentDir, String name) { - return name.startsWith( prefix + "-" ) && name.endsWith( "." + extension ); + return name.startsWith(prefix + "-") && name.endsWith("." + extension); } }; - return IOUtil.listFiles( dataDirectory, filenameFilter ); + return IOUtil.listFiles(dataDirectory, filenameFilter); } - public void writeResponsetimeSamples( String jtlName, Element samplesElement ) - throws IOException - { + public File getResponsetimeSamplesFile(String jtlName) + throws IOException { ensure(); - File performanceSamplesXml = new File( dataDirectory, - IOUtil.getAdjustedFileName( jtlName, PERFORMANCESAMPLE_FILE_PREFIX, + return new File(dataDirectory, IOUtil.getAdjustedFileName(jtlName, PERFORMANCESAMPLE_FILE_PREFIX, - XML_FILE_EXTENSION ) ); + XML_FILE_EXTENSION)); - final DocType docType = new DocType( "responsetimesamples", "SYSTEM", "chronos-responsetimesamples.dtd" ); - IOUtil.writeXmlToFile( performanceSamplesXml, samplesElement, docType ); } - public void writeGCLog( Element gcSamplesXml ) - throws IOException - { + public File getGCLogFile() + throws IOException { ensure(); - - File file = new File( dataDirectory, GC_FILE_PREFIX + "-" + name + '.' + XML_FILE_EXTENSION ); - log.debug( "Writing garbage collection log to " + file ); - IOUtil.writeXmlToFile( file, gcSamplesXml, new DocType( "gcsamples", "SYSTEM", "chronos-gc.dtd" ) ); + return new File(dataDirectory, GC_FILE_PREFIX + "-" + name + '.' + XML_FILE_EXTENSION); } - public TestDataDirectory ensure() - { + public TestDataDirectory ensure() { - IOUtil.ensureDir( dataDirectory ); + IOUtil.ensureDir(dataDirectory); return this; } - public File getDirectory() - { + public File getDirectory() { return dataDirectory; } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/ResponsetimeSampleGroup.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/ResponsetimeSampleGroup.java (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/ResponsetimeSampleGroup.java (revision ) @@ -31,6 +31,10 @@ import org.jfree.data.time.TimeSeries; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.util.List; + /** * @author ksr@lakeside.dk */ @@ -56,6 +60,15 @@ } } + public ResponsetimeSampleGroup( String name, int index ) + { + this.name = name; + this.responseTimeSeries = new TimeSeries( name ); + this.index = index; + this.lastIndex = index; + } + + public final int getIndex() { return index; @@ -67,6 +80,22 @@ public final String getName() { return name; + } + + + public List getSamples() { + return this.samples; + } + + + public void writeTo(XMLStreamWriter writer) throws XMLStreamException { + writer.writeStartElement("responsetimesamplegroup"); + writer.writeAttribute("name", name); + writer.writeAttribute("index", Integer.toString(index)); + for (ResponsetimeSample sample : samples) { + sample.writeTo(writer); + } + writer.writeEndElement(); } } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/HistoricSampleXMLFileHandler.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/HistoricSampleXMLFileHandler.java (revision ) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/HistoricSampleXMLFileHandler.java (revision ) @@ -0,0 +1,155 @@ +/* + * The MIT License + * + * Original work sponsored and donated by National Board of e-Health (NSI), Denmark (http://www.nsi.dk) + * Further enhancement before move to Codehaus sponsored and donated by Lakeside A/S (http://www.lakeside.dk) + * + * Copyright (c) to all contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $HeadURL:$ + * $Id:$ + */ +package org.codehaus.mojo.chronos.common; + + +import org.codehaus.mojo.chronos.common.model.HistoricSample; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * SAX parser for historic sample XML. Not thread safe. + * + * @author Dragisa Krsmanovic + */ +public class HistoricSampleXMLFileHandler extends DefaultHandler { + + private boolean inHistory = false; + private boolean inPercentiles = false; + private boolean inAverages = false; + + + private HistoricSample historicSample = null; + + private Map currentPercentiles = null; + private Map currentAverages = null; + + + public HistoricSampleXMLFileHandler(File file) throws IOException, SAXException, ParserConfigurationException { + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxParserFactory.newSAXParser(); + saxParser.parse(file, this); + } + + /** + * @param uri See {@link org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @param localName See {@link org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @param qName See {@link org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @param attributes See {@link org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @throws org.xml.sax.SAXException See {@link org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @see org.xml.sax.helpers.DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes) + */ + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + if ("history".equals(qName)) { + historicSample = new HistoricSample(); + historicSample.setTimestamp(Long.parseLong(attributes.getValue("timestamp"))); + historicSample.setGcRatio(Double.parseDouble(attributes.getValue("gcRatio"))); + historicSample.setCollectedPrSecond(Double.parseDouble(attributes.getValue("collectedPrSecond"))); + + historicSample.setResponsetimeAverage(Double.parseDouble(attributes.getValue("responsetimeAverage"))); + historicSample.setResponsetime95Percentile(Double.parseDouble(attributes.getValue("responsetime95Percentile"))); + historicSample.setMaxAverageThroughput(Double.parseDouble(attributes.getValue("maxAverageThroughput"))); + inHistory = true; + + } else if ("individualPercentiles".equals(qName)) { + if (!inHistory) { + throw new SAXException("individualPercentiles should be inside history"); + } + currentPercentiles = new HashMap(); + inPercentiles = true; + } else if ("individualAverages".equals(qName)) { + if (!inHistory) { + throw new SAXException("individualAverages should be inside history"); + } + currentAverages = new HashMap(); + inAverages = true; + } else if ("entry".equals(qName)) { + if (!inAverages & !inPercentiles) { + throw new SAXException("entry should be inside individualPercentiles or individualAverages"); + } + + if (inAverages) { + currentAverages.put(attributes.getValue("key"), Double.parseDouble(attributes.getValue("value"))); + } else if (inPercentiles) { + currentPercentiles.put(attributes.getValue("key"), Double.parseDouble(attributes.getValue("value"))); + } + } + } + + /** + * @param uri See {@link org.xml.sax.helpers.DefaultHandler#endElement(String, String, String)} + * @param localName See {@link org.xml.sax.helpers.DefaultHandler#endElement(String, String, String)} + * @param qName See {@link org.xml.sax.helpers.DefaultHandler#endElement(String, String, String)} + * @throws org.xml.sax.SAXException See {@link org.xml.sax.helpers.DefaultHandler#endElement(String, String, String)} + * @see org.xml.sax.helpers.DefaultHandler#endElement(String, String, String) + */ + public void endElement(String uri, String localName, String qName) + throws SAXException { + if ("history".equals(qName)) { + if (inAverages) { + throw new SAXException("individualAverages not properly ended. Encountered end of history."); + } + if (inPercentiles) { + throw new SAXException("individualPercentiles not properly ended. Encountered end of history."); + } + inHistory = false; + + } else if ("individualPercentiles".equals(qName)) { + if (!inPercentiles) { + throw new SAXException("individualPercentiles not properly ended."); + } + historicSample.setIndividualPercentiles(currentPercentiles); + currentPercentiles = null; + inPercentiles = false; + } else if ("individualAverages".equals(qName)) { + if (!inHistory) { + throw new SAXException("individualAverages not properly ended."); + } + historicSample.setIndividualAverages(currentAverages); + currentAverages = null; + inAverages = false; + } + } + + + public HistoricSample getHistoricSample() { + return historicSample; + } +} Index: chronos-report-maven-plugin/src/test/java/org/codehaus/mojo/chronos/report/chart/GraphGeneratorTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-report-maven-plugin/src/test/java/org/codehaus/mojo/chronos/report/chart/GraphGeneratorTest.java (revision 15979) +++ chronos-report-maven-plugin/src/test/java/org/codehaus/mojo/chronos/report/chart/GraphGeneratorTest.java (revision ) @@ -30,7 +30,9 @@ package org.codehaus.mojo.chronos.report.chart; import junit.framework.TestCase; +import org.apache.maven.plugin.logging.SystemStreamLog; import org.apache.maven.reporting.MavenReportException; +import org.codehaus.mojo.chronos.common.ResponsetimeXMLFileHandler; import org.codehaus.mojo.chronos.common.model.GroupedResponsetimeSamples; import org.codehaus.mojo.chronos.report.ReportConfigStub; import org.codehaus.mojo.chronos.report.Utils; @@ -45,6 +47,7 @@ * Testclass for {@link org.codehaus.mojo.chronos.report.chart.GraphGenerator} * * @author ksr@lakeside.dk + * @author Dragisa Krsmanovic */ public class GraphGeneratorTest extends TestCase { @@ -64,9 +67,10 @@ */ public void testDoGenerateReport() throws Exception { File file = new File("src/test/resources/test2-junitsamples.xml"); - GroupedResponsetimeSamples jmeterSamples = GroupedResponsetimeSamples.fromXmlFile(file); + + GroupedResponsetimeSamples jmeterSamples = new ResponsetimeXMLFileHandler(file).getGroupedResponsetimeSamples(); List plugins = Collections.singletonList(new ChronosHistogramPlugin(jmeterSamples)); - GraphGenerator gen = new GraphGenerator(plugins); + GraphGenerator gen = new GraphGenerator(plugins, new SystemStreamLog()); gen.generateGraphs(renderer, bundle, new ReportConfigStub()); assertEquals(6, renderer.charts.size()); } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/chronos-history.dtd =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/chronos-history.dtd (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/chronos-history.dtd (revision 15979) @@ -1,15 +0,0 @@ - - - - - \ No newline at end of file Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/IOUtil.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/IOUtil.java (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/IOUtil.java (revision ) @@ -29,12 +29,6 @@ */ package org.codehaus.mojo.chronos.common; -import org.cdmckay.coffeedom.DocType; -import org.cdmckay.coffeedom.Document; -import org.cdmckay.coffeedom.Element; -import org.cdmckay.coffeedom.output.Format; -import org.cdmckay.coffeedom.output.XMLOutputter; - import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -42,7 +36,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.OutputStreamWriter; public class IOUtil { @@ -102,23 +95,6 @@ directory.mkdir(); } return directory; - } - - public static void writeXmlToFile( File targetFile, Element xml, DocType doctype ) - throws IOException - { - final File targetDir = targetFile.getParentFile(); - ensureDir( targetDir ); - Document doc = new Document( xml ); - doc.setDocType( doctype ); - copyDTDToDir( doctype.getSystemID(), targetDir ); - - Format format = Format.getCompactFormat(); - - OutputStreamWriter writer = new OutputStreamWriter( new FileOutputStream( targetFile ), "UTF-8" ); - new XMLOutputter( format ).output( doc, writer ); - writer.flush(); - writer.close(); } /** Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/abstractmojo/AbstractCheckMojo.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/abstractmojo/AbstractCheckMojo.java (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/abstractmojo/AbstractCheckMojo.java (revision ) @@ -38,7 +38,9 @@ import org.codehaus.mojo.chronos.common.model.GCSamples; import org.codehaus.mojo.chronos.common.model.GroupedResponsetimeSamples; import org.codehaus.mojo.chronos.common.model.ResponsetimeSamples; +import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; /** @@ -46,6 +48,7 @@ * The class is abstract, since it should reside inside the build-plugin (chronos-jmeter-maven-plugin) * * @author ksr@lakeside.dk + * @author Dragisa Krsmanovic */ public abstract class AbstractCheckMojo extends AbstractMojo @@ -81,6 +84,10 @@ } catch ( IOException e ) { + throw new MojoExecutionException( "Failure", e ); + } catch (SAXException e) { + throw new MojoExecutionException( "Failure", e ); + } catch (ParserConfigurationException e) { throw new MojoExecutionException( "Failure", e ); } } Index: chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterSAXFileHandler.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterSAXFileHandler.java (revision 15979) +++ chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterSAXFileHandler.java (revision ) @@ -29,15 +29,14 @@ */ package org.codehaus.mojo.chronos.jmeter; -import org.cdmckay.coffeedom.Element; +import org.codehaus.mojo.chronos.common.model.ResponsetimeSample; +import org.codehaus.mojo.chronos.common.model.ResponsetimeSampleGroup; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; import java.util.Properties; import java.util.SortedMap; import java.util.TreeMap; @@ -46,6 +45,7 @@ * SAXHandler for JMeter xml logs. * * @author ksr@lakeside.dk + * @author Dragisa Krsmanovic */ public final class JMeterSAXFileHandler extends DefaultHandler @@ -157,7 +157,7 @@ sampleAttributes = parentSampleAttributes; } - collectJtl21( sampleAttributes ); + collectJtl21(sampleAttributes); reset(); } } @@ -202,77 +202,54 @@ sampleAttributes = null; } + + private static class Collector { - private final SortedMap sampleGroupsByName = new TreeMap(); + private final SortedMap sampleGroupsByName = new TreeMap(); - private final Map> samplesByName = new HashMap>(); - - private static int lastIndex = 0; - /** * int representing the number of succeeded samples. */ protected int succeeded; - private void collect( String sampleName, int responsetime, long timestamp, boolean success, String threadId ) + private void collect(String sampleName, int responsetime, long timestamp, boolean success, String threadId) { if ( success ) { succeeded++; } - Element groupElement = sampleGroupsByName.get( sampleName ); + ResponsetimeSampleGroup groupElement = sampleGroupsByName.get(sampleName); if ( groupElement == null ) { - int currentIndex = ++lastIndex; - groupElement = new Element( "responsetimesamplegroup" ); - groupElement.setAttribute( "name", sampleName ); - groupElement.setAttribute( "index", Integer.toString( currentIndex ) ); + groupElement = new ResponsetimeSampleGroup(sampleName); - sampleGroupsByName.put( sampleName, groupElement ); + sampleGroupsByName.put(sampleName, groupElement); } - Element sampleElement = new Element( "sample" ); - sampleElement.setAttribute( "responsetime", Integer.toString( responsetime ) ); - sampleElement.setAttribute( "timestamp", Long.toString( timestamp ) ); - sampleElement.setAttribute( "success", Boolean.toString( success ) ); - sampleElement.setAttribute( "threadId", threadId ); - - List sampleList = samplesByName.get( sampleName ); - if ( sampleList == null ) - { - sampleList = new ArrayList(); - samplesByName.put( sampleName, sampleList ); + groupElement.add(new ResponsetimeSample(responsetime, timestamp, success, threadId)); - } + } - sampleList.add( sampleElement ); - } /** - * @return the generated samples obtained by parsing the logfile + * Writes XML document using StAX */ - Element getChronosXml() + void writeTo(XMLStreamWriter writer) throws XMLStreamException { + + writer.writeStartElement("groupedresponsetimesamples"); + writer.writeAttribute("succeeded", Integer.toString(succeeded)); + for ( ResponsetimeSampleGroup sampleGroup : sampleGroupsByName.values() ) - { + { - Element xml = new Element( "groupedresponsetimesamples" ); - xml.setAttribute( "succeeded", Integer.toString( succeeded ) ); - for ( String sampleName : sampleGroupsByName.keySet() ) - { - final Element sampleGroupXml = sampleGroupsByName.get( sampleName ); - for ( Element sample : samplesByName.get( sampleName ) ) - { - sampleGroupXml.addContent( sample ); + sampleGroup.writeTo(writer); - } + } - xml.addContent( sampleGroupXml ); + writer.writeEndElement(); - } + } - return xml; - } + } - } /** - * @return the generated samples obtained by parsing the logfile + * Writes XML document using StAX. */ - Element getChronosXml() - { - return collector.getChronosXml(); + void writeTo(XMLStreamWriter writer) throws XMLStreamException { + collector.writeTo(writer); } } Index: chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/GCLogParser.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/GCLogParser.java (revision 15979) +++ chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/GCLogParser.java (revision ) @@ -29,14 +29,21 @@ */ package org.codehaus.mojo.chronos.jmeter; -import org.cdmckay.coffeedom.Element; +import org.apache.maven.plugin.logging.Log; +import org.codehaus.mojo.chronos.common.IOUtil; +import org.codehaus.mojo.chronos.common.model.GCSamples; +import org.codehaus.mojo.chronos.common.model.GCSample; +import org.xml.sax.SAXException; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; -import java.util.ArrayList; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -44,28 +51,36 @@ * This class is responsible for parsing garbage collection logs. * * @author ksr@lakeside.dk + * @author Dragisa Krsmanovic */ public final class GCLogParser { - + + private final Log log; + private static final String CHRONOS_GC_DTD = "chronos-gc.dtd"; + private final XMLOutputFactory xmlOutputFactory; + + public GCLogParser(Log log) { + this.log = log; + xmlOutputFactory = XMLOutputFactory.newInstance(); + } + /** * Parses the garbage collection log. * * @param gcLogFile The garbage collection logfile - * @return {@link org.jdom.Element} An xml representation of tha gc data * @throws IOException if the logfile could not be parsed */ - public Element convertToChronosFormat( File gcLogFile ) + private GCSamples readGCSampleFile( File gcLogFile ) throws IOException { FileReader fileReader = new FileReader( gcLogFile ); LineNumberReader reader = new LineNumberReader( fileReader ); - final Element xml = new Element( "gcsamples" ); - List sampleXmlList = new ArrayList(); + GCSamples samples = new GCSamples(); String line; - StringBuffer concatLines = new StringBuffer(); + StringBuilder concatLines = new StringBuilder(); try { while ( ( line = reader.readLine() ) != null ) @@ -73,9 +88,9 @@ concatLines.append( line ); if ( line.indexOf( "]" ) > -1 ) { // end of the logentry - Element sampleXml = parseGCLogItem( concatLines.toString() ); - sampleXmlList.add( sampleXml ); - concatLines.setLength( 0 ); + GCSample sample = parseGCLogItem( concatLines.toString() ); + samples.add( sample ); + concatLines = new StringBuilder(); } } } @@ -83,21 +98,16 @@ { reader.close(); } - for ( Element sampleXml : sampleXmlList ) - { - xml.addContent( sampleXml ); + + return samples; - } + } - return xml; - } /** - * Runs a regular expression on source.
- * Puts the info in a {@link org.jdom.Element} and adds that to samples The log entries might be JDK1.4 or JDK5 + * Runs a regular expression on source. * * @param source - * @return {@link org.jdom.Element} */ - private Element parseGCLogItem( String source ) + private GCSample parseGCLogItem( String source ) { String timeinstant = null, heapbeforeStr = null, heapafterStr = null, totalheap = null, processingtimeStr = null; @@ -138,13 +148,36 @@ } } - Element xml = new Element( "gcsample" ); - xml.setAttribute( "timestamp", timeinstant ); - xml.setAttribute( "heapBefore", heapbeforeStr ); - xml.setAttribute( "heapAfter", heapafterStr ); - xml.setAttribute( "heapTotal", totalheap ); - xml.setAttribute( "processingTime", processingtimeStr ); - return xml; + return new GCSample( + Double.parseDouble(timeinstant), + Integer.parseInt(heapbeforeStr), + Integer.parseInt(heapafterStr), + Integer.parseInt(totalheap), + Double.parseDouble(processingtimeStr)); + } + + public void convertToGCXml( File file, File output ) + throws SAXException, IOException, XMLStreamException { + + log.debug("Parsing " + file.getName()); + GCSamples samples = readGCSampleFile(file); //"gcsamples", "SYSTEM", "chronos-gc.dtd" + log.debug("Writing GC document to " + output.getName()); + + File directory = IOUtil.ensureDir(output.getParentFile()); + + IOUtil.copyDTDToDir(CHRONOS_GC_DTD, directory); + + XMLStreamWriter xmlStreamWriter = null; + try { + xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(new BufferedOutputStream(new FileOutputStream(output)), "UTF-8"); + xmlStreamWriter.writeStartDocument("UTF-8", "1.0"); + xmlStreamWriter.writeDTD(""); + samples.writeTo(xmlStreamWriter); + } finally { + if (xmlStreamWriter != null) { + xmlStreamWriter.close(); + } + } } } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/HistoricDataDirectory.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/HistoricDataDirectory.java (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/HistoricDataDirectory.java (revision ) @@ -29,26 +29,31 @@ */ package org.codehaus.mojo.chronos.common; -import org.cdmckay.coffeedom.DocType; import org.codehaus.mojo.chronos.common.model.HistoricSample; import org.codehaus.mojo.chronos.common.model.HistoricSamples; +import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; -public class HistoricDataDirectory -{ +public class HistoricDataDirectory { /** * Prefix for history sample files. */ /* pp */ private static final String HISTORYSAMPLE_FILE_PREFIX = "history-"; + public static final String CHRONOS_HISTORY_DTD = "chronos-history.dtd"; private File dataDirectory; - public HistoricDataDirectory( File chronosDir, String dataId ) - { + public HistoricDataDirectory(File chronosDir, String dataId) { - this.dataDirectory = new File( chronosDir, dataId ); + this.dataDirectory = new File(chronosDir, dataId); } /** @@ -60,22 +65,17 @@ * @throws java.io.IOException Thrown if loading the contents fails. */ public HistoricSamples readHistoricSamples() - throws IOException - { + throws IOException, SAXException, ParserConfigurationException { HistoricSamples samples = new HistoricSamples(); File[] historyFiles = dataDirectory.listFiles(); - if ( historyFiles != null ) - { + if (historyFiles != null) { - Arrays.sort( historyFiles ); + Arrays.sort(historyFiles); - for ( int i = 0; i < historyFiles.length; i++ ) - { - if ( historyFiles[i].isFile() ) - { - if ( historyFiles[i].getName().startsWith( HISTORYSAMPLE_FILE_PREFIX ) ) - { - HistoricSample sample = HistoricSample.fromXML( historyFiles[i] ); + for (File historyFile : historyFiles) { + if (historyFile.isFile()) { + if (historyFile.getName().startsWith(HISTORYSAMPLE_FILE_PREFIX)) { + HistoricSample sample = new HistoricSampleXMLFileHandler(historyFile).getHistoricSample(); - samples.addHistoricSample( sample ); + samples.addHistoricSample(sample); } } } @@ -84,19 +84,31 @@ return samples; } - public void writeHistorySample( HistoricSample sample ) + public void writeHistorySample(HistoricSample sample) - throws IOException - { + throws IOException, XMLStreamException { String fileName = - HISTORYSAMPLE_FILE_PREFIX + sample.getTimestamp() + '.' + TestDataDirectory.XML_FILE_EXTENSION; + HISTORYSAMPLE_FILE_PREFIX + sample.getTimestamp() + '.' + TestDataDirectory.XML_FILE_EXTENSION; - File historyFile = new File( dataDirectory, fileName ); + File historyFile = new File(dataDirectory, fileName); - if ( historyFile.exists() ) - { + if (historyFile.exists()) { historyFile.delete(); } - IOUtil.writeXmlToFile( historyFile, sample.toXML(), - new DocType( "historysamples", "SYSTEM", "chronos-history.dtd" ) ); + File directory = IOUtil.ensureDir(historyFile.getParentFile()); + IOUtil.copyDTDToDir(CHRONOS_HISTORY_DTD, directory); + + XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); + + XMLStreamWriter xmlStreamWriter = null; + try { + xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(new BufferedOutputStream(new FileOutputStream(historyFile)), "UTF-8"); + xmlStreamWriter.writeStartDocument("UTF-8", "1.0"); + xmlStreamWriter.writeDTD(""); + sample.writeTo(xmlStreamWriter); + } finally { + if (xmlStreamWriter != null) { + xmlStreamWriter.close(); + } + } } } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/ResponsetimeSamples.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/ResponsetimeSamples.java (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/ResponsetimeSamples.java (revision ) @@ -73,7 +73,7 @@ * * @param sample JMeterSample to add. */ - protected final void add( ResponsetimeSample sample ) + public void add( ResponsetimeSample sample ) { responsetimeStats.addValue( sample.getResponsetime() ); testStart.increment( sample.getStartTime() ); @@ -211,4 +211,5 @@ { return (long) testEnd.getResult(); } + } \ No newline at end of file Index: chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterOutputMojo.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterOutputMojo.java (revision 15979) +++ chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterOutputMojo.java (revision ) @@ -31,12 +31,14 @@ import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; -import org.cdmckay.coffeedom.Element; import org.codehaus.mojo.chronos.common.IOUtil; import org.codehaus.mojo.chronos.common.ProjectBaseDir; import org.codehaus.mojo.chronos.common.TestDataDirectory; +import org.xml.sax.SAXException; +import javax.xml.stream.XMLStreamException; import java.io.File; import java.io.IOException; @@ -97,13 +99,13 @@ File[] inputFiles = IOUtil.listFilesWithExtension( jmeterOutput, "jtl" ); for ( File outputJtlFile : inputFiles ) { - parseJMeterOutput( testDataDirectory, outputJtlFile ); + convertJMeterOutput(testDataDirectory, outputJtlFile, getLog()); } } else if ( isJtlFile( jmeterOutput ) ) { // exists due to previous check - parseJMeterOutput( testDataDirectory, jmeterOutput ); + convertJMeterOutput(testDataDirectory, jmeterOutput, getLog()); } else { // input is a jmx file @@ -111,23 +113,28 @@ "Executing jmeter tests is not supported. We suggest using the jmeter-maven-plugin " ); } - parseGCLog( testDataDirectory, getGcLogFile() ); + + convertGCLog(testDataDirectory, getGcLogFile(), getLog()); } - public static void parseGCLog( TestDataDirectory testDataDirectory, File gcLogFile ) + public static void convertGCLog(TestDataDirectory testDataDirectory, File gcLogFile, Log log) throws MojoExecutionException { if ( gcLogFile != null && gcLogFile.exists() ) { try { - Element gcSamplesXml = new GCLogParser().convertToChronosFormat( gcLogFile ); - testDataDirectory.writeGCLog( gcSamplesXml ); + log.debug("Parsing GC log file " + gcLogFile); + new GCLogParser(log).convertToGCXml(gcLogFile, testDataDirectory.getGCLogFile()); } catch ( IOException e ) { throw new MojoExecutionException( "Unable to parseJtl20 garbage collection log", e ); + } catch (SAXException e) { + throw new MojoExecutionException( "Unable to parseJtl20 garbage collection log", e ); + } catch (XMLStreamException e) { + throw new MojoExecutionException( "Unable to parseJtl20 garbage collection log", e ); } } } @@ -139,18 +146,17 @@ * @param jtlFile File pointing to the .jtl file to parseJtl20. * @throws MojoExecutionException Thrown if the IO operation fails. */ - public static void parseJMeterOutput( TestDataDirectory testDataDirectory, File jtlFile ) + public static void convertJMeterOutput(TestDataDirectory testDataDirectory, File jtlFile, Log log) throws MojoExecutionException { try { - Element samplesElement = new JMeterLogParser().convertToChronosXml( jtlFile ); String jtlName = IOUtil.removeExtension( jtlFile.getName() ); - testDataDirectory.writeResponsetimeSamples( jtlName, samplesElement ); + new JMeterLogParser(log).convertToChronosXml( jtlFile , testDataDirectory.getResponsetimeSamplesFile( jtlName)); - } + } catch ( Exception e ) { - throw new MojoExecutionException( "Could not parseJtl20 jmeter log", e ); + throw new MojoExecutionException( "Could not parse jmeter log", e ); } } Index: chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/chart/GraphGenerator.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/chart/GraphGenerator.java (revision 15979) +++ chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/chart/GraphGenerator.java (revision ) @@ -29,6 +29,7 @@ */ package org.codehaus.mojo.chronos.report.chart; +import org.apache.maven.plugin.logging.Log; import org.codehaus.mojo.chronos.report.ReportConfig; import org.jfree.chart.JFreeChart; @@ -46,12 +47,17 @@ */ public final class GraphGenerator { + private final Log log; + + private List summaryChartSources = new ArrayList(); private Map> detailsChartSources = new LinkedHashMap>(); - public GraphGenerator( List plugins ) + public GraphGenerator( List plugins, Log log) { + this.log = log; + for ( ChronosReportPlugin plugin : plugins ) { ChartSource summarySource = plugin.getSummaryChartSource(); @@ -90,6 +96,7 @@ { JFreeChart chart = chartSource.getChart( bundle, config ); String fileName = chartSource.getFileName( config ); + log.debug("Rendering summary chart " + fileName); renderer.renderChart( fileName, chart ); } } @@ -101,6 +108,7 @@ { JFreeChart chart = source.getChart( bundle, config ); String fileName = source.getFileName( config ); + log.debug("Rendering detailed chart " + fileName); renderer.renderChart( fileName, chart ); } } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/chronos-responsetimesamples.dtd =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/chronos-responsetimesamples.dtd (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/chronos-responsetimesamples.dtd (revision 15979) @@ -1,20 +0,0 @@ - - - - - - - - Index: chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/history/HistoryReportMojo.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/history/HistoryReportMojo.java (revision 15979) +++ chronos-report-maven-plugin/src/main/java/org/codehaus/mojo/chronos/report/history/HistoryReportMojo.java (revision ) @@ -40,7 +40,9 @@ import org.codehaus.mojo.chronos.report.chart.ChartRenderer; import org.codehaus.mojo.chronos.report.chart.ChartRendererImpl; import org.codehaus.mojo.chronos.report.chart.HistoryChartGenerator; +import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; import java.util.Locale; @@ -158,6 +160,10 @@ } catch ( IOException e ) { + throw new MavenReportException( "ReportGenerator failed", e ); + } catch (SAXException e) { + throw new MavenReportException( "ReportGenerator failed", e ); + } catch (ParserConfigurationException e) { throw new MavenReportException( "ReportGenerator failed", e ); } } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/GCSamples.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/GCSamples.java (revision 15979) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/model/GCSamples.java (revision ) @@ -29,134 +29,100 @@ */ package org.codehaus.mojo.chronos.common.model; -import org.cdmckay.coffeedom.Document; -import org.cdmckay.coffeedom.Element; -import org.cdmckay.coffeedom.input.SAXBuilder; import org.jfree.data.time.Millisecond; import org.jfree.data.time.TimeSeries; -import java.io.File; -import java.io.IOException; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; import java.io.Serializable; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; /** * Container for {@link org.codehaus.mojo.chronos.common.model.GCSample}. * * @author ksr@lakeside.dk + * @author Dragisa Krsmanovic */ public class GCSamples - implements Serializable -{ + implements Serializable { - private final List samples = new ArrayList(); + private final List samples = new ArrayList(); /** * adds a {@link org.codehaus.mojo.chronos.common.model.GCSample} to the list. * * @param sample a {@link org.codehaus.mojo.chronos.common.model.GCSample} */ - public final void add( GCSample sample ) - { + public final void add(GCSample sample) { - samples.add( sample ); + samples.add(sample); } /** * @return the number of samples */ - public final int getSampleCount() - { + public final int getSampleCount() { return samples.size(); } - public final double getTimeStampForSampleAt( int index ) - { + public final double getTimeStampForSampleAt(int index) { - return ( (GCSample) samples.get( index ) ).getTimestamp(); + return ((GCSample) samples.get(index)).getTimestamp(); } - public final void extractHeapBefore( TimeSeries heapBeforeSeries ) - { - for ( Iterator it = samples.iterator(); it.hasNext(); ) - { - GCSample sample = (GCSample) it.next(); + public final void extractHeapBefore(TimeSeries heapBeforeSeries) { + for (GCSample sample : samples) { - heapBeforeSeries.addOrUpdate( getTimestamp( sample ), sample.getHeapBefore() ); + heapBeforeSeries.addOrUpdate(getTimestamp(sample), sample.getHeapBefore()); } } - public final void extractHeapAfter( TimeSeries heapAfterSeries ) - { - for ( Iterator it = samples.iterator(); it.hasNext(); ) - { - GCSample sample = (GCSample) it.next(); + public final void extractHeapAfter(TimeSeries heapAfterSeries) { + for (GCSample sample : samples) { - heapAfterSeries.addOrUpdate( getTimestamp( sample ), sample.getHeapAfter() ); + heapAfterSeries.addOrUpdate(getTimestamp(sample), sample.getHeapAfter()); } } - public final void extractHeapTotal( TimeSeries heapTotalSeries ) - { - for ( Iterator it = samples.iterator(); it.hasNext(); ) - { - GCSample sample = (GCSample) it.next(); + public final void extractHeapTotal(TimeSeries heapTotalSeries) { + for (GCSample sample : samples) { - heapTotalSeries.addOrUpdate( getTimestamp( sample ), sample.getHeapTotal() ); + heapTotalSeries.addOrUpdate(getTimestamp(sample), sample.getHeapTotal()); } } - public final void extractProcessingTime( TimeSeries series ) - { - for ( Iterator it = samples.iterator(); it.hasNext(); ) - { - GCSample sample = (GCSample) it.next(); + public final void extractProcessingTime(TimeSeries series) { + for (GCSample sample : samples) { - series.addOrUpdate( getTimestamp( sample ), sample.getProcessingTime() ); + series.addOrUpdate(getTimestamp(sample), sample.getProcessingTime()); } } - public final double getGarbageCollectionRatio( long totalTime ) - { + public final double getGarbageCollectionRatio(long totalTime) { double totalProcessing = 0.0d; - for ( Iterator it = samples.iterator(); it.hasNext(); ) - { - GCSample sample = (GCSample) it.next(); + for (GCSample sample : samples) { totalProcessing += sample.getProcessingTime(); } return totalProcessing / totalTime; } - public final double getCollectedKBPerSecond( long totalTime ) - { + public final double getCollectedKBPerSecond(long totalTime) { double totalCollected = 0.0d; - for ( Iterator it = samples.iterator(); it.hasNext(); ) - { - GCSample sample = (GCSample) it.next(); + for (GCSample sample : samples) { - totalCollected += ( sample.getHeapBefore() - sample.getHeapAfter() ); + totalCollected += (sample.getHeapBefore() - sample.getHeapAfter()); } - return ( totalCollected / 1000 ) / totalTime; + return (totalCollected / 1000) / totalTime; } - private Millisecond getTimestamp( GCSample sample ) - { + private Millisecond getTimestamp(GCSample sample) { - int milliseconds = (int) ( sample.getTimestamp() * 1000 ); + int milliseconds = (int) (sample.getTimestamp() * 1000); - return ModelUtil.createMillis( milliseconds ); + return ModelUtil.createMillis(milliseconds); } - public void addAll( GCSamples tmp ) - { + public void addAll(GCSamples tmp) { - this.samples.addAll( tmp.samples ); + this.samples.addAll(tmp.samples); } - public static GCSamples fromXML( File file ) - throws IOException - { - SAXBuilder builder = new SAXBuilder(); - Document document = builder.build( file ); - - GCSamples gcsamples = new GCSamples(); - List gcSampleXMLs = document.getRootElement().getChildren(); - for ( Element gcSample : gcSampleXMLs ) - { - gcsamples.add( GCSample.fromXML( gcSample ) ); + public void writeTo(XMLStreamWriter writer) throws XMLStreamException { + writer.writeStartElement("gcsamples"); + for (GCSample sample : samples) { + sample.writeTo(writer); } - return gcsamples; + writer.writeEndElement(); } } \ No newline at end of file Index: chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterLogParser.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterLogParser.java (revision 15979) +++ chronos-jmeter-maven-plugin/src/main/java/org/codehaus/mojo/chronos/jmeter/JMeterLogParser.java (revision ) @@ -29,26 +29,38 @@ */ package org.codehaus.mojo.chronos.jmeter; -import org.cdmckay.coffeedom.Element; +import org.apache.maven.plugin.logging.Log; +import org.codehaus.mojo.chronos.common.IOUtil; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; /** * Responsible for parsing the jmeter log. * * @author ksr@lakeside.dk + * @author Dragisa Krsmanovic */ public final class JMeterLogParser { private final SAXParser saxParser; + private final XMLOutputFactory xmlOutputFactory; - public JMeterLogParser() + private final Log log; + public static final String CHRONOS_RESPONSETIMESAMPLES_DTD = "chronos-responsetimesamples.dtd"; + + public JMeterLogParser(Log log) { + this.log = log; SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); try { @@ -62,22 +74,42 @@ { throw new RuntimeException( e ); } + + xmlOutputFactory = XMLOutputFactory.newInstance(); } /** - * Parse the jmeter (jtl) log. + * Convert the jmeter (jtl) log to Chronos XML. * * @param file The file to parse - * @return An xml representation oin chronos format of the log + * @param output The Chronos XML file to write to * @throws SAXException If there is some XML related error in the logfile * @throws IOException If the JMeter logfile cannot be read + * @throws javax.xml.stream.XMLStreamException If it cannot write to Chronos XML file */ - public Element convertToChronosXml( File file ) - throws SAXException, IOException - { + public void convertToChronosXml( File file, File output ) + throws SAXException, IOException, XMLStreamException { + JMeterSAXFileHandler saxHandler = new JMeterSAXFileHandler(); + log.debug("Parsing " + file.getName()); - saxParser.parse( file, saxHandler ); + saxParser.parse(file, saxHandler); - return saxHandler.getChronosXml(); + log.debug("Writing Chronos document to " + output.getName()); + + File directory = IOUtil.ensureDir(output.getParentFile()); + + IOUtil.copyDTDToDir(CHRONOS_RESPONSETIMESAMPLES_DTD, directory); + + XMLStreamWriter xmlStreamWriter = null; + try { + xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(new BufferedOutputStream(new FileOutputStream(output)), "UTF-8"); + xmlStreamWriter.writeStartDocument("UTF-8", "1.0"); + xmlStreamWriter.writeDTD(""); + saxHandler.writeTo(xmlStreamWriter); + } finally { + if (xmlStreamWriter != null) { + xmlStreamWriter.close(); + } + } } } Index: chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/ResponsetimeXMLFileHandler.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/ResponsetimeXMLFileHandler.java (revision ) +++ chronos-plugincommon/src/main/java/org/codehaus/mojo/chronos/common/ResponsetimeXMLFileHandler.java (revision ) @@ -0,0 +1,135 @@ +/* + * The MIT License + * + * Original work sponsored and donated by National Board of e-Health (NSI), Denmark (http://www.nsi.dk) + * Further enhancement before move to Codehaus sponsored and donated by Lakeside A/S (http://www.lakeside.dk) + * + * Copyright (c) to all contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $HeadURL:$ + * $Id:$ + */ +package org.codehaus.mojo.chronos.common; + + +import org.codehaus.mojo.chronos.common.model.GroupedResponsetimeSamples; +import org.codehaus.mojo.chronos.common.model.ResponsetimeSample; +import org.codehaus.mojo.chronos.common.model.ResponsetimeSampleGroup; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import java.io.File; +import java.io.IOException; + +/** + * SAX parser for the response time XML. + * + * Not thread safe. + * + * @author Dragisa Krsmanovic + */ +public class ResponsetimeXMLFileHandler extends DefaultHandler { + + private boolean inSampleGroup = false; + + private boolean insideGroups = false; + private boolean insideSample = false; + + private GroupedResponsetimeSamples groupedResponsetimeSamples = null; + private ResponsetimeSampleGroup currentGroup = null; + + + public ResponsetimeXMLFileHandler(File file) throws IOException, SAXException, ParserConfigurationException { + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); + SAXParser saxParser = saxParserFactory.newSAXParser(); + saxParser.parse(file, this); + } + + /** + * @param uri See {@link DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @param localName See {@link DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @param qName See {@link DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @param attributes See {@link DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @throws org.xml.sax.SAXException See {@link DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes)} + * @see DefaultHandler#startElement(String, String, String, org.xml.sax.Attributes) + */ + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + if ("groupedresponsetimesamples".equals(qName)) { + groupedResponsetimeSamples = new GroupedResponsetimeSamples( + Integer.parseInt(attributes.getValue("succeeded"))); + insideGroups = true; + } else if ("responsetimesamplegroup".equals(qName)) { + if (!insideGroups) { + throw new SAXException("responsetimesamplegroup should be inside groupedresponsetimesamples"); + } + currentGroup = new ResponsetimeSampleGroup( + attributes.getValue("name"), + Integer.parseInt(attributes.getValue("index"))); + inSampleGroup = true; + } else if ("sample".equals(qName)) { + if (!inSampleGroup) { + throw new SAXException("sample should be inside responsetimesamplegroup"); + } + currentGroup.add(new ResponsetimeSample( + Integer.parseInt(attributes.getValue("responsetime")), + Long.parseLong(attributes.getValue("timestamp")), + "true".equalsIgnoreCase(attributes.getValue("success")), + attributes.getValue("threadId"))); + insideSample = true; + } + } + + /** + * @param uri See {@link DefaultHandler#endElement(String, String, String)} + * @param localName See {@link DefaultHandler#endElement(String, String, String)} + * @param qName See {@link DefaultHandler#endElement(String, String, String)} + * @throws SAXException See {@link DefaultHandler#endElement(String, String, String)} + * @see DefaultHandler#endElement(String, String, String) + */ + public void endElement(String uri, String localName, String qName) + throws SAXException { + if ("groupedresponsetimesamples".equals(qName)) { + if (insideSample || inSampleGroup) { + throw new SAXException("responsetimesamplegroup not properly ended. Encountered end of groupedresponsetimesamples."); + } + insideGroups = false; + } else if ("responsetimesamplegroup".equals(qName)) { + if (insideSample) { + throw new SAXException("sample not properly ended. Encountered end of responsetimesamplegroup."); + } + groupedResponsetimeSamples.add(currentGroup); + currentGroup = null; + inSampleGroup = false; + } else if ("sample".equals(qName)) { + insideSample = false; + } + } + + + public GroupedResponsetimeSamples getGroupedResponsetimeSamples() { + return groupedResponsetimeSamples; + } +}