/*
* JMeter Report Server
* Copyright (C) 2010 eXcentia
* dev@sonar.codehaus.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package es.excentia.jmeter.report.server.testresults;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlValidationError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import es.excentia.jmeter.report.client.serialization.StreamReader;
import es.excentia.jmeter.report.server.testresults.xmlbeans.AbstractSample;
import es.excentia.jmeter.report.server.testresults.xmlbeans.TestResultsDocument;
/**
* Reader para obtener objetos a partir de un fichero jtl
*
* El procedimiento de lectura con stax y xmlbeans para poder leer ficheros
* grandes se ha tomado de http://www.devx.com/xml/Article/34037/1954
*
* @author cfillol
*
*/
public class JtlAbstractSampleReader extends StreamReader<AbstractSample> {
public static final String NAMESPACE = "http://xmlbeans.testresults.server.report.jmeter.excentia.es";
private static final int NODE_READ_BUFFER_MAX_BYTES = 1024 * 1024;
private static final String HTTPSAMPLE_TAG_NAME = "httpSample";
private static final String SAMPLE_TAG_NAME = "sample";
private static final Logger log = LoggerFactory
.getLogger(JtlAbstractSampleReader.class);
private static final boolean LOG_DEBUG = log.isDebugEnabled();
private static final boolean LOG_TRACE = log.isTraceEnabled();
protected XMLEventReader reader;
protected XMLEventWriter writer;
protected XmlOptions options;
protected XmlOptions validationOptions;
protected List<XmlValidationError> validationErrors = new ArrayList<XmlValidationError>();
protected ResettableStringWriter swriter;
protected int readCount = 0;
public JtlAbstractSampleReader(InputStream is) {
super(is);
if (LOG_DEBUG) {
log.debug("Creating JtlAbstractSampleReader ...");
}
try {
// Create the input reader to read the file. We will
// use the defaults here, but it could be configured
// using the properties.
XMLInputFactory inFactory = XMLInputFactory.newInstance();
reader = inFactory.createXMLEventReader(is);
// Create an output writer. The writer uses the resettable
// buffer so we can use the same writer continuously.
XMLOutputFactory outFactory = XMLOutputFactory.newInstance();
outFactory.setProperty("javax.xml.stream.isRepairingNamespaces", true);
swriter = new ResettableStringWriter(NODE_READ_BUFFER_MAX_BYTES);
writer = outFactory.createXMLEventWriter(swriter);
options = new XmlOptions();
Map<String, String> ns = new HashMap<String, String>();
ns.put("", NAMESPACE);
options.setLoadSubstituteNamespaces(ns);
options.setDocumentType(TestResultsDocument.TestResults.type);
validationOptions = new XmlOptions();
validationOptions.setErrorListener(validationErrors);
} catch (XMLStreamException e) {
if (LOG_DEBUG) {
log.debug("Cannot create JtlReader", e);
}
throw new JtlReaderException("Cannot create JtlReader", e);
}
}
@Override
public AbstractSample read() {
try {
// Begin reading events
int sampleDepth = 0;
while (reader.hasNext()) {
XMLEvent evt = reader.nextEvent();
if (evt.isStartElement()) {
StartElement elem = (StartElement) evt;
String tagName = elem.getName().getLocalPart();
if (HTTPSAMPLE_TAG_NAME.equals(tagName)
|| SAMPLE_TAG_NAME.equals(tagName)) {
// Empieza un HttpSample ...
sampleDepth++;
}
}
if (sampleDepth > 0) {
// Nos quedamos con el xml que cuelga del primer HttpSample
// y de todos sus descendientes
writer.add(evt);
} else {
// Descartamos el nodo raíz, pero lo incluimos en el writer
// para que el xml siga siendo válido
writer.add(evt);
writer.flush();
swriter.reset();
}
if (evt.isEndElement()) {
EndElement elem = (EndElement) evt;
String tagName = elem.getName().getLocalPart();
if (HTTPSAMPLE_TAG_NAME.equals(tagName)
|| SAMPLE_TAG_NAME.equals(tagName)) {
// Acaba un HttpSample ...
readCount++; // Actualizamos el contador de HttSamples leídos
if (sampleDepth == 1) {
// Si hemos llegado al final del HttpSample padre,
// terminamos de guardar el xml que nos interesa y
// lo devolvemos en forma de objeto
// Flush the write and grabe the XML from
// the memory, string buffer. At the same time
// we reset the position in the buffer.
writer.flush();
String sampleXml = swriter.reset();
if (LOG_TRACE) {
log.trace(sampleXml);
}
// Convertimos el xml en objeto de XmlBeans.
// Con XmlBeans, tenemos que usar el nodo padre para cargar
// el fragmento interno de xml.
TestResultsDocument.TestResults testResults = (TestResultsDocument.TestResults) XmlObject.Factory
.parse(sampleXml, options);
if (!testResults.validate(validationOptions)) {
throw new JtlReaderException(validationErrors.get(0).toString());
}
// Devolvemos el HttpSample leído
if (HTTPSAMPLE_TAG_NAME.equals(tagName)) {
return testResults.getHttpSampleArray(0);
} else {
return testResults.getSampleArray(0);
}
}
sampleDepth--;
}
}
}
if (LOG_DEBUG) {
log.debug(String.format("JtlAbstractSampleReader read %d nodes",
readCount));
}
return null;
} catch (XMLStreamException e) {
throw new JtlReaderException(e);
} catch (XmlException e) {
throw new JtlReaderException(e);
}
}
}