提交 215f34b4 编写于 作者: K kohsuke

added JobProperty as the extensible property on Job.


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@1683 71c3de6d-444a-0410-be80-ed276b4c234a
上级 ff22b24a
......@@ -270,5 +270,4 @@ public class Functions {
}
return false;
}
}
......@@ -199,7 +199,7 @@ public abstract class Descriptor<T extends Describable<T>> {
}
public static <T extends Describable<T>>
Map<Descriptor<T>,T> toMap(List<T> describables) {
Map<Descriptor<T>,T> toMap(Iterable<T> describables) {
Map<Descriptor<T>,T> m = new LinkedHashMap<Descriptor<T>,T>();
for (T d : describables) {
m.put(d.getDescriptor(),d);
......
......@@ -116,7 +116,7 @@ public final class Hudson extends JobCollection implements Node {
private transient boolean isQuietingDown;
private transient boolean terminating;
private List<JDK> jdks;
private List<JDK> jdks = new ArrayList<JDK>();
/**
* Set of installed cluster nodes.
......@@ -818,6 +818,9 @@ public final class Hudson extends JobCollection implements Node {
for( Descriptor<Trigger> d : Triggers.TRIGGERS )
result &= d.configure(req);
for( JobPropertyDescriptor d : Jobs.PROPERTIES )
result &= d.configure(req);
save();
if(result)
rsp.sendRedirect("."); // go to the top page
......
......@@ -8,6 +8,7 @@ import hudson.tasks.BuildTrigger;
import hudson.tasks.LogRotator;
import hudson.util.ChartUtil;
import hudson.util.ColorPalette;
import hudson.util.CopyOnWriteList;
import hudson.util.DataSetBuilder;
import hudson.util.IOException2;
import hudson.util.RunList;
......@@ -39,6 +40,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.SortedMap;
import java.util.Map;
/**
* A job is an runnable entity under the monitoring of Hudson.
......@@ -85,6 +87,11 @@ public abstract class Job<JobT extends Job<JobT,RunT>, RunT extends Run<JobT,Run
private boolean keepDependencies;
/**
* List of {@link UserProperty}s configured for this project.
*/
private CopyOnWriteList<JobProperty<? super JobT>> properties = new CopyOnWriteList<JobProperty<? super JobT>>();
protected Job(Hudson parent,String name) {
this.parent = parent;
doSetName(name);
......@@ -113,6 +120,9 @@ public abstract class Job<JobT extends Job<JobT,RunT>, RunT extends Run<JobT,Run
saveNextBuildNumber();
save(); // and delete it from the config.xml
}
if(properties==null) // didn't exist < 1.72
properties = new CopyOnWriteList<JobProperty<? super JobT>>();
}
/**
......@@ -196,6 +206,25 @@ public abstract class Job<JobT extends Job<JobT,RunT>, RunT extends Run<JobT,Run
return getName();
}
/**
* Gets all the job properties configured for this job.
*/
@SuppressWarnings("unchecked")
public Map<JobPropertyDescriptor,JobProperty<? super JobT>> getProperties() {
return Descriptor.toMap((Iterable)properties);
}
/**
* Gets the specific property, or null if the propert is not configured for this job.
*/
public <T extends JobProperty> T getProperty(Class<T> clazz) {
for (JobProperty p : properties) {
if(clazz.isInstance(p))
return clazz.cast(p);
}
return null;
}
/**
* Renames a job.
*/
......
......@@ -2,6 +2,9 @@ package hudson.model;
import org.kohsuke.stapler.StaplerRequest;
import java.util.List;
import java.util.ArrayList;
/**
* {@link Descriptor} for {@link Job}s.
*
......@@ -25,4 +28,15 @@ public abstract class JobDescriptor<J extends Job<J,R>,R extends Run<J,R>> exten
* Creates a new {@link Job}.
*/
public abstract Job<J,R> newInstance(String name);
/**
* Gets the {@link JobPropertyDescriptor}s applicable for this job type.
*/
public List<JobPropertyDescriptor> getPropertyDescriptors() {
List<JobPropertyDescriptor> r = new ArrayList<JobPropertyDescriptor>();
for (JobPropertyDescriptor p : Jobs.PROPERTIES)
if(p.isApplicable(clazz))
r.add(p);
return r;
}
}
package hudson.model;
import hudson.ExtensionPoint;
import hudson.Plugin;
/**
* Extensible property of {@link Job}.
*
* <p>
* {@link Plugin}s can extend this to define custom properties
* for {@link Job}s. {@link JobProperty}s show up in the user
* configuration screen, and they are persisted with the job object.
*
* <p>
* Configuration screen should be defined in <tt>config.jelly</tt>.
* Within this page, the {@link JobProperty} instance is available
* as <tt>instance</tt> variable (while <tt>it</tt> refers to {@link Job}.
*
*
* @author Kohsuke Kawaguchi
* @see JobPropertyDescriptor
* @since 1.72
*/
public abstract class JobProperty<J extends Job<?,?>> implements Describable<JobProperty<?>>, ExtensionPoint {
/**
* The {@link Job} object that owns this property.
* This value will be set by the Hudson code.
* Derived classes can expect this value to be always set.
*/
protected transient J owner;
/*package*/ final void setOwner(J owner) {
this.owner = owner;
}
/**
* {@inheritDoc}
*/
public abstract JobPropertyDescriptor getDescriptor();
}
package hudson.model;
/**
* {@link Descriptor} for {@link JobProperty}.
*
* @author Kohsuke Kawaguchi
* @see Jobs#PROPERTIES
* @since 1.72
*/
public abstract class JobPropertyDescriptor extends Descriptor<JobProperty<?>> {
protected JobPropertyDescriptor(Class<? extends JobProperty<?>> clazz) {
super(clazz);
}
/**
* Returns true if this {@link JobProperty} type is applicable to the
* given job type.
*
* <p>
* Normally, this method is implemented like
* {@code return AbstractProject.class.isAssignableFrom(jobType)}
* where "<tt>AbstractProject</tt>" is the J of {@link JobProperty}<tt>&lt;J></tt>.
*
* @return
* true to indicate applicable, in which case the property will be
* displayed in the configuration screen of this job.
*/
public abstract boolean isApplicable(Class<? extends Job> jobType);
}
......@@ -3,6 +3,7 @@ package hudson.model;
import hudson.maven.MavenJob;
import java.util.List;
import java.util.ArrayList;
/**
* List of all installed {@link Job} types.
......@@ -27,6 +28,17 @@ public class Jobs {
return null;
}
/**
* List of all installed {@link JobPropertyDescriptor} types.
*
* <p>
* Plugins can add their {@link JobPropertyDescriptor}s to this list.
*
* @see JobDescriptor#getPropertyDescriptors()
*/
public static final List<JobPropertyDescriptor> PROPERTIES = Descriptor.toList(
);
static {
if(Boolean.getBoolean("hudson.maven"))
JOBS.add(MavenJob.DESCRIPTOR);
......
package hudson.util;
import java.util.List;
import com.thoughtworks.xstream.alias.CannotResolveClassException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* {@link List}-like implementation that has copy-on-write semantics.
......@@ -17,7 +26,11 @@ public class CopyOnWriteList<E> implements Iterable<E> {
private volatile List<E> core;
public CopyOnWriteList(List<E> core) {
this.core = new ArrayList<E>(core);
this(core,false);
}
private CopyOnWriteList(List<E> core, boolean noCopy) {
this.core = noCopy ? core : new ArrayList<E>(core);
}
public CopyOnWriteList() {
......@@ -65,4 +78,40 @@ public class CopyOnWriteList<E> implements Iterable<E> {
}
};
}
/**
* {@link Converter} implementation for XStream.
*/
public static final class ConverterImpl extends AbstractCollectionConverter {
public ConverterImpl(Mapper mapper) {
super(mapper);
}
public boolean canConvert(Class type) {
return type==CopyOnWriteList.class;
}
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
for (Object o : (CopyOnWriteList) source)
writeItem(o, context, writer);
}
@SuppressWarnings("unchecked")
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
// read the items from xml into a list
List items = new ArrayList();
while (reader.hasMoreChildren()) {
reader.moveDown();
try {
Object item = readItem(reader, context, items);
items.add(item);
} catch (CannotResolveClassException e) {
System.err.println("failed to locate class: "+e);
}
reader.moveUp();
}
return new CopyOnWriteList(items,true);
}
}
}
......@@ -5,6 +5,7 @@ import com.thoughtworks.xstream.converters.DataHolder;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import hudson.model.Hudson;
import hudson.util.CopyOnWriteList.ConverterImpl;
/**
* {@link XStream} enhanced for retroweaver support.
......@@ -36,5 +37,6 @@ public class XStream2 extends XStream {
registerConverter(new RobustCollectionConverter(getClassMapper()),10);
registerConverter(new RetroweaverEnumConverter(),10);
registerConverter(new RetrotranslatorEnumConverter(),10);
registerConverter(new ConverterImpl(getClassMapper()),10);
}
}
......@@ -4,7 +4,7 @@
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:s="/lib/form">
<l:layout norefresh="true">
<st:include page="sidepanel.jelly" />
<l:main-panel>
<l:main-panel xmlns:local="local">
<s:form method="post" action="configSubmit">
<s:entry title="Home directory" help="/help/system-config/homeDirectory.html">
${it.rootDir}
......@@ -108,40 +108,23 @@
</s:entry>
</s:section>
<!-- trigger config pane -->
<j:getStatic var="triggers" className="hudson.triggers.Triggers" field="TRIGGERS" />
<j:forEach var="idx" begin="0" end="${size(triggers)-1}">
<j:set var="descriptor" value="${triggers[idx]}" />
<st:include page="${descriptor.globalConfigPage}" from="${descriptor}" optional="true"/>
</j:forEach>
<!-- build wrapper config pane -->
<j:getStatic var="wrappers" className="hudson.tasks.BuildWrappers" field="WRAPPERS" />
<j:forEach var="idx" begin="0" end="${size(wrappers)-1}">
<j:set var="descriptor" value="${wrappers[idx]}" />
<st:include page="${descriptor.globalConfigPage}" from="${descriptor}" optional="true"/>
</j:forEach>
<!-- builder config pane -->
<j:getStatic var="builds" className="hudson.tasks.BuildStep" field="BUILDERS" />
<j:forEach var="idx" begin="0" end="${size(builds)-1}">
<j:set var="descriptor" value="${builds[idx]}" />
<st:include page="${descriptor.globalConfigPage}" from="${descriptor}" optional="true"/>
</j:forEach>
<!-- SCM config pane -->
<j:getStatic var="scms" className="hudson.scm.SCMS" field="SCMS" />
<j:forEach var="idx" begin="0" end="${size(scms)-1}">
<j:set var="descriptor" value="${scms[idx]}" />
<st:include page="${descriptor.globalConfigPage}" from="${descriptor}" optional="true"/>
</j:forEach>
<!-- publisher config pane -->
<j:getStatic var="pubs" className="hudson.tasks.BuildStep" field="PUBLISHERS" />
<j:forEach var="idx" begin="0" end="${size(pubs)-1}">
<j:set var="descriptor" value="${pubs[idx]}" />
<st:include page="${descriptor.globalConfigPage}" from="${descriptor}" optional="true"/>
</j:forEach>
<d:taglib uri="local">
<!-- display global config pages for the given descriptors -->
<d:tag name="globalConfig">
<j:getStatic var="descriptors" className="${className}" field="${field}" />
<j:forEach var="idx" begin="0" end="${size(descriptors)-1}">
<j:set var="descriptor" value="${descriptors[idx]}" />
<st:include page="${descriptor.globalConfigPage}" from="${descriptor}" optional="true"/>
</j:forEach>
</d:tag>
</d:taglib>
<local:globalConfig className="hudson.triggers.Triggers" field="TRIGGERS" />
<local:globalConfig className="hudson.tasks.BuildWrappers" field="WRAPPERS" />
<local:globalConfig className="hudson.tasks.BuildStep" field="BUILDERS" />
<local:globalConfig className="hudson.scm.SCMS" field="SCMS" />
<local:globalConfig className="hudson.tasks.BuildStep" field="PUBLISHERS" />
<local:globalConfig className="hudson.model.Jobs" field="PROPERTIES" />
<s:block>
<input type="submit" name="Submit" value="OK" />
......
......@@ -21,6 +21,16 @@
<st:include page="/hudson/tasks/LogRotator/config.jelly"/>
</f:optionalBlock>
<!-- job property configurations -->
<j:set var="instances" value="${it.properties}" />
<j:forEach var="d" items="${it.descriptor.propertyDescriptors}" varStatus="loop">
<f:section title="${d.displayName}">
<j:set var="descriptor" value="${d}" />
<j:set var="instance" value="${instances[d]}" />
<st:include from="${d}" page="${d.configPage}" optional="true"/>
</f:section>
</j:forEach>
<!-- additional entries from derived classes -->
<st:include page="configure-entries.jelly" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册