Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce SaveableListener#onDeleted #9743

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/src/main/java/hudson/logging/LogRecorder.java
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ public void delete() throws IOException {
loggers.forEach(Target::disable);

getParent().getRecorders().forEach(logRecorder -> logRecorder.getLoggers().forEach(Target::enable));
SaveableListener.fireOnChange(this, getConfigFile());
SaveableListener.fireOnDeleted(this, getConfigFile());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incompatible, I guess, but unlikely to be a problem?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I guess it is unlikely enough.

}

/**
Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/hudson/model/AbstractItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,7 @@ public void delete() throws IOException, InterruptedException {
ItemDeletion.deregister(this);
}
}
SaveableListener.fireOnDeleted(this, getConfigFile());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A related oddity FTR: #9304 (comment)

getParent().onDeleted(AbstractItem.this);
Jenkins.get().rebuildDependencyGraphAsync();
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/hudson/model/Run.java
Original file line number Diff line number Diff line change
Expand Up @@ -1570,6 +1570,7 @@ public void delete() throws IOException {
));
//Still firing the delete listeners; just no need to clean up rootDir
RunListener.fireDeleted(this);
SaveableListener.fireOnDeleted(this, getDataFile());
synchronized (this) { // avoid holding a lock while calling plugin impls of onDeleted
removeRunFromParent();
}
Expand All @@ -1578,6 +1579,7 @@ public void delete() throws IOException {

//The root dir exists and is a directory that needs to be purged
RunListener.fireDeleted(this);
SaveableListener.fireOnDeleted(this, getDataFile());

if (artifactManager != null) {
deleteArtifacts();
Expand Down
30 changes: 21 additions & 9 deletions core/src/main/java/hudson/model/listeners/SaveableListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
import hudson.ExtensionPoint;
import hudson.XmlFile;
import hudson.model.Saveable;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.util.Listeners;

/**
* Receives notifications about save actions on {@link Saveable} objects in Hudson.
Expand All @@ -54,6 +53,17 @@ public abstract class SaveableListener implements ExtensionPoint {
*/
public void onChange(Saveable o, XmlFile file) {}

/**
* Called when a {@link Saveable} object gets deleted.
*
* @param o
* The saveable object.
* @param file
* The {@link XmlFile} for this saveable object.
* @since TODO
*/
public void onDeleted(Saveable o, XmlFile file) {}

/**
* Registers this object as an active listener so that it can start getting
* callbacks invoked.
Expand All @@ -77,13 +87,15 @@ public void unregister() {
* Fires the {@link #onChange} event.
*/
public static void fireOnChange(Saveable o, XmlFile file) {
for (SaveableListener l : all()) {
try {
l.onChange(o, file);
} catch (Throwable t) {
Logger.getLogger(SaveableListener.class.getName()).log(Level.WARNING, null, t);
}
}
Listeners.notify(SaveableListener.class, false, l -> l.onChange(o, file));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the false is crucial here for things like auditing.

}

/**
* Fires the {@link #onDeleted} event.
* @since TODO
*/
public static void fireOnDeleted(Saveable o, XmlFile file) {
Listeners.notify(SaveableListener.class, false, l -> l.onDeleted(o, file));
}

