package hudson.plugins.performance;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.util.ChartUtil;
import hudson.util.ColorPalette;
import hudson.util.DataSetBuilder;
import hudson.util.ShiftedCategoryAxis;
import hudson.util.ChartUtil.NumberOnlyBuildLabel;
import java.awt.BasicStroke;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.category.CategoryDataset;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
public final class PerformanceProjectAction implements Action {
private static final String CONFIGURE_LINK = "configure";
private static final String TRENDREPORT_LINK = "trendReport";
private static final String PLUGIN_NAME = "performance";
private static final long serialVersionUID = 1L;
/** Logger. */
private static final Logger LOGGER = Logger.getLogger(PerformanceProjectAction.class.getName());
public final AbstractProject<?, ?> project;
private transient List<String> performanceReportList;
public String getDisplayName() {
return Messages.ProjectAction_DisplayName();
}
public String getIconFileName() {
return "graph.gif";
}
public String getUrlName() {
return PLUGIN_NAME;
}
public PerformanceProjectAction(AbstractProject project) {
this.project = project;
}
private JFreeChart createErrorsChart(CategoryDataset dataset) {
final JFreeChart chart = ChartFactory.createLineChart(
Messages.ProjectAction_PercentageOfErrors(), // chart title
null, // unused
"%", // range axis label
dataset, // data
PlotOrientation.VERTICAL, // orientation
true, // include legend
true, // tooltips
false // urls
);
// NOW DO SOME OPTIONAL CUSTOMISATION OF THE CHART...
final LegendTitle legend = chart.getLegend();
legend.setPosition(RectangleEdge.RIGHT);
chart.setBackgroundPaint(Color.white);
final CategoryPlot plot = chart.getCategoryPlot();
// plot.setAxisOffset(new Spacer(Spacer.ABSOLUTE, 5.0, 5.0, 5.0, 5.0));
plot.setBackgroundPaint(Color.WHITE);
plot.setOutlinePaint(null);
plot.setRangeGridlinesVisible(true);
plot.setRangeGridlinePaint(Color.black);
CategoryAxis domainAxis = new ShiftedCategoryAxis(null);
plot.setDomainAxis(domainAxis);
domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
domainAxis.setLowerMargin(0.0);
domainAxis.setUpperMargin(0.0);
domainAxis.setCategoryMargin(0.0);
final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
rangeAxis.setUpperBound(100);
rangeAxis.setLowerBound(0);
final LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();
renderer.setBaseStroke(new BasicStroke(4.0f));
ColorPalette.apply(renderer);
// crop extra space around the graph
plot.setInsets(new RectangleInsets(5.0, 0, 0, 5.0));
return chart;
}
private JFreeChart createRespondingTimeChart(CategoryDataset dataset) {
final JFreeChart chart = ChartFactory.createLineChart(
Messages.ProjectAction_RespondingTime(), // charttitle
null, // unused
"ms", // range axis label
dataset, // data
PlotOrientation.VERTICAL, // orientation
true, // include legend
true, // tooltips
false // urls
);
// NOW DO SOME OPTIONAL CUSTOMISATION OF THE CHART...
final LegendTitle legend = chart.getLegend();
legend.setPosition(RectangleEdge.RIGHT);
chart.setBackgroundPaint(Color.white);
final CategoryPlot plot = chart.getCategoryPlot();
// plot.setAxisOffset(new Spacer(Spacer.ABSOLUTE, 5.0, 5.0, 5.0, 5.0));
plot.setBackgroundPaint(Color.WHITE);
plot.setOutlinePaint(null);
plot.setRangeGridlinesVisible(true);
plot.setRangeGridlinePaint(Color.black);
CategoryAxis domainAxis = new ShiftedCategoryAxis(null);
plot.setDomainAxis(domainAxis);
domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
domainAxis.setLowerMargin(0.0);
domainAxis.setUpperMargin(0.0);
domainAxis.setCategoryMargin(0.0);
final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
final LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();
renderer.setBaseStroke(new BasicStroke(4.0f));
ColorPalette.apply(renderer);
// crop extra space around the graph
plot.setInsets(new RectangleInsets(5.0, 0, 0, 5.0));
return chart;
}
public void doErrorsGraph(StaplerRequest request, StaplerResponse response) throws IOException {
PerformanceReportPosition performanceReportPosition = new PerformanceReportPosition();
request.bindParameters(performanceReportPosition);
String performanceReportNameFile = performanceReportPosition.getPerformanceReportPosition();
if (performanceReportNameFile == null) {
if (getPerformanceReportList().size() == 1) {
performanceReportNameFile = getPerformanceReportList().get(0);
} else {
return;
}
}
if (ChartUtil.awtProblemCause != null) {
// not available. send out error message
response.sendRedirect2(request.getContextPath() + "/images/headless.png");
return;
}
DataSetBuilder<String, NumberOnlyBuildLabel> dataSetBuilderErrors = new DataSetBuilder<String, NumberOnlyBuildLabel>();
List<?> builds = getProject().getBuilds();
List<Integer> buildsLimits = getFirstAndLastBuild(request, builds);
int nbBuildsToAnalyze = builds.size();
for (Iterator<?> iterator = builds.iterator(); iterator.hasNext();) {
AbstractBuild<?, ?> currentBuild = (AbstractBuild<?, ?>) iterator.next();
if (nbBuildsToAnalyze <= buildsLimits.get(1) && buildsLimits.get(0) <= nbBuildsToAnalyze) {
NumberOnlyBuildLabel label = new NumberOnlyBuildLabel(currentBuild);
PerformanceBuildAction performanceBuildAction = currentBuild.getAction(PerformanceBuildAction.class);
if (performanceBuildAction == null) {
continue;
}
PerformanceReport performanceReport = performanceBuildAction.getPerformanceReportMap().getPerformanceReport(performanceReportNameFile);
if (performanceReport == null) {
nbBuildsToAnalyze--;
continue;
}
dataSetBuilderErrors.add(performanceReport.errorPercent(), Messages.ProjectAction_Errors(), label);
}
nbBuildsToAnalyze--;
}
ChartUtil.generateGraph(request, response, createErrorsChart(dataSetBuilderErrors.build()), 400, 200);
}
public void doRespondingTimeGraph(StaplerRequest request, StaplerResponse response) throws IOException {
PerformanceReportPosition performanceReportPosition = new PerformanceReportPosition();
request.bindParameters(performanceReportPosition);
String performanceReportNameFile = performanceReportPosition.getPerformanceReportPosition();
if (performanceReportNameFile == null) {
if (getPerformanceReportList().size() == 1) {
performanceReportNameFile = getPerformanceReportList().get(0);
} else {
return;
}
}
if (ChartUtil.awtProblemCause != null) {
// not available. send out error message
response.sendRedirect2(request.getContextPath() + "/images/headless.png");
return;
}
DataSetBuilder<String, NumberOnlyBuildLabel> dataSetBuilderAverage = new DataSetBuilder<String, NumberOnlyBuildLabel>();
List<?> builds = getProject().getBuilds();
List<Integer> buildsLimits = getFirstAndLastBuild(request, builds);
int nbBuildsToAnalyze = builds.size();
for (Iterator<?> iterator = builds.iterator(); iterator.hasNext();) {
AbstractBuild<?, ?> currentBuild = (AbstractBuild<?, ?>) iterator.next();
if (nbBuildsToAnalyze <= buildsLimits.get(1) && buildsLimits.get(0) <= nbBuildsToAnalyze) {
NumberOnlyBuildLabel label = new NumberOnlyBuildLabel(currentBuild);
PerformanceBuildAction performanceBuildAction = currentBuild.getAction(PerformanceBuildAction.class);
if (performanceBuildAction == null) {
continue;
}
PerformanceReport performanceReport = performanceBuildAction.getPerformanceReportMap().getPerformanceReport(performanceReportNameFile);
if (performanceReport == null) {
nbBuildsToAnalyze--;
continue;
}
dataSetBuilderAverage.add(performanceReport.getMax(), Messages.ProjectAction_Maximum(), label);
dataSetBuilderAverage.add(performanceReport.getAverage(), Messages.ProjectAction_Average(), label);
dataSetBuilderAverage.add(performanceReport.getMin(), Messages.ProjectAction_Minimum(), label);
}
nbBuildsToAnalyze--;
}
ChartUtil.generateGraph(request, response, createRespondingTimeChart(dataSetBuilderAverage.build()), 400, 200);
}
/**
* <p>
* give a list of two Integer : the smallest build to use and the biggest.
* </p>
*
* @param request
* @param builds
* @return outList
*/
private List<Integer> getFirstAndLastBuild(StaplerRequest request, List<?> builds) {
List<Integer> outList = new ArrayList<Integer>(2);
GraphConfigurationDetail graphConf = (GraphConfigurationDetail) createUserConfiguration(request);
String configType = graphConf.getConfigType();
if (configType.compareToIgnoreCase(GraphConfigurationDetail.BUILD_CONFIG) == 0) {
if (graphConf.getBuildCount() <= 0) {
configType = GraphConfigurationDetail.NONE_CONFIG;
} else {
if (builds.size() - graphConf.getBuildCount() > 0) {
outList.add(builds.size() - graphConf.getBuildCount() + 1);
} else {
outList.add(1);
}
outList.add(builds.size());
}
} else if (configType.compareToIgnoreCase(GraphConfigurationDetail.DATE_CONFIG) == 0) {
if (GraphConfigurationDetail.DEFAULT_DATE.compareTo(graphConf.getFirstDayCount()) == 0
&& GraphConfigurationDetail.DEFAULT_DATE.compareTo(graphConf.getLastDayCount()) == 0) {
configType = GraphConfigurationDetail.NONE_CONFIG;
} else {
int firstBuild = -1;
int lastBuild = -1;
int var = builds.size();
GregorianCalendar firstDate = null;
GregorianCalendar lastDate = null;
try {
firstDate = GraphConfigurationDetail.getGregorianCalendarFromString(graphConf.getFirstDayCount());
lastDate = GraphConfigurationDetail.getGregorianCalendarFromString(graphConf.getLastDayCount());
lastDate.set(GregorianCalendar.HOUR_OF_DAY, 23);
lastDate.set(GregorianCalendar.MINUTE, 59);
lastDate.set(GregorianCalendar.SECOND, 59);
} catch (ParseException e) {
LOGGER.log(Level.SEVERE,"Error during the manage of the Calendar",e);
}
for (Iterator<?> iterator = builds.iterator(); iterator.hasNext();) {
AbstractBuild<?, ?> currentBuild = (AbstractBuild<?, ?>) iterator.next();
GregorianCalendar buildDate = new GregorianCalendar();
buildDate.setTime(currentBuild.getTimestamp().getTime());
if (firstDate.getTime().before(buildDate.getTime())) {
firstBuild = var;
}
if (lastBuild < 0 && lastDate.getTime().after(buildDate.getTime())) {
lastBuild = var;
}
var--;
}
outList.add(firstBuild);
outList.add(lastBuild);
}
}
if (configType.compareToIgnoreCase(GraphConfigurationDetail.NONE_CONFIG) == 0) {
outList.add(1);
outList.add(builds.size());
}
return outList;
}
public AbstractProject<?, ?> getProject() {
return project;
}
public List<String> getPerformanceReportList() {
this.performanceReportList = new ArrayList<String>(0);
if (this.project != null && this.project.getSomeBuildWithWorkspace() != null) {
File file = new File(this.project.getSomeBuildWithWorkspace().getRootDir(), PerformanceReportMap
.getPerformanceReportDirRelativePath());
if (file.exists()) {
for (File performanceReportFile : file.listFiles()) {
this.performanceReportList.add(performanceReportFile.getName());
}
}
}
if (this.performanceReportList != null) {
Collections.sort(performanceReportList);
}
return this.performanceReportList;
}
public void setPerformanceReportList(List<String> performanceReportList) {
this.performanceReportList = performanceReportList;
}
public boolean isTrendVisibleOnProjectDashboard() {
if (getPerformanceReportList() != null && getPerformanceReportList().size() == 1) {
return true;
} else {
return false;
}
}
/**
* Returns the graph configuration for this project.
*
* @param link
* not used
* @param request
* Stapler request
* @param response
* Stapler response
* @return the dynamic result of the analysis (detail page).
*/
public Object getDynamic(final String link, final StaplerRequest request, final StaplerResponse response) {
if (CONFIGURE_LINK.equals(link)) {
return createUserConfiguration(request);
} else if (TRENDREPORT_LINK.equals(link)) {
return createTrendReport(request);
} else {
return null;
}
}
/**
* Creates a view to configure the trend graph for the current user.
*
* @param request
* Stapler request
* @return a view to configure the trend graph for the current user
*/
private Object createUserConfiguration(final StaplerRequest request) {
GraphConfigurationDetail graph = new GraphConfigurationDetail(project, PLUGIN_NAME, request);
return graph;
}
/**
* Creates a view to configure the trend graph for the current user.
*
* @param request
* Stapler request
* @return a view to configure the trend graph for the current user
*/
private Object createTrendReport(final StaplerRequest request) {
String filename = getTrendReportFilename(request);
CategoryDataset dataSet = getTrendReportData (request, filename).build();
TrendReportDetail report = new TrendReportDetail(project, PLUGIN_NAME, request, filename, dataSet);
return report;
}
private String getTrendReportFilename(final StaplerRequest request) {
PerformanceReportPosition performanceReportPosition = new PerformanceReportPosition();
request.bindParameters(performanceReportPosition);
return performanceReportPosition.getPerformanceReportPosition();
}
private DataSetBuilder getTrendReportData(final StaplerRequest request, String performanceReportNameFile) {
DataSetBuilder<String, NumberOnlyBuildLabel> dataSet = new DataSetBuilder<String, NumberOnlyBuildLabel>();
List<?> builds = getProject().getBuilds();
List<Integer> buildsLimits = getFirstAndLastBuild(request, builds);
int nbBuildsToAnalyze = builds.size();
for (Iterator<?> iterator = builds.iterator(); iterator.hasNext();) {
AbstractBuild<?, ?> currentBuild = (AbstractBuild<?, ?>) iterator.next();
if (nbBuildsToAnalyze <= buildsLimits.get(1) && buildsLimits.get(0) <= nbBuildsToAnalyze) {
NumberOnlyBuildLabel label = new NumberOnlyBuildLabel(currentBuild);
PerformanceBuildAction performanceBuildAction = currentBuild.getAction(PerformanceBuildAction.class);
if (performanceBuildAction == null) {
continue;
}
PerformanceReport report = null;
report = performanceBuildAction.getPerformanceReportMap().getPerformanceReport(performanceReportNameFile);
if (report == null) {
nbBuildsToAnalyze--;
continue;
}
dataSet.add(report.getMax(), Messages.ProjectAction_Maximum(), label);
dataSet.add(report.getAverage(), Messages.ProjectAction_Average(), label);
dataSet.add(report.getMin(), Messages.ProjectAction_Minimum(), label);
}
nbBuildsToAnalyze--;
}
return dataSet;
}
}