提交 18ed9667 编写于 作者: K kohsuke

Merged revisions 33770,33808,33830 via svnmerge from

https://www.dev.java.net/svn/hudson/branches/extensible-axis

........
  r33770 | kohsuke | 2010-08-12 17:48:02 -0700 (Thu, 12 Aug 2010) | 1 line
  
  the work in progress thus far. still has some test failures.
........
  r33808 | kohsuke | 2010-08-13 17:17:44 -0700 (Fri, 13 Aug 2010) | 1 line
  
  JDKAxis always normalize its values in the order JDKs are defined.
........
  r33830 | kohsuke | 2010-08-13 23:52:59 -0700 (Fri, 13 Aug 2010) | 1 line
  
  using the latest
........


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@33834 71c3de6d-444a-0410-be80-ed276b4c234a
上级 79bad4d7
......@@ -378,7 +378,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler-jelly</artifactId>
<version>1.148</version>
<version>1.150</version>
<exclusions>
<exclusion>
<groupId>commons-jelly</groupId>
......
......@@ -23,12 +23,18 @@
*/
package hudson.matrix;
import hudson.DescriptorExtensionList;
import hudson.ExtensionPoint;
import hudson.Util;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.util.QuotedStringTokenizer;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
......@@ -45,15 +51,21 @@ import java.util.Arrays;
*
* @author Kohsuke Kawaguchi
*/
public final class Axis implements Comparable<Axis>, Iterable<String> {
public class Axis extends AbstractDescribableImpl<Axis> implements Comparable<Axis>, Iterable<String>, ExtensionPoint {
/**
* Name of this axis.
* Used as a variable name.
*
* @deprecated as of 1.373
* Use {@link #getName()}
*/
public final String name;
/**
* Possible values for this axis.
*
* @deprecated as of 1.373
* Use {@link #getValues()}
*/
public final List<String> values;
......@@ -74,29 +86,32 @@ public final class Axis implements Comparable<Axis>, Iterable<String> {
* Axis with empty values need to be removed later.
*/
@DataBoundConstructor
public Axis(String name, String value) {
public Axis(String name, String valueString) {
this.name = name;
this.values = new ArrayList<String>(Arrays.asList(Util.tokenize(value)));
this.values = new ArrayList<String>(Arrays.asList(Util.tokenize(valueString)));
}
/**
* Returns ture if this axis is a system-reserved axis
* that has special treatment.
* Returns true if this axis is a system-reserved axis
* that <strike>has</strike> used to have af special treatment.
*
* @deprecated as of 1.373
* System vs user difference are generalized into extension point.
*/
public boolean isSystem() {
return name.equals("jdk") || name.equals("label");
return false;
}
public Iterator<String> iterator() {
return values.iterator();
return getValues().iterator();
}
public int size() {
return values.size();
return getValues().size();
}
public String value(int index) {
return values.get(index);
return getValues().get(index);
}
/**
......@@ -114,6 +129,26 @@ public final class Axis implements Comparable<Axis>, Iterable<String> {
return this.name.compareTo(that.name);
}
/**
* Name of this axis.
* Used as a variable name.
*/
public String getName() {
return name;
}
/**
* Possible values for this axis.
*/
public List<String> getValues() {
return Collections.unmodifiableList(values);
}
@Override
public AxisDescriptor getDescriptor() {
return (AxisDescriptor)super.getDescriptor();
}
@Override
public String toString() {
return new StringBuilder().append(name).append("={").append(Util.join(values,",")).append('}').toString();
......@@ -154,4 +189,25 @@ public final class Axis implements Comparable<Axis>, Iterable<String> {
return null;
return new Axis(name,values);
}
/**
* Previously we used to persist {@link Axis}, but now those are divided into subtypes.
* So upon deserialization, resolve to the proper type.
*/
public Object readResolve() {
if (getClass()!=Axis.class) return this;
if (getName().equals("jdk"))
return new JDKAxis(getValues());
if (getName().equals("label"))
return new LabelAxis(getName(),getValues());
return new TextAxis(getName(),getValues());
}
/**
* Returns all the registered {@link AxisDescriptor}s.
*/
public static DescriptorExtensionList<Axis,AxisDescriptor> all() {
return Hudson.getInstance().getDescriptorList(Axis.class);
}
}
/*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* 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 hudson.matrix;
import hudson.Util;
import hudson.model.Descriptor;
import hudson.model.Failure;
import hudson.model.Hudson;
import hudson.util.FormValidation;
import org.kohsuke.stapler.QueryParameter;
/**
* {@link Descriptor} for {@link Axis}
*
* @author Kohsuke Kawaguchi
*/
public abstract class AxisDescriptor extends Descriptor<Axis> {
protected AxisDescriptor(Class<? extends Axis> clazz) {
super(clazz);
}
protected AxisDescriptor() {
}
/**
* Return false if the user shouldn't be able to create thie axis from the UI.
*/
public boolean isInstantiable() {
return true;
}
/**
* Makes sure that the given name is good as a axis name.
*/
public FormValidation doCheckName(@QueryParameter String value) {
if(Util.fixEmpty(value)==null)
return FormValidation.ok();
try {
Hudson.checkGoodName(value);
return FormValidation.ok();
} catch (Failure e) {
return FormValidation.error(e.getMessage());
}
}
}
......@@ -25,12 +25,16 @@ package hudson.matrix;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import hudson.Util;
import hudson.model.Label;
import hudson.util.RobustCollectionConverter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Arrays;
import java.util.Set;
/**
* List of {@link Axis}.
......@@ -41,7 +45,7 @@ public class AxisList extends ArrayList<Axis> {
public AxisList() {
}
public AxisList(Collection<Axis> c) {
public AxisList(Collection<? extends Axis> c) {
super(c);
}
......@@ -57,6 +61,13 @@ public class AxisList extends ArrayList<Axis> {
return null;
}
/**
* Creates a subset of the list that only contains the type assignable to the specified type.
*/
public AxisList subList(Class<? extends Axis> subType) {
return new AxisList(Util.filter(this,subType));
}
@Override
public boolean add(Axis axis) {
return axis!=null && super.add(axis);
......
......@@ -25,6 +25,7 @@ package hudson.matrix;
import hudson.Util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
......@@ -53,7 +54,7 @@ public final class Combination extends TreeMap<String,String> implements Compara
public Combination(AxisList axisList, List<String> values) {
for(int i=0; i<axisList.size(); i++)
super.put(axisList.get(i).name,values.get(i));
super.put(axisList.get(i).getName(),values.get(i));
}
public Combination(AxisList axisList,String... values) {
......@@ -65,6 +66,10 @@ public final class Combination extends TreeMap<String,String> implements Compara
super.put(e.getKey(),e.getValue());
}
public String get(Axis a) {
return get(a.getName());
}
/**
* Obtains the continuous unique index number of this {@link Combination}
* in the given {@link AxisList}.
......@@ -73,7 +78,7 @@ public final class Combination extends TreeMap<String,String> implements Compara
int r = 0;
for (Axis a : axis) {
r *= a.size();
r += a.indexOf(get(a.name));
r += a.indexOf(get(a));
}
return r;
}
......@@ -92,7 +97,7 @@ public final class Combination extends TreeMap<String,String> implements Compara
private long toModuloIndex(AxisList axis) {
long r = 0;
for (Axis a : axis) {
r += a.indexOf(get(a.name));
r += a.indexOf(get(a));
r *= 31;
}
return r;
......@@ -150,12 +155,22 @@ public final class Combination extends TreeMap<String,String> implements Compara
StringBuilder buf = new StringBuilder();
for (Axis a : subset) {
if(buf.length()>0) buf.append(',');
buf.append(a.name).append('=').append(get(a.name));
buf.append(a.getName()).append('=').append(get(a));
}
if(buf.length()==0) buf.append("default"); // special case to avoid 0-length name.
return buf.toString();
}
/**
* Gets the values that correspond to the specified axes, in their order.
*/
public List<String> values(Collection<? extends Axis> axes) {
List<String> r = new ArrayList<String>(axes.size());
for (Axis a : axes)
r.add(get(a));
return r;
}
/**
* Converts to the ID string representation:
* <tt>axisName=value,axisName=value,...</tt>
......@@ -218,12 +233,12 @@ public final class Combination extends TreeMap<String,String> implements Compara
Map<String,Axis> axisByValue = new HashMap<String,Axis>();
for (Axis a : axes) {
for (String v : a.values) {
for (String v : a.getValues()) {
Axis old = axisByValue.put(v,a);
if(old!=null) {
// these two axes have colliding values
nonUniqueAxes.add(old.name);
nonUniqueAxes.add(a.name);
nonUniqueAxes.add(old.getName());
nonUniqueAxes.add(a.getName());
}
}
}
......
/*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* 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 hudson.matrix;
import hudson.Extension;
/**
* {@link AxisDescriptor} for manually entered default axis.
*
* @author Kohsuke Kawaguchi
*/
@Extension
public class DefaultAxisDescriptor extends AxisDescriptor {
public DefaultAxisDescriptor() {
super(Axis.class);
}
@Override
public String getDisplayName() {
return "Axis";
}
@Override
public boolean isInstantiable() {
return false;
}
}
/*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* 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 hudson.matrix;
import hudson.Extension;
import hudson.model.Hudson;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.Arrays;
import java.util.List;
/**
* {@link Axis} that selects available JDKs.
*
* @author Kohsuke Kawaguchi
*/
public class JDKAxis extends Axis {
/**
* JDK axis was used to be stored as a plain "Axis" with the name "jdk",
* so it cannot be configured by any other name.
*/
public JDKAxis(List<String> values) {
super("jdk", values);
}
@DataBoundConstructor
public JDKAxis(String[] values) {
super("jdk", Arrays.asList(values));
}
@Override
public boolean isSystem() {
return true;
}
@Extension
public static class DescriptorImpl extends AxisDescriptor {
@Override
public String getDisplayName() {
return "JDK";
}
/**
* If there's no JDK configured, there's no point in this axis.
*/
@Override
public boolean isInstantiable() {
return !Hudson.getInstance().getJDKs().isEmpty();
}
}
}
/*
* The MIT License
*
* Copyright (c) 2010, InfraDNA, Inc.
*
* 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 hudson.matrix;
import hudson.Extension;
import hudson.model.Hudson;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.List;
/**
* {@link Axis} that selects label expressions.
*
* @author Kohsuke Kawaguchi
*/
public class LabelAxis extends Axis {
@DataBoundConstructor
public LabelAxis(String name, List<String> values) {
super(name, values);
}
@Override
public boolean isSystem() {
return true;
}
@Extension
public static class DescriptorImpl extends AxisDescriptor {
@Override
public String getDisplayName() {
return "Slaves";
}
/**
* If there's no distributed build set up, it's pointless to provide this axis.
*/
@Override
public boolean isInstantiable() {
Hudson h = Hudson.getInstance();
return !h.getNodes().isEmpty() || !h.clouds.isEmpty();
}
}
}
......@@ -23,6 +23,7 @@
*/
package hudson.matrix;
import hudson.Util;
import hudson.util.DescribableList;
import hudson.model.AbstractBuild;
import hudson.model.Cause;
......@@ -192,7 +193,9 @@ public class MatrixConfiguration extends Project<MatrixConfiguration,MatrixRun>
@Override
public Label getAssignedLabel() {
return Hudson.getInstance().getLabel(combination.get("label"));
// combine all the label axes by &&.
String expr = Util.join(combination.values(getParent().getAxes().subList(LabelAxis.class)), "&&");
return Hudson.getInstance().getLabel(Util.fixEmpty(expr));
}
@Override
......
......@@ -24,13 +24,14 @@
package hudson.matrix;
import hudson.CopyOnWrite;
import hudson.XmlFile;
import hudson.Util;
import hudson.Extension;
import hudson.Util;
import hudson.XmlFile;
import hudson.model.AbstractProject;
import hudson.model.BuildableItemWithBuildWrappers;
import hudson.model.DependencyGraph;
import hudson.model.Descriptor;
import hudson.model.Failure;
import hudson.model.Descriptor.FormException;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.ItemGroup;
......@@ -38,14 +39,12 @@ import hudson.model.Items;
import hudson.model.JDK;
import hudson.model.Job;
import hudson.model.Label;
import hudson.model.Queue.FlyweightTask;
import hudson.model.ResourceController;
import hudson.model.Result;
import hudson.model.SCMedItem;
import hudson.model.Saveable;
import hudson.model.TopLevelItem;
import hudson.model.ResourceController;
import hudson.model.BuildableItemWithBuildWrappers;
import hudson.model.Queue.FlyweightTask;
import hudson.model.Descriptor.FormException;
import hudson.tasks.BuildStep;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildWrapper;
......@@ -55,8 +54,13 @@ import hudson.tasks.Publisher;
import hudson.triggers.Trigger;
import hudson.util.CopyOnWriteMap;
import hudson.util.DescribableList;
import hudson.util.FormValidation;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.TokenList;
import javax.servlet.ServletException;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
......@@ -65,25 +69,14 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.TokenList;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.QueryParameter;
/**
* {@link Job} that allows you to run multiple different configurations
* from a single setting.
......@@ -92,9 +85,7 @@ import org.kohsuke.stapler.QueryParameter;
*/
public class MatrixProject extends AbstractProject<MatrixProject,MatrixBuild> implements TopLevelItem, SCMedItem, ItemGroup<MatrixConfiguration>, Saveable, FlyweightTask, BuildableItemWithBuildWrappers {
/**
* Other configuration axes.
*
* This also includes special axis "label" and "jdk" if they are configured.
* Configuration axes.
*/
private volatile AxisList axes = new AxisList();
......@@ -243,6 +234,9 @@ public class MatrixProject extends AbstractProject<MatrixProject,MatrixBuild> im
/**
* Gets the subset of {@link AxisList} that are not system axes.
*
* @deprecated as of 1.373
* System vs user difference are generalized into extension point.
*/
public List<Axis> getUserAxes() {
List<Axis> r = new ArrayList<Axis>();
......@@ -474,12 +468,9 @@ public class MatrixProject extends AbstractProject<MatrixProject,MatrixBuild> im
* @return never null
*/
public Set<Label> getLabels() {
Axis a = axes.find("label");
if(a==null) return Collections.emptySet();
Set<Label> r = new HashSet<Label>();
for (String l : a)
r.add(Hudson.getInstance().getLabel(l));
for (Combination c : axes.subList(LabelAxis.class).list())
r.add(Hudson.getInstance().getLabel(Util.join(c.values(),"&&")));
return r;
}
......@@ -549,23 +540,8 @@ public class MatrixProject extends AbstractProject<MatrixProject,MatrixBuild> im
protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException {
super.submit(req, rsp);
AxisList newAxes = new AxisList();
HashSet<String> axisNames = new HashSet<String>();
JSONObject json = req.getSubmittedForm();
// parse user axes
if(req.getParameter("hasAxes")!=null) {
newAxes.addAll(req.bindParametersToList(Axis.class,"axis."));
// get rid of empty values
for (Iterator<Axis> itr = newAxes.iterator(); itr.hasNext();) {
Axis a = itr.next();
if(a.values.isEmpty()) { itr.remove(); continue; }
checkAxisName(a.name);
if (axisNames.contains(a.name))
throw new FormException(Messages.MatrixProject_DuplicateAxisName(),"axis.name");
axisNames.add(a.name);
}
}
if(req.getParameter("hasCombinationFilter")!=null) {
this.combinationFilter = Util.nullify(req.getParameter("combinationFilter"));
} else {
......@@ -581,12 +557,10 @@ public class MatrixProject extends AbstractProject<MatrixProject,MatrixBuild> im
}
// parse system axes
newAxes.add(Axis.parsePrefixed(req,"jdk"));
if(req.getParameter("multipleNodes")!=null)
newAxes.add(Axis.parsePrefixed(req,"label"));
this.axes = newAxes;
JSONObject json = req.getSubmittedForm();
DescribableList<Axis,AxisDescriptor> newAxes = new DescribableList<Axis,AxisDescriptor>(this);
newAxes.rebuildHetero(req, json, Axis.all(),"axis");
checkAxisNames(newAxes);
this.axes = new AxisList(newAxes.toList());
runSequentially = json.has("runSequentially");
......@@ -597,6 +571,19 @@ public class MatrixProject extends AbstractProject<MatrixProject,MatrixBuild> im
rebuildConfigurations();
}
/**
* Verifies that Axis names are valid and unique.
*/
private void checkAxisNames(Iterable<Axis> newAxes) throws FormException {
HashSet<String> axisNames = new HashSet<String>();
for (Axis a : newAxes) {
a.getDescriptor().doCheckName(a.getName());
if (axisNames.contains(a.getName()))
throw new FormException(Messages.MatrixProject_DuplicateAxisName(),"axis.name");
axisNames.add(a.getName());
}
}
/**
* Also delete all the workspaces of the configuration, too.
*/
......@@ -608,30 +595,6 @@ public class MatrixProject extends AbstractProject<MatrixProject,MatrixBuild> im
return rsp;
}
/**
* Makes sure that the given name is good as a axis name.
*/
public FormValidation doCheckAxisName(@QueryParameter String value) {
checkPermission(CONFIGURE);
if(Util.fixEmpty(value)==null)
return FormValidation.ok();
try {
checkAxisName(value);
return FormValidation.ok();
} catch (Failure e) {
return FormValidation.error(e.getMessage());
}
}
/**
* Makes sure that the given name is good as a axis name.
* TODO: maybe be even more restrictive since these are used as shell variables
*/
private static void checkAxisName(String name) throws Failure {
Hudson.checkGoodName(name);
}
public DescriptorImpl getDescriptor() {
return DESCRIPTOR;
......@@ -648,6 +611,18 @@ public class MatrixProject extends AbstractProject<MatrixProject,MatrixBuild> im
public MatrixProject newInstance(String name) {
return new MatrixProject(name);
}
/**
* All {@link AxisDescriptor}s that contribute to the UI.
*/
public List<AxisDescriptor> getAxisDescriptors() {
List<AxisDescriptor> r = new ArrayList<AxisDescriptor>();
for (AxisDescriptor d : Axis.all()) {
if (d.isInstantiable())
r.add(d);
}
return r;
}
}
private static final Logger LOGGER = Logger.getLogger(MatrixProject.class.getName());
......
package hudson.matrix;
import hudson.Extension;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.List;
/**
* User-defined plain text axis.
*
* @author Kohsuke Kawaguchi
*/
public class TextAxis extends Axis {
public TextAxis(String name, List<String> values) {
super(name, values);
}
public TextAxis(String name, String... values) {
super(name, values);
}
@DataBoundConstructor
public TextAxis(String name, String valueString) {
super(name, valueString);
}
@Extension
public static class DescriptorImpl extends AxisDescriptor {
@Override
public String getDisplayName() {
return "User-defined Axis";
}
}
}
<!--
The MIT License
Copyright (c) 2010, InfraDNA, Inc.
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.
-->
<!-- JDK config -->
<?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" xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
<f:entry>
<j:forEach var="jdk" items="${app.JDKs}">
<f:checkbox name="values" value="${jdk.name}" checked="${instance.values.contains(jdk.name)}" />
<label class="attach-previous">${jdk.name}</label>
<st:nbsp/>
</j:forEach>
</f:entry>
</j:jelly>
<!--
The MIT License
Copyright (c) 2010, InfraDNA, Inc.
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.
-->
<!-- JDK config -->
<?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" xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
<f:entry title="${%Name}" field="name">
<f:textbox default="label" />
</f:entry>
<f:entry title="${%Node/Label}" field="labels">
<div class="yahooTree labelAxis-tree" style="border: 1px solid gray; height: 10em; overflow:auto;" values="${instance.valueString}" />
<l:yui module="treeview" />
<link rel="stylesheet" href="${rootURL}/scripts/yui/treeview/assets/skins/sam/treeview.css" type="text/css" />
<script>
hudsonRules["DIV.labelAxis-tree"] = function(e) {
var tree = new YAHOO.widget.TreeView(e);
var labels = new YAHOO.widget.TextNode("${%Labels}", tree.getRoot(), false);
var machines = new YAHOO.widget.TextNode("${%Individual nodes}", tree.getRoot(), false);
var values = (e.getAttribute("values") || "").split(" ");
function has(v) {
return values.include(v) ? " checked " : "";
}
<j:forEach var="l" items="${app.labelAtoms}">
new YAHOO.widget.TextNode('&lt;input type="checkbox" name="values" value="${l.name}" '+has("${l.name}")+'&gt;<label class="attach-previous">${h.jsStringEscape(l.name)} (${h.jsStringEscape(l.description)})</label>', ${l.isSelfLabel()?'machines':'labels'}, false);
</j:forEach>
tree.draw();
<!--
force the rendering of HTML, so that input fields are there
even when the form is submitted without this tree expanded.
-->
tree.expandAll();
tree.collapseAll();
};
</script>
</f:entry>
</j:jelly>
......@@ -41,17 +41,12 @@ THE SOFTWARE.
</p:config-trigger>
<f:section title="${%Configuration Matrix}">
<!-- JDK config -->
<j:set var="jdks" value="${app.JDKs}" />
<j:if test="${!empty(jdks)}">
<f:entry title="JDK" help="/help/matrix/jdk.html">
<j:forEach var="jdk" items="${jdks}">
<f:checkbox name="jdk.${jdk.name}" id="jdk.${jdk.name}" checked="${it.JDKs.contains(jdk)}" />
<label for="jdk.${jdk.name}">${jdk.name}</label>
<st:nbsp/>
</j:forEach>
</f:entry>
</j:if>
<f:block>
<f:hetero-list name="axis" hasHeader="true"
descriptors="${descriptor.axisDescriptors}"
items="${it.axes}"
addCaption="${%Add axis}"/>
</f:block>
<!-- master slave -->
<j:if test="${!empty(app.slaves)}">
......@@ -86,29 +81,6 @@ THE SOFTWARE.
</f:optionalBlock>
</j:if>
<j:set var="userAxes" value="${it.userAxes}" />
<f:optionalBlock name="hasAxes" title="${%Axes}" checked="${!empty(userAxes)}"
help="/help/matrix/axes.html">
<f:entry>
<f:repeatable var="axis" items="${userAxes}" noAddButton="true" minimum="1">
<table>
<f:entry title="${%Name}">
<f:textbox name="axis.name" value="${axis.name}" checkUrl="'checkAxisName?value='+encodeURIComponent(this.value)"/>
</f:entry>
<f:entry title="${%Values}" help="/help/matrix/axis-value.html">
<f:expandableTextbox name="axis.value" value="${axis.valueString}" />
</f:entry>
<f:entry>
<div align="right">
<input type="button" value="${%Add more axis}" class="repeatable-add show-if-last" />
<input type="button" value="${%Delete}" class="repeatable-delete show-if-not-only" style="margin-left: 1em;" />
</div>
</f:entry>
</table>
</f:repeatable>
</f:entry>
</f:optionalBlock>
<f:optionalBlock field="runSequentially" title="${%Run each configuration sequentially}"/>
<f:optionalBlock name="hasCombinationFilter" title="${%Combination Filter}" checked="${!empty(it.combinationFilter)}"
......
<!--
The MIT License
Copyright (c) 2010, InfraDNA, Inc.
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.
-->
<?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" xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
<f:entry title="${%Name}" field="name">
<f:textbox />
</f:entry>
<f:entry title="${%Values}" field="valueString">
<f:expandableTextbox />
</f:entry>
</j:jelly>
......@@ -45,6 +45,7 @@ THE SOFTWARE.
<f:slave-mode name="mode" node="${it}" />
<!-- TODO: should be packaged as a tag -->
<f:dropdownList name="slave.launcher" title="${%Launch method}"
help="${descriptor.getHelpFile('launcher')}">
<j:forEach var="d" items="${h.getComputerLauncherDescriptors()}">
......
......@@ -32,8 +32,8 @@ THE SOFTWARE.
<st:attribute name="name" use="required">
form name that receives an array for all the items in the heterogeneous list.
</st:attribute>
<st:attribute name="items" use="required">
existing items to be displayed
<st:attribute name="items" use="required" type="java.util.Collection">
existing items to be displayed. Something iterable, such as array or collection.
</st:attribute>
<st:attribute name="descriptors" use="required">
all types that the user can add.
......
......@@ -23,11 +23,6 @@
*/
package hudson.matrix
import hudson.matrix.Axis
import hudson.matrix.AxisList
import hudson.matrix.MatrixBuild
import hudson.matrix.MatrixProject
import hudson.matrix.MatrixRun
import hudson.model.Cause
import hudson.model.Result
import hudson.tasks.Ant
......@@ -49,6 +44,8 @@ import hudson.util.OneShotEvent
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import hudson.model.JDK
import hudson.model.Slave
/**
*
......@@ -131,8 +128,8 @@ public class MatrixProjectTest extends HudsonTestCase {
// set up 2x2 matrix
AxisList axes = new AxisList();
axes.add(new Axis("db","mysql","oracle"));
axes.add(new Axis("direction","north","south"));
axes.add(new TextAxis("db","mysql","oracle"));
axes.add(new TextAxis("direction","north","south"));
p.setAxes(axes);
return p;
......@@ -178,7 +175,7 @@ public class MatrixProjectTest extends HudsonTestCase {
// 5*5*5*5*5 matrix
def p = createMatrixProject();
p.axes = new AxisList(
['a','b','c','d','e'].collect { name -> new Axis(name, (1..4)*.toString() ) }
['a','b','c','d','e'].collect { name -> new TextAxis(name, (1..4)*.toString() ) }
);
assertRectangleTable(p)
}
......@@ -188,18 +185,63 @@ public class MatrixProjectTest extends HudsonTestCase {
// 2*3*4*5*6 matrix
def p = createMatrixProject();
p.axes = new AxisList(
(2..6).collect { n -> new Axis("axis${n}", (1..n)*.toString() ) }
(2..6).collect { n -> new TextAxis("axis${n}", (1..n)*.toString() ) }
);
assertRectangleTable(p)
}
/**
* Makes sure that the configuration correctly roundtrips.
*/
public void testConfigRoundtrip() {
hudson.getJDKs().addAll([
new JDK("jdk1.7","somewhere"),
new JDK("jdk1.6","here"),
new JDK("jdk1.5","there")]);
List<Slave> slaves = (0..2).collect { createSlave() }
def p = createMatrixProject();
p.axes.add(new JDKAxis(["jdk1.6","jdk1.5"]));
p.axes.add(new LabelAxis("label1",[slaves[0].nodeName, slaves[1].nodeName]));
p.axes.add(new LabelAxis("label2",[slaves[2].nodeName])); // make sure single value handling works OK
def o = new AxisList(p.axes);
configRoundtrip(p);
def n = p.axes;
assertEquals(o.size(),n.size());
(0 ..< (o.size())).each { i ->
def oi = o[i];
def ni = n[i];
assertSame(oi.class,ni.class);
assertEquals(oi.name,ni.name);
assertEquals(oi.values,ni.values);
}
}
public void testLabelAxes() {
def p = createMatrixProject();
List<Slave> slaves = (0..<4).collect { createSlave() }
p.axes.add(new LabelAxis("label1",[slaves[0].nodeName, slaves[1].nodeName]));
p.axes.add(new LabelAxis("label2",[slaves[2].nodeName, slaves[3].nodeName]));
System.out.println(p.labels);
assertEquals(4,p.labels.size());
assertTrue(p.labels.contains(hudson.getLabel("slave0&&slave2")));
assertTrue(p.labels.contains(hudson.getLabel("slave1&&slave2")));
assertTrue(p.labels.contains(hudson.getLabel("slave0&&slave3")));
assertTrue(p.labels.contains(hudson.getLabel("slave1&&slave3")));
}
/**
* Quiettng down Hudson causes a dead lock if the parent is running but children is in the queue
*/
@Bug(4873)
void testQuietDownDeadlock() {
def p = createMatrixProject();
p.axes = new AxisList(new Axis("foo","1","2"));
p.axes = new AxisList(new TextAxis("foo","1","2"));
p.runSequentially = true; // so that we can put the 2nd one in the queue
OneShotEvent firstStarted = new OneShotEvent();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册