/*******************************************************************************
*
* Copyright (c) 2004-2010 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:
*
* Kohsuke Kawaguchi, Tom Huybrechts
*
*
*******************************************************************************/
package hudson.matrix;
import hudson.Util;
import hudson.security.ACL;
import hudson.util.DescribableList;
import hudson.model.AbstractBuild;
import hudson.model.Cause;
import hudson.model.CauseAction;
import hudson.model.DependencyGraph;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.JDK;
import hudson.model.Label;
import hudson.model.ParametersAction;
import hudson.model.Project;
import hudson.model.SCMedItem;
import hudson.model.Queue.NonBlockingTask;
import hudson.model.Cause.LegacyCodeCause;
import hudson.scm.SCM;
import hudson.tasks.BuildWrapper;
import hudson.tasks.Builder;
import hudson.tasks.LogRotator;
import hudson.tasks.Publisher;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* One configuration of {@link MatrixProject}.
*
* @author Kohsuke Kawaguchi
*/
public class MatrixConfiguration extends Project<MatrixConfiguration, MatrixRun> implements SCMedItem, NonBlockingTask {
/**
* The actual value combination.
*/
private transient /*final*/ Combination combination;
/**
* Hash value of {@link #combination}. Cached for efficiency.
*/
private transient String digestName;
public MatrixConfiguration(MatrixProject parent, Combination c) {
super(parent, c.toString());
setCombination(c);
updateTransientActions();
}
@Override
public void onLoad(ItemGroup<? extends Item> parent, String name) throws IOException {
// directory name is not a name for us --- it's taken from the combination name
super.onLoad(parent, combination.toString());
}
/**
* Used during loading to set the combination back.
*/
/*package*/ void setCombination(Combination c) {
this.combination = c;
this.digestName = c.digest().substring(0, 8);
}
/**
* Build numbers are always synchronized with the parent.
*
* <p> Computing this is bit tricky. Several considerations:
*
* <ol> <li>A new configuration build #N is started while the parent build
* #N is building, and when that happens we want to return N. <li>But the
* configuration build #N is done before the parent build #N finishes, and
* when that happens we want to return N+1 because that's going to be the
* next one. <li>Configuration builds might skip some numbers if the parent
* build is aborted before this configuration is built. <li>If nothing is
* building right now and the last build of the parent is #N, then we want
* to return N+1. </ol>
*/
@Override
public int getNextBuildNumber() {
AbstractBuild lb = getParent().getLastBuild();
if (lb == null) {
return 0;
}
int n = lb.getNumber();
if (!lb.isBuilding()) {
n++;
}
lb = getLastBuild();
if (lb != null) {
n = Math.max(n, lb.getNumber() + 1);
}
return n;
}
@Override
public int assignBuildNumber() throws IOException {
int nb = getNextBuildNumber();
MatrixRun r = getLastBuild();
if (r != null && r.getNumber() >= nb) // make sure we don't schedule the same build twice
{
throw new IllegalStateException("Build #" + nb + " is already completed");
}
return nb;
}
@Override
public String getDisplayName() {
return combination.toCompactString(getParent().getAxes());
}
@Override
public MatrixProject getParent() {
return (MatrixProject) super.getParent();
}
/**
* Get the actual combination of the axes values for this
* {@link MatrixConfiguration}
*/
public Combination getCombination() {
return combination;
}
/**
* Since {@link MatrixConfiguration} is always invoked from
* {@link MatrixRun} once and just once, there's no point in having a quiet
* period.
*/
@Override
public int getQuietPeriod() {
return 0;
}
/**
* Inherit the value from the parent.
*/
@Override
public int getScmCheckoutRetryCount() {
return getParent().getScmCheckoutRetryCount();
}
@Override
public boolean isConfigurable() {
return false;
}
@Override
public boolean isConcurrentBuild() {
return getParent().isConcurrentBuild();
}
@Override
protected Class<MatrixRun> getBuildClass() {
return MatrixRun.class;
}
@Override
protected MatrixRun newBuild() throws IOException {
// for every MatrixRun there should be a parent MatrixBuild
MatrixBuild lb = getParent().getLastBuild();
MatrixRun lastBuild = new MatrixRun(this, lb.getTimestamp());
lastBuild.number = lb.getNumber();
builds.put(lastBuild);
return lastBuild;
}
@Override
protected void buildDependencyGraph(DependencyGraph graph) {
}
@Override
public MatrixConfiguration asProject() {
return this;
}
@Override
public Label getAssignedLabel() {
// 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
public String getPronoun() {
return Messages.MatrixConfiguration_Pronoun();
}
@Override
public JDK getJDK() {
return Hudson.getInstance().getJDK(combination.get("jdk"));
}
//
// inherit build setting from the parent project
//
@Override
public List<Builder> getBuilders() {
return getParent().getBuilders();
}
@Override
public Map<Descriptor<Publisher>, Publisher> getPublishers() {
return getParent().getPublishers();
}
@Override
public DescribableList<Builder, Descriptor<Builder>> getBuildersList() {
return getParent().getBuildersList();
}
@Override
public DescribableList<Publisher, Descriptor<Publisher>> getPublishersList() {
return getParent().getPublishersList();
}
@Override
public Map<Descriptor<BuildWrapper>, BuildWrapper> getBuildWrappers() {
return getParent().getBuildWrappers();
}
/**
* @since 2.1.0
*/
@Override
public DescribableList<BuildWrapper, Descriptor<BuildWrapper>> getBuildWrappersList() {
return getParent().getBuildWrappersList();
}
@Override
public Publisher getPublisher(Descriptor<Publisher> descriptor) {
return getParent().getPublisher(descriptor);
}
@Override
public LogRotator getLogRotator() {
LogRotator lr = getParent().getLogRotator();
return new LinkedLogRotator(lr != null ? lr.getArtifactDaysToKeep() : -1,
lr != null ? lr.getArtifactNumToKeep() : -1);
}
@Override
public SCM getScm() {
return getParent().getScm();
}
/*package*/ String getDigestName() {
return digestName;
}
/**
* JDK cannot be set on {@link MatrixConfiguration} because it's controlled
* by {@link MatrixProject}.
*
* @deprecated Not supported.
*/
@Override
public void setJDK(JDK jdk) throws IOException {
throw new UnsupportedOperationException();
}
/**
* @deprecated Value is controlled by {@link MatrixProject}.
*/
@Override
public void setLogRotator(LogRotator logRotator) {
//TODO fins the reason
//throw new UnsupportedOperationException();
}
/**
* Returns true if this configuration is a configuration currently in use
* today (as opposed to the ones that are there only to keep the past
* record.)
*
* @see MatrixProject#getActiveConfigurations()
*/
public boolean isActiveConfiguration() {
return getParent().getActiveConfigurations().contains(this);
}
/**
* @since 2.1.0
*/
@Override
public ACL getACL() {
return getParent().getACL();
}
/**
* On Cygwin, path names cannot be longer than 256 chars. See
* http://cygwin.com/ml/cygwin/2005-04/msg00395.html and
* http://www.nabble.com/Windows-Filename-too-long-errors-t3161089.html for
* the background of this issue. Setting this flag to true would cause
* Hudson to use cryptic but short path name, giving more room for jobs to
* use longer path names.
*/
public static boolean useShortWorkspaceName = Boolean.getBoolean(MatrixConfiguration.class.getName() + ".useShortWorkspaceName");
/**
* @deprecated Use {@link #scheduleBuild(ParametersAction, Cause)}. Since
* 1.283
*/
public boolean scheduleBuild(ParametersAction parameters) {
return scheduleBuild(parameters, new LegacyCodeCause());
}
/**
*
* @param parameters Can be null.
*/
public boolean scheduleBuild(ParametersAction parameters, Cause c) {
return Hudson.getInstance().getQueue().schedule(this, getQuietPeriod(), parameters, new CauseAction(c)) != null;
}
}