diff --git a/core/src/main/java/hudson/model/Action.java b/core/src/main/java/hudson/model/Action.java index 2e8d2e641f..9e48742986 100644 --- a/core/src/main/java/hudson/model/Action.java +++ b/core/src/main/java/hudson/model/Action.java @@ -68,6 +68,11 @@ import javax.annotation.CheckForNull; * (for example with {@link Build}) via XStream. In some other cases, * {@link Action}s are transient and not persisted (such as * when it's used with {@link Job}). + *
+ * The {@link Actionable#replaceAction(Action)}, {@link Actionable#addOrReplaceAction(Action)}, and
+ * {@link Actionable#removeAction(Action)} methods use {@link Action#equals(Object)} to determine whether to update
+ * or replace or remove an {@link Action}. As such, {@link Action} subclasses that provide a deep
+ * {@link #equals(Object)} will assist in reducing the need for unnecessary persistence.
*
* @author Kohsuke Kawaguchi
*/
diff --git a/core/src/main/java/hudson/model/Actionable.java b/core/src/main/java/hudson/model/Actionable.java
index 7734785421..7a5e92daa1 100644
--- a/core/src/main/java/hudson/model/Actionable.java
+++ b/core/src/main/java/hudson/model/Actionable.java
@@ -133,8 +133,9 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
}
/**
- * Adds a new action. Note calls to {@link #getAllActions()} that happen before calls to this method may not see the
- * update.Note: this method will always modify the actions
+ * Adds a new action.
+ * Note: calls to {@link #getAllActions()} that happen before calls to this method may not see the update.
+ * Note: this method will always modify the actions
*
* The default implementation is mostly equivalent to the call chain {@code getActions().add(a)}.
*/
@@ -148,15 +149,34 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
}
/**
- * Add an action, replacing any existing action of the (exact) same class. Note calls to {@link #getAllActions()}
- * that happen before calls to this method may not see the update. Note: this method will always modify the
- * actions
+ * Add an action, replacing any existing actions of the (exact) same class.
+ * Note: calls to {@link #getAllActions()} that happen before calls to this method may not see the update.
+ * Note: this method does not affect transient actions contributed by a {@link TransientActionFactory}
+ *
* @param a an action to add/replace
* @since 1.548
+ * @see #addOrReplaceAction(Action) if you want to know whether the backing {@link #actions} was modified, for
+ * example in cases where the caller would need to persist the {@link Actionable} in order to persist the change
+ * and there is a desire to elide unneccessary persistence of unmodified objects.
*/
@SuppressWarnings({"ConstantConditions", "deprecation"})
@SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE")
public void replaceAction(@Nonnull Action a) {
+ addOrReplaceAction(a);
+ }
+
+ /**
+ * Add an action, replacing any existing actions of the (exact) same class.
+ * Note: calls to {@link #getAllActions()} that happen before calls to this method may not see the update.
+ * Note: this method does not affect transient actions contributed by a {@link TransientActionFactory}
+ *
+ * @param a an action to add/replace
+ * @return {@code true} if this actions changed as a result of the call
+ * @since FIXME
+ */
+ @SuppressWarnings({"ConstantConditions", "deprecation"})
+ @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE")
+ public boolean addOrReplaceAction(@Nonnull Action a) {
if (a == null) {
throw new IllegalArgumentException("Action must be non-null");
}
@@ -175,11 +195,13 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
if (!found) {
addAction(a);
}
+ return !found || !old.isEmpty();
}
/**
- * Remove an action. Note calls to {@link #getAllActions()} that happen before calls to this method may not see the
- * update.
+ * Remove an action.
+ * Note: calls to {@link #getAllActions()} that happen before calls to this method may not see the update.
+ * Note: this method does not affect transient actions contributed by a {@link TransientActionFactory}
*
* @param a an action to remove (if {@code null} then this will be a no-op)
* @return {@code true} if this actions changed as a result of the call
@@ -195,8 +217,9 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
}
/**
- * Removes any actions of the specified type. Note calls to {@link #getAllActions()} that happen before calls to
- * this method may not see the update.
+ * Removes any actions of the specified type.
+ * Note: calls to {@link #getAllActions()} that happen before calls to this method may not see the update.
+ * Note: this method does not affect transient actions contributed by a {@link TransientActionFactory}
*
* @param clazz the type of actions to remove
* @return {@code true} if this actions changed as a result of the call
@@ -220,8 +243,9 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
}
/**
- * Replaces any actions of the specified type by the supplied action. Note calls to {@link #getAllActions()} that
- * happen before calls to this method may not see the update.
+ * Replaces any actions of the specified type by the supplied action.
+ * Note: calls to {@link #getAllActions()} that happen before calls to this method may not see the update.
+ * Note: this method does not affect transient actions contributed by a {@link TransientActionFactory}
*
* @param clazz the type of actions to replace (note that the action you are replacing this with need not extend
* this class)
@@ -235,6 +259,9 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
if (clazz == null) {
throw new IllegalArgumentException("Action type must be non-null");
}
+ if (a == null) {
+ throw new IllegalArgumentException("Action must be non-null");
+ }
// CopyOnWriteArrayList does not support Iterator.remove, so need to do it this way:
List