Skip to content

Commit

Permalink
Merge pull request hudson#321 from 8nevil8/master
Browse files Browse the repository at this point in the history
Draft implementation of cascadable JobProperties. Improve highlighting for modified elements
  • Loading branch information
antkozak committed Nov 23, 2011
2 parents fd628f4 + e3a2db3 commit 73259a9
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 54 deletions.
1 change: 1 addition & 0 deletions hudson-core/src/main/java/hudson/model/Items.java
Expand Up @@ -162,6 +162,7 @@ public static XmlFile getConfigFile(Item item) {
XSTREAM.alias("axis-list-property", AxisListProjectProperty.class);
XSTREAM.alias("describable-list-property", DescribableListProjectProperty.class);
XSTREAM.aliasField("project-properties", Job.class, "jobProperties");
XSTREAM.aliasField("cascading-job-properties", Job.class, "cascadingJobProperties");
XSTREAM.alias("appointed-node-property", AppointedNode.class);
}
}
185 changes: 148 additions & 37 deletions hudson-core/src/main/java/hudson/model/Job.java
Expand Up @@ -87,6 +87,7 @@
import org.apache.commons.lang3.StringUtils;
import org.hudsonci.api.model.IJob;
import org.hudsonci.api.model.IProjectProperty;
import org.hudsonci.model.project.property.BaseProjectProperty;
import org.hudsonci.model.project.property.ExternalProjectProperty;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
Expand Down Expand Up @@ -196,6 +197,14 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
*/
private Set<String> cascadingChildrenNames = new CopyOnWriteArraySet<String>();

/**
* Set contains json-save names of cascadable {@link JobProperty} classes. Intended to be used for cascading support
* of external hudson plugins, that extends {@link JobProperty} class.
* See {@link #properties} field description
* @since 2.2.0
*/
private Set<String> cascadingJobProperties = new CopyOnWriteArraySet<String>();

/**
* Selected cascadingProject for this job.
*/
Expand Down Expand Up @@ -436,7 +445,7 @@ protected void buildProjectProperties() throws IOException {
property.setJob(this);
}
convertLogRotatorProperty();
convertJobProperty();
convertJobProperties();
}

void convertLogRotatorProperty() {
Expand All @@ -446,9 +455,36 @@ void convertLogRotatorProperty() {
}
}

