package hudson.plugins.global_build_stats;
import hudson.Extension;
import hudson.Plugin;
import hudson.model.ManagementLink;
import hudson.model.TaskListener;
import hudson.model.AbstractBuild;
import hudson.model.Hudson;
import hudson.model.listeners.RunListener;
import hudson.plugins.global_build_stats.business.GlobalBuildStatsBusiness;
import hudson.plugins.global_build_stats.model.BuildHistorySearchCriteria;
import hudson.plugins.global_build_stats.model.BuildStatConfiguration;
import hudson.plugins.global_build_stats.model.HistoricScale;
import hudson.plugins.global_build_stats.model.JobBuildResult;
import hudson.plugins.global_build_stats.model.ModelIdGenerator;
import hudson.plugins.global_build_stats.validation.GlobalBuildStatsValidator;
import hudson.plugins.global_build_stats.xstream.GlobalBuildStatsXStreamConverter;
import hudson.security.Permission;
import hudson.util.ChartUtil;
import hudson.util.FormValidation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import javax.servlet.ServletException;
import org.jfree.chart.JFreeChart;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
/**
* Entry point of the global build stats plugin
*
* @author fcamblor
* @plugin
*/
public class GlobalBuildStatsPlugin extends Plugin {
/**
* List of aggregated job build results
* This list will grow over time
*/
private List<JobBuildResult> jobBuildResults = new ArrayList<JobBuildResult>();
/**
* List of persisted build statistics configurations used on the
* global build stats screen
*/
private List<BuildStatConfiguration> buildStatConfigs = new ArrayList<BuildStatConfiguration>();
/**
* Business layer for global build stats
*/
transient private GlobalBuildStatsBusiness business = new GlobalBuildStatsBusiness(this);
/**
* Validator layer for global build stats
*/
transient private GlobalBuildStatsValidator validator = new GlobalBuildStatsValidator();
@Override
public void start() throws Exception {
super.start();
Hudson.XSTREAM.registerConverter(new GlobalBuildStatsXStreamConverter());
// XStream compacting aliases...
Hudson.XSTREAM.alias(GlobalBuildStatsXStreamConverter.JOB_BUILD_RESULT_CLASS_ALIAS, JobBuildResult.class);
Hudson.XSTREAM.alias(GlobalBuildStatsXStreamConverter.BUILD_STAT_CONFIG_CLASS_ALIAS, BuildStatConfiguration.class);
Hudson.XSTREAM.aliasField("t", BuildStatConfiguration.class, "buildStatTitle");
Hudson.XSTREAM.aliasField("w", BuildStatConfiguration.class, "buildStatWidth");
Hudson.XSTREAM.aliasField("h", BuildStatConfiguration.class, "buildStatHeight");
Hudson.XSTREAM.aliasField("l", BuildStatConfiguration.class, "historicLength");
Hudson.XSTREAM.aliasField("s", BuildStatConfiguration.class, "historicScale");
Hudson.XSTREAM.aliasField("jf", BuildStatConfiguration.class, "jobFilter");
Hudson.XSTREAM.aliasField("sbr", BuildStatConfiguration.class, "shownBuildResults");
Hudson.XSTREAM.aliasField("r", JobBuildResult.class, "result");
Hudson.XSTREAM.aliasField("n", JobBuildResult.class, "jobName");
Hudson.XSTREAM.aliasField("nb", JobBuildResult.class, "buildNumber");
Hudson.XSTREAM.aliasField("d", JobBuildResult.class, "buildDate");
}
@Override
public void postInitialize() throws Exception {
super.postInitialize();
// Reload plugin informations
this.load();
}
/**
* Let's add a link in the administration panel linking to the global build stats page
*/
@Extension
public static class GlobalBuildStatsManagementLink extends ManagementLink {
public String getIconFileName() {
return "/plugin/global-build-stats/icons/global-build-stats.png";
}
public String getDisplayName() {
return "Global Builds Stats";
}
public String getUrlName() {
return "plugin/global-build-stats/";
}
@Override public String getDescription() {
return "Displays stats about daily build results";
}
}
/**
* At the end of every jobs, let's gather job result informations into global build stats
* persisted data
*/
@Extension
public static class GlobalBuildStatsRunListener extends RunListener<AbstractBuild>{
public GlobalBuildStatsRunListener() {
super(AbstractBuild.class);
}
@Override
public void onCompleted(AbstractBuild r, TaskListener listener) {
super.onCompleted(r, listener);
getPluginBusiness().onJobCompleted(r);
}
}
private static GlobalBuildStatsBusiness getPluginBusiness(){
// Retrieving global build stats plugin & adding build result to the registered build
// result
return Hudson.getInstance().getPlugin(GlobalBuildStatsPlugin.class).business;
}
// Form validations
public FormValidation doCheckJobFilter(@QueryParameter String value){
return validator.checkJobFilter(value);
}
public FormValidation doCheckFailuresShown(@QueryParameter String value){
return validator.checkFailuresShown(value);
}
public FormValidation doCheckUnstablesShown(@QueryParameter String value){
return validator.checkUnstablesShown(value);
}
public FormValidation doCheckAbortedShown(@QueryParameter String value){
return validator.checkAbortedShown(value);
}
public FormValidation doCheckNotBuildsShown(@QueryParameter String value){
return validator.checkNotBuildsShown(value);
}
public FormValidation doCheckSuccessShown(@QueryParameter String value){
return validator.checkSuccessShown(value);
}
public FormValidation doCheckHistoricScale(@QueryParameter String value){
return validator.checkHistoricScale(value);
}
public FormValidation doCheckHistoricLength(@QueryParameter String value){
return validator.checkHistoricLength(value);
}
public FormValidation doCheckBuildStatHeight(@QueryParameter String value){
return validator.checkBuildStatHeight(value);
}
public FormValidation doCheckBuildStatWidth(@QueryParameter String value){
return validator.checkBuildStatWidth(value);
}
public FormValidation doCheckTitle(@QueryParameter String value){
return validator.checkTitle(value);
}
public HttpResponse doRecordBuildInfos() throws IOException {
Hudson.getInstance().checkPermission(getRequiredPermission());
business.recordBuildInfos();
return new HttpResponse() {
public void generateResponse(StaplerRequest req, StaplerResponse rsp,
Object node) throws IOException, ServletException {
}
};
}
public void doShowChart(StaplerRequest req, StaplerResponse res) throws ServletException, IOException {
// Don't check any role : this url is public and should provide a BuildStatConfiguration public id
BuildStatConfiguration config = business.searchBuildStatConfigById(req.getParameter("buildStatId"));
if(config == null){
throw new IllegalArgumentException("Unknown buildStatId parameter !");
}
JFreeChart chart = business.createChart(config);
ChartUtil.generateGraph(req, res, chart, config.getBuildStatWidth(), config.getBuildStatHeight());
}
public void doCreateChart(StaplerRequest req, StaplerResponse res) throws ServletException, IOException {
Hudson.getInstance().checkPermission(getRequiredPermission());
// Passing null id since this is a not persisted BuildStatConfiguration
BuildStatConfiguration config = createBuildStatConfig(null, req);
JFreeChart chart = business.createChart(config);
ChartUtil.generateGraph(req, res, chart, config.getBuildStatWidth(), config.getBuildStatHeight());
}
public void doCreateChartMap(StaplerRequest req, StaplerResponse res) throws ServletException, IOException {
Hudson.getInstance().checkPermission(getRequiredPermission());
String buildStatId = req.getParameter("buildStatId");
BuildStatConfiguration config = null;
if(buildStatId != null){
config = business.searchBuildStatConfigById(buildStatId);
} else {
// Passing null id since this is a not persisted BuildStatConfiguration
config = createBuildStatConfig(null, req);
}
JFreeChart chart = business.createChart(config);
ChartUtil.generateClickableMap(req, res, chart, config.getBuildStatWidth(), config.getBuildStatHeight());
}
public void doBuildHistory(StaplerRequest req, StaplerResponse res) throws ServletException, IOException {
Hudson.getInstance().checkPermission(getRequiredPermission());
BuildHistorySearchCriteria searchCriteria = new BuildHistorySearchCriteria();
req.bindParameters(searchCriteria);
List<JobBuildResult> filteredJobBuildResults = business.searchBuilds(searchCriteria);
req.setAttribute("jobResults", filteredJobBuildResults);
req.setAttribute("searchCriteria", searchCriteria);
req.getView(this, "/hudson/plugins/global_build_stats/GlobalBuildStatsPlugin/buildHistory.jelly").forward(req, res);
}
public void doUpdateBuildStatConfiguration(StaplerRequest req, StaplerResponse res) throws ServletException, IOException {
Hudson.getInstance().checkPermission(getRequiredPermission());
business.updateBuildStatConfiguration(req.getParameter("buildStatId"), createBuildStatConfig(req.getParameter("buildStatId"), req));
res.forwardToPreviousPage(req);
}
public void doAddBuildStatConfiguration(StaplerRequest req, StaplerResponse res) throws ServletException, IOException {
Hudson.getInstance().checkPermission(getRequiredPermission());
business.addBuildStatConfiguration(
createBuildStatConfig(ModelIdGenerator.INSTANCE.generateIdForClass(BuildStatConfiguration.class), req));
res.forwardToPreviousPage(req);
}
public void doDeleteConfiguration(StaplerRequest req, StaplerResponse res) throws ServletException, IOException {
Hudson.getInstance().checkPermission(getRequiredPermission());
business.deleteBuildStatConfiguration(req.getParameter("buildStatId"));
res.forwardToPreviousPage(req);
}
public void doMoveUpConf(StaplerRequest req, StaplerResponse res) throws ServletException, IOException {
Hudson.getInstance().checkPermission(getRequiredPermission());
business.moveUpConf(req.getParameter("buildStatId"));
res.forwardToPreviousPage(req);
}
public void doMoveDownConf(StaplerRequest req, StaplerResponse res) throws ServletException, IOException {
Hudson.getInstance().checkPermission(getRequiredPermission());
business.moveDownConf(req.getParameter("buildStatId"));
res.forwardToPreviousPage(req);
}
/**
* Method must stay here since, for an unknown reason, in buildHistory.jelly,
* call to <j:invokeStatic> doesn't work (and <j:invoke> work fine !)
* @param value Parameter which should be escaped
* @return value where "\" are escaped
*/
public static String escapeAntiSlashes(String value){
return GlobalBuildStatsBusiness.escapeAntiSlashes(value);
}
private BuildStatConfiguration createBuildStatConfig(String id, StaplerRequest req){
// TODO: refactor this using StaplerRequest.bindParameters() with introspection !
return new BuildStatConfiguration(
id,
req.getParameter("title"),
Integer.parseInt(req.getParameter("buildStatWidth")),
Integer.parseInt(req.getParameter("buildStatHeight")),
Integer.parseInt(req.getParameter("historicLength")),
HistoricScale.valueOf(req.getParameter("historicScale")),
req.getParameter("jobFilter"),
Boolean.parseBoolean(req.getParameter("successShown")),
Boolean.parseBoolean(req.getParameter("failuresShown")),
Boolean.parseBoolean(req.getParameter("unstablesShown")),
Boolean.parseBoolean(req.getParameter("abortedShown")),
Boolean.parseBoolean(req.getParameter("notBuildsShown")));
}
public BuildStatConfiguration[] getBuildStatConfigsArrayed() {
return buildStatConfigs.toArray(new BuildStatConfiguration[]{});
}
public List<BuildStatConfiguration> getBuildStatConfigs() {
return buildStatConfigs;
}
public Permission getRequiredPermission(){
return Hudson.ADMINISTER;
}
public HistoricScale[] getHistoricScales(){
return HistoricScale.values();
}
public List<JobBuildResult> getJobBuildResults() {
return jobBuildResults;
}
public void setJobBuildResults(List<JobBuildResult> jobBuildResults) {
this.jobBuildResults = jobBuildResults;
}
}