提交 f1a751f7 编写于 作者: J Jesse Glick

[FIXED JENKINS-18224] Introduced TransientActionFactory.

上级 cf396b0f
......@@ -3,7 +3,6 @@ package hudson.matrix;
import groovy.lang.GroovyRuntimeException;
import hudson.AbortException;
import hudson.Extension;
import hudson.Util;
import hudson.console.ModelHyperlinkNote;
import hudson.matrix.MatrixBuild.MatrixBuildExecution;
import hudson.matrix.listeners.MatrixBuildListener;
......@@ -238,7 +237,7 @@ public class DefaultMatrixExecutionStrategyImpl extends MatrixExecutionStrategy
exec.getListener().getLogger().println(Messages.MatrixBuild_Triggering(ModelHyperlinkNote.encodeTo(c)));
// filter the parent actions for those that can be passed to the individual jobs.
List<MatrixChildAction> childActions = Util.filter(build.getActions(), MatrixChildAction.class);
List<MatrixChildAction> childActions = build.getActions(MatrixChildAction.class);
c.scheduleBuild(childActions, new UpstreamCause((Run)build));
}
......
......@@ -30,7 +30,6 @@ import hudson.EnvVars;
import hudson.FilePath;
import hudson.Functions;
import hudson.Launcher;
import hudson.Util;
import hudson.console.AnnotatedLargeText;
import hudson.console.ExpandableDetailsNote;
import hudson.console.ModelHyperlinkNote;
......@@ -922,7 +921,7 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
for (Environment e : buildEnvironments)
e.buildEnvVars(env);
for (EnvironmentContributingAction a : Util.filter(getActions(),EnvironmentContributingAction.class))
for (EnvironmentContributingAction a : getActions(EnvironmentContributingAction.class))
a.buildEnvVars(this,env);
EnvVars.resolve(env);
......
......@@ -776,13 +776,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
}
public List<ProminentProjectAction> getProminentActions() {
List<Action> a = getActions();
List<ProminentProjectAction> pa = new Vector<ProminentProjectAction>();
for (Action action : a) {
if(action instanceof ProminentProjectAction)
pa.add((ProminentProjectAction) action);
}
return pa;
return getActions(ProminentProjectAction.class);
}
@Override
......
......@@ -23,27 +23,19 @@
*/
package hudson.model;
import hudson.Functions;
import hudson.model.queue.Tasks;
import hudson.Util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import jenkins.model.Jenkins;
import jenkins.model.ModelObjectWithContextMenu;
import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.JellyTagException;
import org.apache.commons.jelly.Script;
import org.apache.commons.jelly.XMLOutput;
import jenkins.model.TransientActionFactory;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.WebApp;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.jelly.JellyClassTearOff;
import org.kohsuke.stapler.jelly.JellyFacet;
import org.xml.sax.helpers.DefaultHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* {@link ModelObject} that can have additional {@link Action}s.
......@@ -60,15 +52,19 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
private volatile CopyOnWriteArrayList<Action> actions;
/**
* Gets actions contributed to this build.
* Gets actions contributed to this object.
*
* <p>
* A new {@link Action} can be added by {@code getActions().add(...)}.
*
* <p>If you are <em>reading</em> the list, rather than <em>modifying</em> it,
* use {@link #getAllActions} instead.
* This method by default returns only <em>persistent</em> actions
* (though some subclasses override it to return an extended unmodifiable list).
*
* @return
* may be empty but never null.
*/
@Exported
public List<Action> getActions() {
if(actions == null) {
synchronized (this) {
......@@ -81,7 +77,27 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
}
/**
* Gets all actions of a specified type that contributed to this build.
* Gets all actions, transient or persistent.
* {@link #getActions} is supplemented with anything contributed by {@link TransientActionFactory}.
* @return an unmodifiable, possible empty list
* @since TODO
*/
@Exported(name="actions")
public final List<? extends Action> getAllActions() {
List<Action> _actions = new ArrayList<Action>(getActions());
for (TransientActionFactory<?> taf : Jenkins.getInstance().getExtensionList(TransientActionFactory.class)) {
if (taf.type().isInstance(this)) {
_actions.addAll(createFor(taf));
}
}
return Collections.unmodifiableList(_actions);
}
private <T extends Actionable> Collection<? extends Action> createFor(TransientActionFactory<T> taf) {
return taf.createFor(taf.type().cast(this));
}
/**
* Gets all actions of a specified type that contributed to this object.
*
* @param type The type of action to return.
* @return
......@@ -89,11 +105,7 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
* @see #getAction(Class)
*/
public <T extends Action> List<T> getActions(Class<T> type) {
List<T> result = new Vector<T>();
for (Action a : getActions())
if (type.isInstance(a))
result.add(type.cast(a));
return result;
return Util.filter(getAllActions(), type);
}
/**
......@@ -106,6 +118,8 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
getActions().add(a);
}
/** @deprecated No clear purpose, since subclasses may have overridden {@link #getActions}, and does not consider {@link TransientActionFactory}. */
@Deprecated
public Action getAction(int index) {
if(actions==null) return null;
return actions.get(index);
......@@ -119,14 +133,14 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
* @see #getActions(Class)
*/
public <T extends Action> T getAction(Class<T> type) {
for (Action a : getActions())
for (Action a : getAllActions())
if (type.isInstance(a))
return type.cast(a);
return null;
}
public Object getDynamic(String token, StaplerRequest req, StaplerResponse rsp) {
for (Action a : getActions()) {
for (Action a : getAllActions()) {
if(a==null)
continue; // be defensive
String urlName = a.getUrlName();
......
......@@ -189,7 +189,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
}
result.addAll(transientActions);
}
return result;
return Collections.unmodifiableList(result);
}
@Override
......
......@@ -972,11 +972,8 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
public PermalinkList getPermalinks() {
// TODO: shall we cache this?
PermalinkList permalinks = new PermalinkList(Permalink.BUILTIN);
for (Action a : getActions()) {
if (a instanceof PermalinkProjectAction) {
PermalinkProjectAction ppa = (PermalinkProjectAction) a;
permalinks.addAll(ppa.getPermalinks());
}
for (PermalinkProjectAction ppa : getActions(PermalinkProjectAction.class)) {
permalinks.addAll(ppa.getPermalinks());
}
return permalinks;
}
......
......@@ -539,7 +539,7 @@ public class Queue extends ResourceController implements Saveable {
shouldScheduleItem |= action.shouldSchedule(actions);
}
for (QueueAction action: Util.filter(actions,QueueAction.class)) {
shouldScheduleItem |= action.shouldSchedule(item.getActions());
shouldScheduleItem |= action.shouldSchedule((new ArrayList<Action>(item.getAllActions())));
}
if(!shouldScheduleItem) {
duplicatesInQueue.add(item);
......@@ -1417,7 +1417,7 @@ public class Queue extends ResourceController implements Saveable {
}
protected Item(Item item) {
this(item.task, item.getActions(), item.id, item.future, item.inQueueSince);
this(item.task, new ArrayList<Action>(item.getAllActions()), item.id, item.future, item.inQueueSince);
}
/**
......@@ -1453,13 +1453,10 @@ public class Queue extends ResourceController implements Saveable {
@Exported
public String getParams() {
StringBuilder s = new StringBuilder();
for(Action action : getActions()) {
if(action instanceof ParametersAction) {
ParametersAction pa = (ParametersAction)action;
for (ParameterValue p : pa.getParameters()) {
s.append('\n').append(p.getShortDescription());
}
}
for (ParametersAction pa : getActions(ParametersAction.class)) {
for (ParameterValue p : pa.getParameters()) {
s.append('\n').append(p.getShortDescription());
}
}
return s.toString();
}
......
......@@ -332,7 +332,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
*/
@SuppressWarnings("deprecation")
protected void onLoad() {
for (Action a : getActions()) {
for (Action a : getAllActions()) {
if (a instanceof RunAction2) {
((RunAction2) a).onLoad(this);
} else if (a instanceof RunAction) {
......@@ -348,7 +348,9 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
* Return all transient actions associated with this build.
*
* @return the list can be empty but never null. read only.
* @deprecated Use {@link #getAllActions} instead.
*/
@Deprecated
public List<Action> getTransientActions() {
List<Action> actions = new ArrayList<Action>();
for (TransientBuildActionFactory factory: TransientBuildActionFactory.all()) {
......@@ -443,21 +445,11 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
* Gets the subset of {@link #getActions()} that consists of {@link BuildBadgeAction}s.
*/
public List<BuildBadgeAction> getBadgeActions() {
List<BuildBadgeAction> r = null;
for (Action a : getActions()) {
if(a instanceof BuildBadgeAction) {
if(r==null)
r = new ArrayList<BuildBadgeAction>();
r.add((BuildBadgeAction)a);
}
}
List<BuildBadgeAction> r = getActions(BuildBadgeAction.class);
if(isKeepLog()) {
if(r==null)
r = new ArrayList<BuildBadgeAction>();
r.add(new KeepLogBuildBadge());
}
if(r==null) return Collections.emptyList();
else return r;
return r;
}
/**
......@@ -1389,7 +1381,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
SearchIndexBuilder builder = super.makeSearchIndex()
.add("console")
.add("changes");
for (Action a : getActions()) {
for (Action a : getAllActions()) {
if(a.getIconFileName()!=null)
builder.add(a.getUrlName());
}
......
......@@ -6,6 +6,7 @@ import hudson.ExtensionPoint;
import jenkins.model.Jenkins;
import java.util.Collection;
import java.util.Collections;
import jenkins.model.TransientActionFactory;
/**
* Extension point for inserting transient {@link Action}s into {@link Run}s.
......@@ -15,8 +16,9 @@ import java.util.Collections;
* @author Lucie Votypkova
* @since 1.458
* @see Action
* @deprecated Does not contribute to {@link Run#getActions}. Use {@link TransientActionFactory} instead.
*/
@Deprecated
public abstract class TransientBuildActionFactory implements ExtensionPoint {
/**
* Creates actions for the given build.
......
......@@ -30,6 +30,7 @@ import jenkins.model.Jenkins;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import jenkins.model.TransientActionFactory;
/**
* Extension point for inserting transient {@link hudson.model.Action}s to {@link hudson.model.Computer}s.
......@@ -39,6 +40,7 @@ import java.util.List;
* @author Stephen Connolly
* @since 1.405
* @see hudson.model.Action
* @see TransientActionFactory
*/
public abstract class TransientComputerActionFactory implements ExtensionPoint {
/**
......
......@@ -30,6 +30,7 @@ import hudson.tasks.BuildStep;
import jenkins.model.Jenkins;
import java.util.Collection;
import jenkins.model.TransientActionFactory;
/**
* Extension point for inserting transient {@link Action}s into {@link AbstractProject}s.
......@@ -50,6 +51,7 @@ import java.util.Collection;
* @author Kohsuke Kawaguchi
* @since 1.327
* @see Action
* @see TransientActionFactory
*/
public abstract class TransientProjectActionFactory implements ExtensionPoint {
/**
......
......@@ -67,7 +67,7 @@ public final class WorkUnitContext {
this.item = item;
this.task = item.task;
this.future = (FutureImpl)item.getFuture();
this.actions = item.getActions();
this.actions = new ArrayList<Action>(item.getAllActions());
// +1 for the main task
int workUnitSize = Tasks.getSubTasksOf(task).size();
......
......@@ -214,7 +214,7 @@ public interface ModelObjectWithContextMenu extends ModelObject {
} else
if (self instanceof Actionable) {
// fallback
this.addAll(((Actionable)self).getActions());
this.addAll(((Actionable)self).getAllActions());
}
return this;
......
/*
* The MIT License
*
* Copyright 2013 Jesse Glick.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.model;
import hudson.model.Action;
import hudson.model.Actionable;
import java.util.Collection;
import javax.annotation.Nonnull;
/**
* Allows you to add actions to any kind of {@link Actionable} at once.
* @see Actionable#getAllActions
* @since TODO
*/
public abstract class TransientActionFactory<T extends Actionable> {
/**
* The type of object this factory cares about.
* Declared separately, rather than by having {@link #createFor} do a check-cast,
* so that method bodies are not loaded until actually needed.
* @return the type of {@link T}
*/
public abstract Class<T> type();
/**
* Creates actions for a given object.
* This may be called frequently for the same object, so if your implementation is expensive, do your own caching.
* @param target an actionable object
* @return a possible empty set of actions
*/
public abstract @Nonnull Collection<? extends Action> createFor(@Nonnull T target);
}
......@@ -52,7 +52,7 @@ THE SOFTWARE.
<st:include page="jobpropertysummaries.jelly" />
<!-- merge fragments from the actions -->
<j:forEach var="a" items="${it.actions}">
<j:forEach var="a" items="${it.allActions}">
<st:include page="jobMain.jelly" it="${a}" optional="true" />
</j:forEach>
......
......@@ -108,7 +108,7 @@ THE SOFTWARE.
</t:summary>
<!-- give actions a chance to contribute summary item -->
<j:forEach var="a" items="${it.actions}">
<j:forEach var="a" items="${it.allActions}">
<st:include page="summary.jelly" from="${a}" optional="true" it="${a}" />
</j:forEach>
</table>
......
......@@ -61,7 +61,7 @@ THE SOFTWARE.
</j:if>
</j:if>
<j:forEach var="atr" items="${it.lastCompletedBuild.actions}">
<j:forEach var="atr" items="${it.lastCompletedBuild.allActions}">
<j:if test="${atr!=null}">
<j:if test="${atr.class.name == 'hudson.tasks.test.AggregatedTestResultPublisher$TestResultAction'}">
<t:summary icon="clipboard.png">
......@@ -75,7 +75,7 @@ THE SOFTWARE.
</table>
<!-- merge fragments from the actions -->
<j:forEach var="a" items="${it.actions}">
<j:forEach var="a" items="${it.allActions}">
<st:include page="jobMain.jelly" it="${a}" optional="true" />
</j:forEach>
......
......@@ -28,10 +28,10 @@ THE SOFTWARE.
Shows a list of tasks.
<st:attribute name="actions">
List of actions. Defaults to "it.actions"
List of actions. Defaults to "it.allActions"
</st:attribute>
</st:documentation>
<j:forEach var="action" items="${attrs.actions?:it.actions}">
<j:forEach var="action" items="${attrs.actions?:it.allActions}">
<!--
allow the action to take over the rendering, but otherwise fall back to the default
the action object itself is available in the 'action' variable
......
......@@ -28,7 +28,7 @@ THE SOFTWARE.
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<div style="float:right">
<j:forEach var="a" items="${it.actions}">
<j:forEach var="a" items="${it.allActions}">
<!--
For historical reason, we failed to expose the action as "it", so
this exposes it as "action", without breaking the compatibility with plugins
......
/*
* The MIT License
*
* Copyright 2013 Jesse Glick.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package jenkins.model;
import hudson.model.AbstractItem;
import hudson.model.Action;
import java.util.Collection;
import java.util.Collections;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
public class TransientActionFactoryTest {
@Rule public JenkinsRule r = new JenkinsRule();
@Test public void addedToAbstractItem() throws Exception {
assertNotNull(r.createFolder("d").getAction(MyAction.class));
assertNotNull(r.createFreeStyleProject().getAction(MyAction.class));
}
@TestExtension("addedToAbstractItem") public static class TestItemFactory extends TransientActionFactory<AbstractItem> {
@Override public Class<AbstractItem> type() {return AbstractItem.class;}
@Override public Collection<? extends Action> createFor(AbstractItem i) {
return Collections.singleton(new MyAction());
}
}
private static class MyAction implements Action {
@Override public String getIconFileName() {
return null;
}
@Override public String getDisplayName() {
return null;
}
@Override public String getUrlName() {
return null;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册