package jenkins.branch;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.BulkChange;
import hudson.Extension;
import hudson.model.Job;
import hudson.model.Run;
import jenkins.model.BuildDiscarder;
import org.kohsuke.stapler.DataBoundConstructor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* @author Stephen Connolly
*/
public class BuildRetentionBranchProperty extends BranchProperty {
private final BuildDiscarder buildDiscarder;
@DataBoundConstructor
public BuildRetentionBranchProperty(BuildDiscarder buildDiscarder) {
this.buildDiscarder = buildDiscarder;
}
public BuildDiscarder getBuildDiscarder() {
return buildDiscarder;
}
@Override
public <P extends Job<P,B>,B extends Run<P,B>> JobDecorator<P,B> jobDecorator(Class<P> jobType) {
return new JobDecorator<P, B>(){
@NonNull
@Override
public P project(@NonNull final P project) {
// HACK ALERT
// ==========
// The lesser evil hack is to set the field by reflection, so we try that first
Boolean success = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
try {
Field logRotator = Job.class.getDeclaredField("logRotator");
boolean accessible = logRotator.isAccessible();
try {
if (!accessible) logRotator.setAccessible(true);
logRotator.set(project, buildDiscarder);
// now finally check our handywork in case the field gets renamed.
return project.getBuildDiscarder() == buildDiscarder;
} finally {
if (!accessible) logRotator.setAccessible(false);
}
} catch (IllegalAccessException e) {
return false;
} catch (NoSuchFieldException e) {
return false;
}
}
});
if (!Boolean.TRUE.equals(success)) {
// ok that didn't work
BulkChange bc = new BulkChange(project);
try {
// HACK ALERT
// ==========
// A side-effect of setting the buildDiscarder is that it will try to save the job
// we don't actually want to save the job in this method, so we turn off saving by
// abusing BulkChange and the fact that BulkChange.abort does not revert the state
// of the object.
project.setBuildDiscarder(buildDiscarder);
} catch (IOException e) {
// ignore
} finally {
// we don't actually want to save the change
bc.abort();
}
}
return project;
}
};
}
/**
* Our {@link hudson.model.Descriptor}.
*/
@Extension
@SuppressWarnings("unused") // instantiated by Jenkins
public static class DescriptorImpl extends BranchPropertyDescriptor {
/**
* {@inheritDoc}
*/
@Override
public String getDisplayName() {
return "Discard old builds";
}
}
}