in src/core/src/main/java/org/apache/jmeter/report/dashboard/HtmlTemplateExporter.java [287:461]
public void export(SampleContext context, File file,
ReportGeneratorConfiguration configuration) throws ExportException {
Objects.requireNonNull(context, "context must not be null");
Objects.requireNonNull(file, "file must not be null");
Objects.requireNonNull(configuration, "configuration must not be null");
log.debug("Start template processing");
// Create data context and populate it
DataContext dataContext = new DataContext();
// Get the configuration of the current exporter
final ExporterConfiguration exportCfg =
configuration.getExportConfigurations().get(getName());
// Get template directory property value
File templateDirectory = getPropertyFromConfig(
exportCfg,
TEMPLATE_DIR,
new File(JMeterUtils.getJMeterBinDir(), TEMPLATE_DIR_NAME_DEFAULT),
File.class);
if (!templateDirectory.isDirectory()) {
String message = String.format(
"\"%s\" is not a valid template directory",
templateDirectory.getAbsolutePath());
log.error(message);
throw new ExportException(message);
}
// Get output directory property value
File outputDir = getPropertyFromConfig(exportCfg, OUTPUT_DIR,
new File(JMeterUtils.getJMeterBinDir(), OUTPUT_DIR_NAME_DEFAULT), File.class);
String globallyDefinedOutputDir = JMeterUtils.getProperty(JMeter.JMETER_REPORT_OUTPUT_DIR_PROPERTY);
if(!StringUtils.isEmpty(globallyDefinedOutputDir)) {
outputDir = new File(globallyDefinedOutputDir);
}
JOrphanUtils.canSafelyWriteToFolder(outputDir, HtmlTemplateExporter::htmlReportFileFilter);
if (log.isInfoEnabled()) {
log.info("Will generate dashboard in folder: {}", outputDir.getAbsolutePath());
}
// Add a flag defining if only sample series are filtered to the context
final boolean filtersOnlySampleSeries = exportCfg.filtersOnlySampleSeries();
addToContext(
DATA_CTX_FILTERS_ONLY_SAMPLE_SERIES,
filtersOnlySampleSeries,
dataContext);
// Add the series filter to the context
final String seriesFilter = exportCfg.getSeriesFilter();
Pattern filterPattern = null;
if (StringUtils.isNotBlank(seriesFilter)) {
try {
filterPattern = Pattern.compile(seriesFilter);
} catch (PatternSyntaxException ex) {
log.error("Invalid series filter: '{}', {}", seriesFilter, ex.getDescription());
}
}
addToContext(DATA_CTX_SERIES_FILTER, seriesFilter, dataContext);
// Add the flag defining whether only controller series are displayed
final boolean showControllerSeriesOnly = exportCfg.showControllerSeriesOnly();
addToContext(
DATA_CTX_SHOW_CONTROLLERS_ONLY,
showControllerSeriesOnly,
dataContext);
JsonizerVisitor jsonizer = new JsonizerVisitor();
Map<String, Object> storedData = context.getData();
// Add begin date consumer result to the data context
addResultToContext(
ReportGenerator.BEGIN_DATE_CONSUMER_NAME, storedData, dataContext, jsonizer);
// Add end date summary consumer result to the data context
addResultToContext(
ReportGenerator.END_DATE_CONSUMER_NAME, storedData, dataContext, jsonizer);
// Add Apdex summary consumer result to the data context
addResultToContext(
ReportGenerator.APDEX_SUMMARY_CONSUMER_NAME, storedData, dataContext, jsonizer);
// Add errors summary consumer result to the data context
addResultToContext(
ReportGenerator.ERRORS_SUMMARY_CONSUMER_NAME, storedData, dataContext, jsonizer);
// Add requests summary consumer result to the data context
addResultToContext(
ReportGenerator.REQUESTS_SUMMARY_CONSUMER_NAME, storedData, dataContext, jsonizer);
// Add statistics summary consumer result to the data context
addResultToContext(
ReportGenerator.STATISTICS_SUMMARY_CONSUMER_NAME, storedData, dataContext, jsonizer);
// Add Top 5 errors by sampler consumer result to the data context
addResultToContext(
ReportGenerator.TOP5_ERRORS_BY_SAMPLER_CONSUMER_NAME, storedData, dataContext, jsonizer);
// Collect graph results from sample context and transform them into
// Json strings to inject in the data context
ExtraOptionsResultCustomizer customizer = new ExtraOptionsResultCustomizer();
EmptyGraphChecker checker =
new EmptyGraphChecker(filtersOnlySampleSeries, showControllerSeriesOnly, filterPattern);
Map<String, GraphConfiguration> mapConfiguration = new HashMap<>();
DataContext customGraphs = new DataContext();
for (Map.Entry<String, GraphConfiguration> graphEntry : configuration.getGraphConfigurations().entrySet()) {
final String graphId = graphEntry.getKey();
final GraphConfiguration graphConfiguration = graphEntry.getValue();
// Initialize customizer and checker
customizer.setExtraOptions(exportCfg.getGraphExtraConfigurations().get(graphId));
checker.setExcludesControllers(graphConfiguration.excludesControllers());
checker.setGraphId(graphId);
mapConfiguration.put(graphId, graphConfiguration);
if (graphId.startsWith(CUSTOM_GRAPH_PREFIX)) {
addResultToContext(
graphId, storedData, customGraphs, jsonizer, customizer, checker);
} else {
// Export graph data
addResultToContext(
graphId, storedData, dataContext, jsonizer, customizer, checker);
}
}
dataContext.put("graphConfigurations", mapConfiguration);
dataContext.put("customsGraphsData", customGraphs);
// Replace the begin date with its formatted string and store the old timestamp
long oldTimestamp = formatTimestamp(
ReportGenerator.BEGIN_DATE_CONSUMER_NAME, dataContext);
// Replace the end date with its formatted string
formatTimestamp(ReportGenerator.END_DATE_CONSUMER_NAME, dataContext);
// Add time zone offset (that matches the begin date) to the context
TimeZone timezone = TimeZone.getDefault();
addToContext(
DATA_CTX_TIMEZONE_OFFSET,
timezone.getOffset(oldTimestamp),
dataContext);
// Add report title to the context
if (StringUtils.isNotEmpty(configuration.getReportTitle())) {
dataContext.put(DATA_CTX_REPORT_TITLE, StringEscapeUtils.escapeHtml4(configuration.getReportTitle()));
}
// Add the test file name to the context
addToContext(DATA_CTX_TESTFILE, file.getName(), dataContext);
// Add the overall filter property to the context
addToContext(DATA_CTX_OVERALL_FILTER, configuration.getSampleFilter(), dataContext);
// Walk template directory to copy files and process templated ones
Configuration templateCfg = new Configuration(Configuration.VERSION_2_3_30);
try {
templateCfg.setDirectoryForTemplateLoading(templateDirectory);
templateCfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
if (log.isInfoEnabled()) {
log.info("Report will be generated in: {}, creating folder structure", outputDir.getAbsolutePath());
}
FileUtils.forceMkdir(outputDir);
TemplateVisitor visitor = new TemplateVisitor(
templateDirectory.toPath(),
outputDir.toPath(),
templateCfg,
dataContext);
Files.walkFileTree(templateDirectory.toPath(), visitor);
} catch (IOException ex) {
throw new ExportException("Unable to process template files.", ex);
}
log.debug("End of template processing");
}