| author | akozak | 2011-11-24 07:30:01 (EST) |
|---|---|---|
| committer | Winston Prakash | 2011-12-01 20:47:24 (EST) |
| commit | ec8573db009a424ca2aeaec73d15cd7832d0e126 (patch) (side-by-side diff) | |
| tree | ac6f634b17ea0a593464fcccdcb359ba0c27bd33 | |
| parent | 4f9b09cfb0761c4947a5f03039104e76c19485fd (diff) | |
| download | org.eclipse.hudson.core-ec8573db009a424ca2aeaec73d15cd7832d0e126.zip org.eclipse.hudson.core-ec8573db009a424ca2aeaec73d15cd7832d0e126.tar.gz org.eclipse.hudson.core-ec8573db009a424ca2aeaec73d15cd7832d0e126.tar.bz2 | |
Implement cascading functionality for triggers
Signed-off-by: Winston Prakash <winston.prakash@gmail.com>
9 files changed, 292 insertions, 36 deletions
diff --git a/hudson-core/src/main/java/hudson/model/AbstractProject.java b/hudson-core/src/main/java/hudson/model/AbstractProject.java index 388a5a9..76d3edd 100644 --- a/hudson-core/src/main/java/hudson/model/AbstractProject.java +++ b/hudson-core/src/main/java/hudson/model/AbstractProject.java @@ -39,6 +39,7 @@ import hudson.model.RunMap.Constructor; import hudson.model.labels.LabelAtom; import hudson.model.labels.LabelExpression; import hudson.util.CascadingUtil; +import hudson.util.DescribableListUtil; import org.eclipse.hudson.api.model.IProjectProperty; import org.eclipse.hudson.api.model.project.property.BooleanProjectProperty; import org.eclipse.hudson.api.model.project.property.IntegerProjectProperty; @@ -98,6 +99,7 @@ import org.apache.commons.lang3.math.NumberUtils; import org.eclipse.hudson.api.model.IAbstractProject; import org.eclipse.hudson.api.model.project.property.SCMProjectProperty; import org.eclipse.hudson.api.model.project.property.StringProjectProperty; +import org.eclipse.hudson.api.model.project.property.TriggerProjectProperty; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.stapler.ForwardToView; @@ -141,7 +143,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A * To allow derived classes to link {@link SCM} config to elsewhere, * access to this variable should always go through {@link #getScm()}. * @deprecated as of 2.2.0 - * don't use this field directly, logic was moved to {@link org.hudsonci.api.model.IProjectProperty}. + * don't use this field directly, logic was moved to {@link org.eclipse.hudson.api.model.IProjectProperty}. * Use getter/setter for accessing to this field. */ @Deprecated @@ -251,6 +253,11 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A /** * List of all {@link Trigger}s for this project. + * + * @deprecated as of 2.2.0 + * + * don't use this field directly, logic was moved to {@link org.eclipse.hudson.api.model.IProjectProperty}. + * Use getter/setter for accessing to this field. */ protected List<Trigger<?>> triggers = new Vector<Trigger<?>>(); @@ -304,7 +311,6 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A } @Override - @SuppressWarnings({"unchecked"}) public void onLoad(ItemGroup<? extends Item> parent, String name) throws IOException { super.onLoad(parent, name); @@ -317,20 +323,16 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A // boolean! Can't tell if xml file contained false.. if (enableRemoteTrigger) OldDataMonitor.report(this, "1.77"); - if(triggers==null) { - // it didn't exist in < 1.28 - triggers = new Vector<Trigger<?>>(); - OldDataMonitor.report(this, "1.28"); - } - for (Trigger t : triggers) + for (Trigger t : getTriggerDescribableList()) { t.start(this,false); + } if(scm==null) scm = new NullSCM(); // perhaps it was pointing to a plugin that no longer exists. if(transientActions==null) transientActions = new Vector<Action>(); // happens when loaded from disk - updateTransientActions(); + getTriggerDescribableList().setOwner(this); } @Override @@ -343,7 +345,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A convertQuietPeriodProperty(); convertScmCheckoutRetryCountProperty(); convertJDKProperty(); - convertScmProperty(); + convertTriggerProperties(); } void convertBlockBuildWhenUpstreamBuildingProperty() throws IOException { @@ -402,6 +404,12 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A } } + void convertTriggerProperties() { + if (triggers != null) { + setTriggers(triggers); + triggers = null; + } + } @Override protected void performDelete() throws IOException, InterruptedException { // prevent a new build while a delete operation is in progress @@ -1588,12 +1596,13 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A /** * Adds a new {@link Trigger} to this {@link Project} if not active yet. */ + @SuppressWarnings("unchecked") public void addTrigger(Trigger<?> trigger) throws IOException { - addToList(trigger,triggers); + CascadingUtil.getTriggerProjectProperty(this, trigger.getDescriptor().getJsonSafeClassName()).setValue(trigger); } public void removeTrigger(TriggerDescriptor trigger) throws IOException { - removeFromList(trigger,triggers); + CascadingUtil.getTriggerProjectProperty(this, trigger.getJsonSafeClassName()).setValue(null); } protected final synchronized <T extends Describable<T>> @@ -1601,7 +1610,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A for( int i=0; i<collection.size(); i++ ) { if(collection.get(i).getDescriptor()==item.getDescriptor()) { // replace - collection.set(i,item); + collection.set(i, item); save(); return; } @@ -1626,27 +1635,40 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A } public synchronized Map<TriggerDescriptor,Trigger> getTriggers() { - return (Map)Descriptor.toMap(triggers); + return (Map)Descriptor.toMap(getTriggerDescribableList()); } /** * @return list of {@link Trigger} elements. */ public List<Trigger<?>> getTriggersList() { - return triggers; + return getTriggerDescribableList().toList(); + } + + /** + * @return describable list of trigger elements. + */ + public DescribableList<Trigger<?>, TriggerDescriptor> getTriggerDescribableList() { + return DescribableListUtil.convertToDescribableList(Trigger.for_(this), this, TriggerProjectProperty.class); } /** * Gets the specific trigger, or null if the propert is not configured for this job. */ public <T extends Trigger> T getTrigger(Class<T> clazz) { - for (Trigger p : triggers) { + for (Trigger p : getTriggersList()) { if(clazz.isInstance(p)) return clazz.cast(p); } return null; } + public void setTriggers(List<Trigger<?>> triggerList) { + for (Trigger trigger : triggerList) { + CascadingUtil.getTriggerProjectProperty(this, trigger.getDescriptor().getJsonSafeClassName()).setValue(trigger); + } + } + // // // fingerprint related @@ -1898,11 +1920,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A setScm(SCMS.parseSCM(req,this)); - for (Trigger t : triggers) - t.stop(); - triggers = buildDescribable(req, Trigger.for_(this)); - for (Trigger t : triggers) - t.start(this,true); + buildTriggers(req, req.getSubmittedForm(), Trigger.for_(this)); } /** @@ -1928,6 +1946,14 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A return r; } + protected void buildTriggers(StaplerRequest req, JSONObject json, List<TriggerDescriptor> descriptors) + throws FormException { + for (TriggerDescriptor d : descriptors) { + String propertyName = d.getJsonSafeClassName(); + CascadingUtil.setChildrenTrigger(this, d, propertyName, req, json); + } + } + /** * Serves the workspace files. */ diff --git a/hudson-core/src/main/java/hudson/model/Items.java b/hudson-core/src/main/java/hudson/model/Items.java index 2f725db..fd10b57 100644 --- a/hudson-core/src/main/java/hudson/model/Items.java +++ b/hudson-core/src/main/java/hudson/model/Items.java @@ -41,6 +41,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.StringTokenizer; +import org.eclipse.hudson.api.model.project.property.TriggerProjectProperty; /** * Convenience methods related to {@link Item}. @@ -140,6 +141,7 @@ public class Items { //aliases for project properties. XSTREAM.alias("base-property", BaseProjectProperty.class); XSTREAM.alias("external-property", ExternalProjectProperty.class); + XSTREAM.alias("trigger-property", TriggerProjectProperty.class); XSTREAM.alias("integer-property", IntegerProjectProperty.class); XSTREAM.alias("boolean-property", BooleanProjectProperty.class); XSTREAM.alias("string-property", StringProjectProperty.class); diff --git a/hudson-core/src/main/java/hudson/triggers/Trigger.java b/hudson-core/src/main/java/hudson/triggers/Trigger.java index aff98a5..1f761da 100644 --- a/hudson-core/src/main/java/hudson/triggers/Trigger.java +++ b/hudson-core/src/main/java/hudson/triggers/Trigger.java @@ -1,6 +1,6 @@ /******************************************************************************* * - * Copyright (c) 2004-2009 Oracle Corporation. + * Copyright (c) 2004-2011 Oracle Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -9,7 +9,7 @@ * * Contributors: * -* Kohsuke Kawaguchi, Brian Westrich, Jean-Baptiste Quenot, Stephen Connolly, Tom Huybrechts +* Kohsuke Kawaguchi, Brian Westrich, Jean-Baptiste Quenot, Stephen Connolly, Tom Huybrechts, Nikita Levyankov * * *******************************************************************************/ @@ -305,4 +305,27 @@ public abstract class Trigger<J extends Item> implements Describable<Trigger<?>> } return r; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Trigger trigger = (Trigger) o; + + if (spec != null ? !spec.equals(trigger.spec) : trigger.spec != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return spec != null ? spec.hashCode() : 0; + } } diff --git a/hudson-core/src/main/java/hudson/util/CascadingUtil.java b/hudson-core/src/main/java/hudson/util/CascadingUtil.java index c41dc71..ad3734c 100644 --- a/hudson-core/src/main/java/hudson/util/CascadingUtil.java +++ b/hudson-core/src/main/java/hudson/util/CascadingUtil.java @@ -20,6 +20,8 @@ import hudson.model.Descriptor; import hudson.model.Hudson; import hudson.model.Item; import hudson.model.Job; +import hudson.triggers.Trigger; +import hudson.triggers.TriggerDescriptor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; @@ -39,6 +41,7 @@ import org.eclipse.hudson.api.model.project.property.LogRotatorProjectProperty; import org.eclipse.hudson.api.model.project.property.ResultProjectProperty; import org.eclipse.hudson.api.model.project.property.SCMProjectProperty; import org.eclipse.hudson.api.model.project.property.StringProjectProperty; +import org.eclipse.hudson.api.model.project.property.TriggerProjectProperty; import org.kohsuke.stapler.StaplerRequest; /** @@ -193,6 +196,19 @@ public class CascadingUtil { } /** + * Returns TriggerProjectProperty by specified key. If property doesn't exists, it will be initialized and added to + * current job. + * + * @param currentJob job that should be analyzed. + * @param key key. + * @return {@link org.eclipse.hudson.model.project.property.TriggerProjectProperty} instance. + * @throws NullPointerException if currentJob is null. + */ + public static TriggerProjectProperty getTriggerProjectProperty(Job currentJob, String key) { + return getProjectProperty(currentJob, key, TriggerProjectProperty.class); + } + + /** * Returns project property by specified key. * * @param currentJob job that should be analyzed. @@ -363,4 +379,41 @@ public class CascadingUtil { baseProperty.setValue(describable); } } -}
\ No newline at end of file + + /** + * Sets trigger for job and all its children if necessary. + * + * @param job parentJob + * @param descriptor trigger descriptor + * @param key trigger property key + * @param req stapler request + * @param json submited json + * @throws hudson.model.Descriptor.FormException if incorrect parameters + */ + @SuppressWarnings("unchecked") + public static void setChildrenTrigger(Job job, TriggerDescriptor descriptor, String key, StaplerRequest req, + JSONObject json) throws Descriptor.FormException { + TriggerProjectProperty<Trigger<?>> property = CascadingUtil.getTriggerProjectProperty(job, key); + if (property.getValue() != null) { + property.getValue().stop(); + } + Trigger trigger = null; + if (json.has(key)) { + trigger = descriptor.newInstance(req, json.getJSONObject(key)); + trigger.start(job, true); + } + property.setValue(trigger); + Set<String> cascadingChildrenNames = job.getCascadingChildrenNames(); + for (String childName : cascadingChildrenNames) { + Job childJob = (Job)Hudson.getInstance().getItem(childName); + if (StringUtils.equals(job.getName(), childJob.getCascadingProjectName())) { + TriggerProjectProperty childProperty = CascadingUtil.getTriggerProjectProperty(childJob, key); + if (!childProperty.isOverridden()) { + setChildrenTrigger(childJob, descriptor, key, req, json); + } else if (!childProperty.allowOverrideValue(trigger, childProperty.getValue())) { + childProperty.setOverridden(false); + } + } + } + } + } diff --git a/hudson-core/src/main/java/hudson/util/DescribableListUtil.java b/hudson-core/src/main/java/hudson/util/DescribableListUtil.java index 0923133..86b3781 100644 --- a/hudson-core/src/main/java/hudson/util/DescribableListUtil.java +++ b/hudson-core/src/main/java/hudson/util/DescribableListUtil.java @@ -29,7 +29,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; import net.sf.json.JSONObject; -import org.eclipse.hudson.api.model.IJob; +import org.eclipse.hudson.api.model.IProjectProperty; import org.eclipse.hudson.api.model.project.property.BaseProjectProperty; import org.eclipse.hudson.api.model.project.property.ExternalProjectProperty; import org.kohsuke.stapler.StaplerRequest; @@ -136,14 +136,27 @@ public final class DescribableListUtil { * @param owner new owner for properties. * @return {@link DescribableList} */ - @SuppressWarnings("unchecked") public static <T extends Describable<T>> DescribableList<T, Descriptor<T>> convertToDescribableList( List<Descriptor<T>> descriptors, Job owner) { + return convertToDescribableList(descriptors, owner, ExternalProjectProperty.class); + } + + /** + * Converts collection of propertyClass descriptors to {@link DescribableList} + * + * @param descriptors . + * @param owner new owner for properties. + * @param propertyClass projectProperty + * @return {@link DescribableList} + */ + @SuppressWarnings("unchecked") + public static <T extends Describable<T>, D extends Descriptor<T>, P extends IProjectProperty> DescribableList<T, D> + convertToDescribableList(List<D> descriptors, Job owner, Class<P> propertyClass) { List<T> describableList = new CopyOnWriteArrayList<T>(); - DescribableList<T, Descriptor<T>> result = new DescribableList<T, Descriptor<T>>(owner); + DescribableList<T, D> result = new DescribableList<T, D>(owner); for (Descriptor<T> descriptor : descriptors) { - ExternalProjectProperty<T> property = CascadingUtil.getExternalProjectProperty(owner, - descriptor.getJsonSafeClassName()); + IProjectProperty<T> property = + CascadingUtil.getProjectProperty(owner, descriptor.getJsonSafeClassName(), propertyClass); if (null != property.getValue()) { describableList.add(property.getValue()); } diff --git a/hudson-core/src/main/java/org/eclipse/hudson/api/model/project/property/TriggerProjectProperty.java b/hudson-core/src/main/java/org/eclipse/hudson/api/model/project/property/TriggerProjectProperty.java new file mode 100644 index 0000000..42774b9 --- a/dev/null +++ b/hudson-core/src/main/java/org/eclipse/hudson/api/model/project/property/TriggerProjectProperty.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * + * Copyright (c) 2011 Oracle Corporation. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * + * Nikita Levyankov + * + *******************************************************************************/ + +package org.eclipse.hudson.api.model.project.property; + +import org.eclipse.hudson.api.model.IJob; + +/** + * Property for triggers in case of we should use child project trigger + * instead of parent project if they are equals. + */ +public class TriggerProjectProperty<T> extends ExternalProjectProperty<T> { + public TriggerProjectProperty(IJob job) { + super(job); + } + + @Override + protected boolean updateOriginalValue(T value, T cascadingValue) { + T candidateValue = null == value ? getDefaultValue() : value; + if (allowOverrideValue(cascadingValue, candidateValue)) { + setOriginalValue(value, true); + } else { + setOriginalValue(value, false); + } + return true; + } +} diff --git a/hudson-core/src/main/resources/lib/hudson/project/config-trigger.jelly b/hudson-core/src/main/resources/lib/hudson/project/config-trigger.jelly index 41b67a0..b85885d 100644 --- a/hudson-core/src/main/resources/lib/hudson/project/config-trigger.jelly +++ b/hudson-core/src/main/resources/lib/hudson/project/config-trigger.jelly @@ -1,6 +1,6 @@ <!-- ************************************************************************** # -# Copyright (c) 2004-2009 Oracle Corporation. +# Copyright (c) 2004-2011 Oracle Corporation. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -9,7 +9,7 @@ # # Contributors: # -# Kohsuke Kawaguchi +# Kohsuke Kawaguchi, Nikita Levyankov # # #************************************************************************** --> @@ -23,11 +23,9 @@ <j:invokeStatic var="triggers" className="hudson.triggers.Trigger" method="for_"> <j:arg value="${it}" type="hudson.model.Item" /> </j:invokeStatic> - <f:descriptorList title="${%Build Triggers}" - descriptors="${triggers}" - instances="${it.triggers}"> + <t:cascadingDescriptorList title="${%Build Triggers}" descriptors="${triggers}"> <d:invokeBody /> <!-- pseudo-trigger to configure URL to trigger builds remotely. --> <st:include page="/hudson/model/BuildAuthorizationToken/config.jelly" /> - </f:descriptorList> -</j:jelly> + </t:cascadingDescriptorList> +</j:jelly>
\ No newline at end of file diff --git a/hudson-core/src/test/java/hudson/triggers/TriggerEqualHashCodeTest.java b/hudson-core/src/test/java/hudson/triggers/TriggerEqualHashCodeTest.java new file mode 100644 index 0000000..73a7652 --- a/dev/null +++ b/hudson-core/src/test/java/hudson/triggers/TriggerEqualHashCodeTest.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * + * Copyright (c) 2011 Oracle Corporation. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * + * Nikita Levyankov + * + *******************************************************************************/ +package hudson.triggers; + +import antlr.ANTLRException; +import java.util.Arrays; +import java.util.Collection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static org.junit.Assert.assertEquals; + +/** + * Test for equals and hashCode methods of {@link Trigger} objects + * </p> + * Date: 10/28/2011 + * + * @author + */ +@RunWith(Parameterized.class) +public class TriggerEqualHashCodeTest { + + private boolean expectedResult; + private Trigger trigger1; + private Trigger trigger2; + + public TriggerEqualHashCodeTest(boolean expectedResult, Trigger trigger1, Trigger trigger2) { + this.expectedResult = expectedResult; + this.trigger1 = trigger1; + this.trigger2 = trigger2; + } + + @Parameterized.Parameters + public static Collection generateData() throws ANTLRException { + return Arrays.asList(new Object[][] { + {true, new TimerTrigger(""), new TimerTrigger("")}, + {true, new TimerTrigger("* * * * *"), new TimerTrigger("* * * * *")}, + {false, new TimerTrigger("* * * * *"), new TimerTrigger("*/2 * * * *")}, + {true, new SCMTrigger(""), new SCMTrigger("")}, + {true, new SCMTrigger("* * * * *"), new SCMTrigger("* * * * *")}, + {false, new SCMTrigger("* * * * *"), new SCMTrigger("*/2 * * * *")} + }); + } + + @Test + public void testEquals() throws ANTLRException { + assertEquals(expectedResult, trigger1.equals(trigger2)); + } + + @Test + public void testHashCode() { + assertEquals(expectedResult, trigger1.hashCode() == trigger2.hashCode()); + } +} diff --git a/hudson-core/src/test/java/org/eclipse/hudson/api/model/project/property/ProjectPropertyTest.java b/hudson-core/src/test/java/org/eclipse/hudson/api/model/project/property/ProjectPropertyTest.java index 53ec7f0..c847d16 100644 --- a/hudson-core/src/test/java/org/eclipse/hudson/api/model/project/property/ProjectPropertyTest.java +++ b/hudson-core/src/test/java/org/eclipse/hudson/api/model/project/property/ProjectPropertyTest.java @@ -15,6 +15,7 @@ package org.eclipse.hudson.api.model.project.property; +import antlr.ANTLRException; import hudson.FilePath; import hudson.Launcher; import hudson.matrix.Axis; @@ -32,6 +33,8 @@ import hudson.scm.SCMRevisionState; import hudson.tasks.JavadocArchiver; import hudson.tasks.LogRotator; import hudson.tasks.Shell; +import hudson.triggers.TimerTrigger; +import hudson.triggers.Trigger; import hudson.util.DescribableList; import java.io.File; import java.io.IOException; @@ -161,6 +164,16 @@ public class ProjectPropertyTest { } } + @Test + public void testTriggerProjectPropertyConstructor() { + try { + new TriggerProjectProperty(null); + fail("Null should be handled by TriggerProjectProperty constructor."); + } catch (Exception e) { + assertEquals(BaseProjectProperty.INVALID_JOB_EXCEPTION, e.getMessage()); + } + } + /** * Checks prepareValue method. */ @@ -689,6 +702,28 @@ public class ProjectPropertyTest { } + /** + * Test 1updateOriginalValue method for TriggerProjectProperty. + * + * @throws ANTLRException if any + */ + @Test + public void testTriggerProjectPropertyUpdateOriginalValue() throws ANTLRException { + TriggerProjectProperty property = new TriggerProjectProperty(project); + Trigger originalTrigger = new TimerTrigger("* * * * *"); + Trigger cascadingTrigger = new TimerTrigger("* * * * *"); + property.updateOriginalValue(originalTrigger, cascadingTrigger); + //Property isn't overridden because of values equal. + assertFalse(property.isOverridden()); + //If trigger property value equals to cascading be sure that sets original value instead of cascading. + assertEquals(property.getOriginalValue(), originalTrigger); + + cascadingTrigger= new TimerTrigger("*/2 * * * *"); + property.updateOriginalValue(originalTrigger, cascadingTrigger); + assertTrue(property.isOverridden()); + assertEquals(property.getOriginalValue(), originalTrigger); + } + private class FakeSCM extends SCM { @Override public SCMRevisionState calcRevisionsFromBuild(AbstractBuild<?, ?> build, Launcher launcher, |