void convertJobProperty() {
if (null != properties && null == getProperty(PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME)) {
setParameterDefinitionProperties(properties);
void convertJobProperties() {
if (null != properties && null == cascadingJobProperties) {
cascadingJobProperties = new CopyOnWriteArraySet<String>();
convertCascadingJobProperties(properties);
}
}

/**
* Adds cascading JobProperty.
*
* @param cascadingJobProperty BaseProjectProperty wrapper for JobProperty.
*/
private void addCascadingJobProperty(BaseProjectProperty projectProperty) {
if (null != projectProperty) {
cascadingJobProperties.add(projectProperty.getKey());
}
}

/**
* Adds cascading JobProperty.
*
* @param cascadingJobPropertyKey key of cascading JobProperty.
*/
private void removeCascadingJobProperty(String cascadingJobPropertyKey) {
if (null != cascadingJobPropertyKey) {
IProjectProperty projectProperty = CascadingUtil.getProjectProperty(this, cascadingJobPropertyKey);
if (null != projectProperty) {
projectProperty.resetValue();
}
cascadingJobProperties.remove(cascadingJobPropertyKey);
}
}

Expand Down Expand Up @@ -637,18 +673,71 @@ public boolean supportsLogRotator() {
return true;
}

public void setParameterDefinitionProperties(CopyOnWriteList<JobProperty<? super JobT>> properties) {
/**
* Method converts JobProperties to cascading values.
* <p/>
* If property is {@link AuthorizationMatrixProperty} - it will be skipped.
* If property is {@link ParametersDefinitionProperty} - it will be added to list of parameterDefinition properties.
* All the rest properties will be converted to {@link BaseProjectProperty} classes and added
* to cascadingJobProperties set.
*
* @param properties list of {@link JobProperty}
*/
private void convertCascadingJobProperties(CopyOnWriteList<JobProperty<? super JobT>> properties) {
CopyOnWriteList parameterDefinitionProperties = new CopyOnWriteList();
for (JobProperty property : properties) {
if (property instanceof AuthorizationMatrixProperty) {
continue;
}
if (property instanceof ParametersDefinitionProperty) {
parameterDefinitionProperties.add(property);
continue;
}
BaseProjectProperty projectProperty = CascadingUtil.getBaseProjectProperty(this,
property.getDescriptor().getJsonSafeClassName());
addCascadingJobProperty(projectProperty);
}
if (null == getProperty(PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME)) {
setParameterDefinitionProperties(parameterDefinitionProperties);
}
}

/**
* @return list of cascading {@link JobProperty} instances. Includes {@link ParametersDefinitionProperty} and
* children of {@link JobProperty} from external plugins.
*/
private CopyOnWriteList getCascadingJobProperties() {
CopyOnWriteList result = new CopyOnWriteList();
CopyOnWriteList<ParametersDefinitionProperty> definitionProperties = getParameterDefinitionProperties();
if (null != cascadingJobProperties && !cascadingJobProperties.isEmpty()) {
for (String key : cascadingJobProperties) {
IProjectProperty projectProperty = CascadingUtil.getProjectProperty(this, key);
Object value = projectProperty.getValue();
if (null != value) {
result.add(value);
}
}
}
if (null != definitionProperties && !definitionProperties.isEmpty()) {
result.addAll(definitionProperties.getView());
}
return result;
}

/**
* Sets list of {@link ParametersDefinitionProperty}. Supports cascading functionality.
*
* @param properties properties to set.
*/
private void setParameterDefinitionProperties(CopyOnWriteList<ParametersDefinitionProperty> properties) {
CascadingUtil.setParameterDefinitionProperties(this, PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME,
parameterDefinitionProperties);
properties);
}

public CopyOnWriteList<ParametersDefinitionProperty> getParameterDefinitionProperties() {
/**
* @return list of {@link ParametersDefinitionProperty}. Supports cascading functionality.
*/
private CopyOnWriteList<ParametersDefinitionProperty> getParameterDefinitionProperties() {
return CascadingUtil.getCopyOnWriteListProjectProperty(this, PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME)
.getValue();
}
Expand Down Expand Up @@ -686,16 +775,21 @@ public Collection<? extends Job> getAllJobs() {
* @since 1.188
*/
public void addProperty(JobProperty<? super JobT> jobProp) throws IOException {
((JobProperty) jobProp).setOwner(this);
if (((JobProperty) jobProp) instanceof ParametersDefinitionProperty) {
JobProperty jobProperty = (JobProperty) jobProp;
jobProperty.setOwner(this);
if (jobProperty instanceof AuthorizationMatrixProperty) {
properties.add(jobProp);
} else if (jobProperty instanceof ParametersDefinitionProperty) {
CopyOnWriteList list = CascadingUtil.getCopyOnWriteListProjectProperty(this,
PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME)
.getOriginalValue();
PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME).getOriginalValue();
if (null != list) {
list.add(jobProp);
}
} else {
properties.add(jobProp);
BaseProjectProperty projectProperty = CascadingUtil.getBaseProjectProperty(this,
jobProperty.getDescriptor().getJsonSafeClassName());
projectProperty.setValue(jobProperty);
addCascadingJobProperty(projectProperty);
}
save();
}
Expand All @@ -706,15 +800,17 @@ public void addProperty(JobProperty<? super JobT> jobProp) throws IOException {
* @since 1.279
*/
public void removeProperty(JobProperty<? super JobT> jobProp) throws IOException {
if (((JobProperty) jobProp) instanceof ParametersDefinitionProperty) {
JobProperty jobProperty = (JobProperty) jobProp;
if (jobProperty instanceof AuthorizationMatrixProperty) {
properties.remove(jobProp);
} else if (jobProperty instanceof ParametersDefinitionProperty) {
CopyOnWriteList list = CascadingUtil.getCopyOnWriteListProjectProperty(this,
PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME)
.getOriginalValue();
PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME).getOriginalValue();
if (null != list) {
list.remove(jobProp);
}
} else {
properties.remove(jobProp);
removeCascadingJobProperty(jobProperty.getDescriptor().getJsonSafeClassName());
}
save();
}
Expand All @@ -730,8 +826,10 @@ public <T extends JobProperty> T removeProperty(Class<T> clazz) throws IOExcepti
if (clazz.equals(ParametersDefinitionProperty.class)) {
sourceProperties = CascadingUtil.getCopyOnWriteListProjectProperty(this,
PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME).getOriginalValue();
} else {
} else if (clazz.equals(AuthorizationMatrixProperty.class)) {
sourceProperties = properties;
} else {
sourceProperties = getCascadingJobProperties();
}
if (null != sourceProperties) {
for (JobProperty<? super JobT> p : sourceProperties) {
Expand All @@ -754,29 +852,31 @@ public Map<JobPropertyDescriptor, JobProperty<? super JobT>> getProperties() {

/**
* List of all {@link JobProperty} exposed primarily for the remoting API.
* @since 1.282
* List contains cascadable {@link JobProperty} if any.
* @since 2.2.0
*/
@Exported(name = "property", inline = true)
public List<JobProperty<? super JobT>> getAllProperties() {
CopyOnWriteList<ParametersDefinitionProperty> definitionProperties = getParameterDefinitionProperties();
CopyOnWriteList cascadingJobProperties = getCascadingJobProperties();
List<JobProperty<? super JobT>> result = properties.getView();
if (null != definitionProperties) {
result = Collections.unmodifiableList(ListUtils.union(result, definitionProperties.getView()));
if (null != cascadingJobProperties && !cascadingJobProperties.isEmpty()) {
result = Collections.unmodifiableList(ListUtils.union(result, cascadingJobProperties.getView()));
}
return result;
}

/**
* Gets the specific property, or null if the propert is not configured for
* this job.
* Supports cascading properties
* @since 2.2.0
*/
public <T extends JobProperty> T getProperty(Class<T> clazz) {
CopyOnWriteList<JobProperty<? super JobT>> sourceProperties;
if (clazz.equals(ParametersDefinitionProperty.class)) {
sourceProperties = CascadingUtil.getCopyOnWriteListProjectProperty(this,
PARAMETERS_DEFINITION_JOB_PROPERTY_PROPERTY_NAME).getOriginalValue();
} else {
if (clazz.equals(AuthorizationMatrixProperty.class)) {
sourceProperties = properties;
} else {
sourceProperties = getCascadingJobProperties();
}
if (null != sourceProperties) {
for (JobProperty p : sourceProperties) {
Expand Down Expand Up @@ -1356,19 +1456,30 @@ protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOExceptio
properties.clear();
CopyOnWriteList parameterDefinitionProperties = new CopyOnWriteList();
int i = 0;
for (JobPropertyDescriptor d : JobPropertyDescriptor
.getPropertyDescriptors(Job.this.getClass())) {
String name = "jobProperty" + (i++);
JSONObject config = json.getJSONObject(name);
JobProperty prop = d.newInstance(req, config);

if (prop instanceof ParametersDefinitionProperty) {
prop.setOwner(this);
parameterDefinitionProperties.add(prop);
} else if (null != prop) {
prop.setOwner(this);
properties.add(prop);
for (JobPropertyDescriptor d : JobPropertyDescriptor.getPropertyDescriptors(Job.this.getClass())) {
if (!CascadingUtil.isCascadableJobProperty(d)) {
String name = "jobProperty" + i;
JSONObject config = json.getJSONObject(name);
JobProperty prop = d.newInstance(req, config);
if (null != prop) {
prop.setOwner(this);
if (prop instanceof AuthorizationMatrixProperty) {
properties.add(prop);
} else if (prop instanceof ParametersDefinitionProperty) {
parameterDefinitionProperties.add(prop);
}
}
} else {
BaseProjectProperty property = CascadingUtil.getBaseProjectProperty(this,
d.getJsonSafeClassName());
JobProperty prop = d.newInstance(req, json.getJSONObject(d.getJsonSafeClassName()));
if (null != prop) {
prop.setOwner(this);
}
property.setValue(prop);
addCascadingJobProperty(property);
}
i++;
}
setParameterDefinitionProperties(parameterDefinitionProperties);
LogRotator logRotator = null;
Expand Down
21 changes: 20 additions & 1 deletion hudson-core/src/main/java/hudson/util/CascadingUtil.java
Expand Up @@ -30,8 +30,10 @@
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.JobPropertyDescriptor;
import hudson.model.ParameterDefinition;
import hudson.model.ParametersDefinitionProperty;
import hudson.security.AuthorizationMatrixProperty;
import hudson.triggers.Trigger;
import hudson.triggers.TriggerDescriptor;
import java.io.IOException;
Expand Down Expand Up @@ -248,7 +250,7 @@ public static TriggerProjectProperty getTriggerProjectProperty(Job currentJob, S
*/
@SuppressWarnings("unchecked")
public static <T extends IProjectProperty> T getProjectProperty(Job currentJob, String key, Class<T> clazz) {
if(currentJob == null){
if (currentJob == null) {
throw new IllegalArgumentException("Job cannot be null");
}
IProjectProperty t = (IProjectProperty) currentJob.getProjectProperties().get(key);
Expand Down Expand Up @@ -518,4 +520,21 @@ public static void setParameterDefinitionProperties(Job job,
}
}
}

/**
* Checks whether JobProperty supports cascading.
* Method skips {@link AuthorizationMatrixProperty} and {@link ParametersDefinitionProperty} classes.
* {@link AuthorizationMatrixProperty} doesn't support cascading for now.
* As for {@link ParametersDefinitionProperty} single instance doesn't support cascading, so, classes are
* grouped into list of {@link ParametersDefinitionProperty} and whole list could be inherited or overridden.
* * @param d property descriptor.
* @return true - if JobProperty could be used for cascading, false - otherwise.
* @see #setParameterDefinitionProperties(hudson.model.Job, String, CopyOnWriteList)
* @see hudson.model.Job#getParameterDefinitionProperties()
*/
public static boolean isCascadableJobProperty(JobPropertyDescriptor d) {
return !(d instanceof AuthorizationMatrixProperty.DescriptorImpl
|| d instanceof ParametersDefinitionProperty.DescriptorImpl);
}
}
27 changes: 23 additions & 4 deletions hudson-core/src/main/resources/hudson/model/Job/configure.jelly
Expand Up @@ -72,10 +72,29 @@ THE SOFTWARE.
<j:set var="instances" value="${it.properties}" />
<j:forEach var="d" items="${h.getJobPropertyDescriptors(it.getClass())}" varStatus="loop">
<j:scope>
<j:set var="descriptor" value="${d}" />
<j:set var="instance" value="${instances[d]}" />

<f:rowSet name="jobProperty${loop.index}">
<j:set var="descriptor" value="${d}"/>
<j:set var="instance" value="${instances[d]}"/>
<j:set var="rowSetName" value="${'jobProperty'+loop.index}"/>
<j:set var="propertyOverridden" value="false"/>
<j:if test="${cu.isCascadableJobProperty(descriptor)}">
<j:set var="rowSetName" value="${descriptor.jsonSafeClassName}"/>
<j:set var="cascadingProperty" value="${cu.getBaseProjectProperty(it, descriptor.jsonSafeClassName)}"/>
<j:set var="instance" value="${cascadingProperty.value}"/>
<j:set var="propertyOverridden" value="${cascadingProperty.isOverridden()}"/>
</j:if>
<f:rowSet name="${rowSetName}" isPropertyOverridden='${propertyOverridden}'>
<j:if test="${propertyOverridden}">
<tr>
<td>
<div class="optional-reset">
<a href="#" class="reset-button"
resetURL="${jobUrl}/resetProjectProperty?propertyName=${cascadingProperty.key}">
<img src="${imagesURL}/16x16/undo.png"/>
</a>
</div>
</td>
</tr>
</j:if>
<st:include from="${d}" page="${d.configPage}" optional="true"/>
</f:rowSet>
</j:scope>
Expand Down
8 changes: 7 additions & 1 deletion hudson-core/src/main/resources/lib/form/rowSet.jelly
Expand Up @@ -34,15 +34,21 @@ THE SOFTWARE.
<st:attribute name="ref">
id of the thing that serves as the group head, if that's available separately
</st:attribute>
<st:attribute name="isPropertyOverridden">
if present and true - value is overridden
</st:attribute>
</st:documentation>

<j:if test="${attrs.isPropertyOverridden==null}">
<j:set target="${attrs}" property="isPropertyOverridden" value="false"/>
</j:if>
<j:choose>
<j:when test="${attrs.ref==null and attrs.name==null}">
<!-- noop -->
<d:invokeBody />
</j:when>
<j:otherwise>
<tr ref="${attrs.ref}" class="row-set-start" style="display:none" name="${attrs.name}" />
<tr ref="${attrs.ref}" class="row-set-start ${attrs.isPropertyOverridden? 'modified': 'original'}" style="display:none" name="${attrs.name}" />
<d:invokeBody />
<tr class="row-set-end" />
</j:otherwise>
Expand Down

0 comments on commit 73259a9

Please sign in to comment.