package hudson; import hudson.model.Saveable; import hudson.model.Hudson; import java.io.IOException; /** * Transaction-like object that can be used to make a bunch of changes to an object, and defer the * {@link Saveable#save()} until the end. * *
* The usage of {@link BulkChange} needs to follow a specific closure-like pattern, namely: * *
* BulkChange bc = new BulkChange(someObject);
* try {
* ... make changes to 'someObject'
* } finally {
* bc.commit();
* }
*
*
* * ... or if you'd like to avoid saving when something bad happens: * *
* BulkChange bc = new BulkChange(someObject);
* try {
* ... make changes to 'someObject'
* bc.commit();
* } finally {
* bc.abort();
* }
*
*
* * Use of this method is optional. If {@link BulkChange} is not used, individual mutator * will perform the save operation, and things will just run somewhat slower. * * *
* For this class to work as intended, {@link Saveable} implementations need to co-operate. * Namely, * *
* See {@link Hudson#save()} as an example if you are not sure how to implement {@link Saveable}. * * @author Kohsuke Kawaguchi * @since 1.249 */ public class BulkChange { private final Saveable saveable; public final Exception allocator; private final BulkChange parent; private boolean completed; public BulkChange(Saveable saveable) { this.parent = current(); this.saveable = saveable; // rememeber who allocated this object in case // someone forgot to call save() at the end. allocator = new Exception(); // in effect at construction INSCOPE.set(this); } /** * Saves the accumulated changes. */ public void commit() throws IOException { if(completed) return; completed = true; // move this object out of the scope first before save, or otherwise the save() method will do nothing. pop(); saveable.save(); } /** * Exits the scope of {@link BulkChange} without saving the changes. * *
* This can be used when a bulk change fails in the middle. * Note that unlike a real transaction, this will not roll back the state of the object. * *
* The abort method can be called after the commit method, in which case this method does nothing.
* This is so that {@link BulkChange} can be used naturally in the try/finally block.
*/
public void abort() {
if(completed) return;
completed = true;
pop();
}
private void pop() {
if(current()!=this)
throw new AssertionError("Trying to save BulkChange that's not in scope");
INSCOPE.set(parent);
}
/**
* {@link BulkChange}s that are effective currently.
*/
private static final ThreadLocal
* The expected usage is from the {@link Saveable#save()} implementation to check
* if the actual persistence should happen now or not.
*/
public static boolean contains(Saveable s) {
for(BulkChange b=current(); b!=null; b=b.parent)
if(b.saveable== s)
return true;
return false;
}
}