/**
Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/jenkins/model/Nodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ public void run() {
jenkins.trimLabels(node);
}
NodeListener.fireOnDeleted(node);
SaveableListener.fireOnDeleted(node, getConfigFile(node));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ public static class DeletingLogRecorderListener extends SaveableListener {
private static boolean recordDeletion;

@Override
public void onChange(Saveable o, XmlFile file) {
public void onDeleted(Saveable o, XmlFile file) {
if (o instanceof LogRecorder && "dummy".equals(((LogRecorder) o).getName())) {
if (!file.exists()) {
recordDeletion = true;
Expand Down
26 changes: 19 additions & 7 deletions test/src/test/java/hudson/model/AbstractItemTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;

import hudson.ExtensionList;
import hudson.XmlFile;
Expand Down Expand Up @@ -61,7 +62,7 @@ public void reload() throws Exception {
// reload away
p.doReload();

assertFalse(SaveableListener.class.getSimpleName() + " should not have been called", testSaveableListener.wasCalled());
assertFalse(SaveableListener.class.getSimpleName() + " should not have been called", testSaveableListener.isChangeCalled());


assertEquals("Good Evening", p.getDescription());
Expand All @@ -70,6 +71,9 @@ public void reload() throws Exception {

assertNotEquals(b, b2); // should be different object
assertEquals(b.getDescription(), b2.getDescription()); // but should have the same properties

p.delete();
assertTrue(SaveableListener.class.getSimpleName() + " should have been called", testSaveableListener.isDeleteCalled());
}

@Test
Expand Down Expand Up @@ -158,21 +162,29 @@ private String getPath(URL u) {
public static class TestSaveableListener extends SaveableListener {
private Saveable saveable;

private boolean called;
private boolean changeCalled;
private boolean deleteCalled;

private void setSaveable(Saveable saveable) {
this.saveable = saveable;
}

public boolean wasCalled() {
return called;
public boolean isChangeCalled() {
return changeCalled;
}

public boolean isDeleteCalled() {
return deleteCalled;
}

@Override
public void onChange(Saveable o, XmlFile file) {
if (o == saveable) {
this.called = true;
}
this.changeCalled |= o == saveable;
}

@Override
public void onDeleted(Saveable o, XmlFile file) {
this.deleteCalled |= o == saveable;
}
}
}
15 changes: 15 additions & 0 deletions test/src/test/java/hudson/model/RunTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import hudson.ExtensionList;
import hudson.FilePath;
import hudson.Launcher;
import hudson.XmlFile;
import hudson.model.listeners.ItemListener;
import hudson.model.listeners.SaveableListener;
import hudson.tasks.ArtifactArchiver;
import hudson.tasks.BuildTrigger;
import hudson.tasks.Builder;
Expand Down Expand Up @@ -112,6 +116,17 @@ public class RunTest {
FreeStyleBuild b = j.buildAndAssertSuccess(p);
b.delete();
assertTrue(Mgr.deleted.get());
assertTrue(ExtensionList.lookupSingleton(SaveableListenerImpl.class).deleted);
}

@TestExtension("deleteArtifactsCustom")
public static class SaveableListenerImpl extends SaveableListener {
boolean deleted;

@Override
public void onDeleted(Saveable o, XmlFile file) {
deleted = true;
}
}

@Issue("SECURITY-1902")
Expand Down
17 changes: 17 additions & 0 deletions test/src/test/java/jenkins/model/NodesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.ExtensionList;
import hudson.XmlFile;
import hudson.model.Descriptor;
import hudson.model.Failure;
import hudson.model.Node;
import hudson.model.Saveable;
import hudson.model.Slave;
import hudson.model.listeners.SaveableListener;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.DumbSlave;
import java.io.IOException;
Expand Down Expand Up @@ -99,6 +102,10 @@ public void addNodeShouldReplaceExistingNode() throws Exception {
assertEquals(0, l.deleted);
assertEquals(1, l.updated);
assertEquals(1, l.created);
var saveableListener = ExtensionList.lookupSingleton(SaveableListenerImpl.class);
assertEquals(0, saveableListener.deleted);
r.jenkins.removeNode(newNode);
assertEquals(1, saveableListener.deleted);
}

@TestExtension("addNodeShouldReplaceExistingNode")
Expand All @@ -122,6 +129,16 @@ protected void onCreated(Node node) {
}
}

@TestExtension("addNodeShouldReplaceExistingNode")
public static final class SaveableListenerImpl extends SaveableListener {
int deleted;

@Override
public void onDeleted(Saveable o, XmlFile file) {
deleted++;
}
}

@Test
@Issue("JENKINS-56403")
public void replaceNodeShouldRemoveOldNode() throws Exception {
Expand Down