提交 92afdbe6 编写于 作者: K Kohsuke Kawaguchi

Merge remote-tracking branch 'origin/master'

......@@ -72,6 +72,9 @@ Upcoming changes</a>
<li class=rfe>
Don't run trigger for disabled/copied projects.
(<a href="https://github.com/jenkinsci/jenkins/pull/1617">PR 1617</a>)
<li class=rfe>
Jenkins now support self-restart and daemonization in FreeBSD
(<a href="https://github.com/jenkinsci/jenkins/pull/1770">PR 1770</a>)
</ul>
<h3><a name=v1.620>What's new in 1.620</a> (2015/07/12)</h3>
<ul class=image>
......
......@@ -471,9 +471,9 @@ THE SOFTWARE.
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
<dependency><!-- [JENKINS-4433] -->
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.txw2</groupId>
......@@ -513,7 +513,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>akuma</artifactId>
<version>1.9</version>
<version>1.10</version>
</dependency>
<dependency>
<groupId>org.kohsuke</groupId>
......
......@@ -213,48 +213,50 @@ public class NodeProvisioner {
// bring up.
int plannedCapacitySnapshot = 0;
List<PlannedNode> completedLaunches = new ArrayList<PlannedNode>();
for (Iterator<PlannedNode> itr = pendingLaunches.iterator(); itr.hasNext(); ) {
PlannedNode f = itr.next();
if (f.future.isDone()) {
completedLaunches.add(f);
itr.remove();
try {
Node node = f.future.get();
for (CloudProvisioningListener cl : CloudProvisioningListener.all()) {
cl.onComplete(f, node);
}
jenkins.addNode(node);
LOGGER.log(Level.INFO,
"{0} provisioning successfully completed. "
+ "We have now {1,number,integer} computer(s)",
new Object[]{f.displayName, jenkins.getComputers().length});
} catch (InterruptedException e) {
throw new AssertionError(e); // since we confirmed that the future is already done
} catch (ExecutionException e) {
LOGGER.log(Level.WARNING, "Provisioned slave " + f.displayName + " failed to launch",
e.getCause());
for (CloudProvisioningListener cl : CloudProvisioningListener.all()) {
cl.onFailure(f, e.getCause());
}
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Provisioned slave " + f.displayName + " failed to launch",
e);
for (CloudProvisioningListener cl : CloudProvisioningListener.all()) {
cl.onFailure(f, e);
}
} catch (Error e) {
// we are not supposed to try and recover from Errors
throw e;
} catch (Throwable e) {
LOGGER.log(Level.SEVERE, "Unexpected uncaught exception encountered while "
+ "processing provisioned slave " + f.displayName, e);
} finally {
itr.remove();
f.spent();
}
} else {
plannedCapacitySnapshot += f.numExecutors;
}
}
for (PlannedNode f : completedLaunches) {
try {
Node node = f.future.get();
for (CloudProvisioningListener cl : CloudProvisioningListener.all()) {
cl.onComplete(f, node);
}
jenkins.addNode(node);
LOGGER.log(Level.INFO,
"{0} provisioning successfully completed. We have now {1,number,integer} computer"
+ "(s)",
new Object[]{f.displayName, jenkins.getComputers().length});
} catch (InterruptedException e) {
throw new AssertionError(e); // since we confirmed that the future is already done
} catch (ExecutionException e) {
LOGGER.log(Level.WARNING, "Provisioned slave " + f.displayName + " failed to launch",
e.getCause());
for (CloudProvisioningListener cl : CloudProvisioningListener.all()) {
cl.onFailure(f, e.getCause());
}
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Provisioned slave " + f.displayName + " failed to launch", e);
for (CloudProvisioningListener cl : CloudProvisioningListener.all()) {
cl.onFailure(f, e);
}
}
f.spent();
}
float plannedCapacity = plannedCapacitySnapshot;
plannedCapacitiesEMA.update(plannedCapacity);
......
......@@ -94,17 +94,39 @@ public abstract class ParameterizedJobMixIn<JobT extends Job<JobT, RunT> & Param
}
/**
* Convenience method to schedule a build with the ability to wait for its result.
* Often used during functional tests ({@code JenkinsRule.assertBuildStatusSuccess}).
* Provides a standard implementation of an optional method of the same name in a {@link Job} type to schedule a build with the ability to wait for its result.
* That job method is often used during functional tests ({@code JenkinsRule.assertBuildStatusSuccess}).
* @param quietPeriod seconds to wait before starting (normally 0)
* @param actions various actions to associate with the scheduling, such as {@link ParametersAction} or {@link CauseAction}
* @return a handle by which you may wait for the build to complete (or just start); or null if the build was not actually scheduled for some reason
*/
public final @CheckForNull QueueTaskFuture<RunT> scheduleBuild2(int quietPeriod, Action... actions) {
return scheduleBuild2(quietPeriod, Arrays.asList(actions));
Queue.Item i = scheduleBuild2(quietPeriod, Arrays.asList(actions));
return i != null ? (QueueTaskFuture) i.getFuture() : null;
}
/**
* Convenience method to schedule a build.
* Useful for {@link Trigger} implementations, for example.
* If you need to wait for the build to start (or finish), use {@link Queue.Item#getFuture}.
* @param job a job which might be schedulable
* @param quietPeriod seconds to wait before starting; use {@code -1} to use the job’s default settings
* @param actions various actions to associate with the scheduling, such as {@link ParametersAction} or {@link CauseAction}
* @return a newly created, or reused, queue item if the job could be scheduled; null if it was refused for some reason (e.g., some {@link Queue.QueueDecisionHandler} rejected it), or if {@code job} is not a {@link ParameterizedJob} or it is not {@link Job#isBuildable})
* @since 1.621
*/
public static @CheckForNull Queue.Item scheduleBuild2(final Job<?,?> job, int quietPeriod, Action... actions) {
if (!(job instanceof ParameterizedJob)) {
return null;
}
return new ParameterizedJobMixIn() {
@Override protected Job asJob() {
return job;
}
}.scheduleBuild2(quietPeriod == -1 ? ((ParameterizedJob) job).getQuietPeriod() : quietPeriod, Arrays.asList(actions));
}
private @CheckForNull QueueTaskFuture<RunT> scheduleBuild2(int quietPeriod, List<Action> actions) {
@CheckForNull Queue.Item scheduleBuild2(int quietPeriod, List<Action> actions) {
if (!asJob().isBuildable())
return null;
......@@ -112,8 +134,7 @@ public abstract class ParameterizedJobMixIn<JobT extends Job<JobT, RunT> & Param
if (isParameterized() && Util.filter(queueActions, ParametersAction.class).isEmpty()) {
queueActions.add(new ParametersAction(getDefaultParametersValues()));
}
Queue.Item i = Jenkins.getInstance().getQueue().schedule2(asJob(), quietPeriod, queueActions).getItem();
return i != null ? (QueueTaskFuture) i.getFuture() : null;
return Jenkins.getInstance().getQueue().schedule2(asJob(), quietPeriod, queueActions).getItem();
}
private List<ParameterValue> getDefaultParametersValues() {
......@@ -244,11 +265,31 @@ public abstract class ParameterizedJobMixIn<JobT extends Job<JobT, RunT> & Param
* Suggested implementation of {@link ParameterizedJob#getBuildNowText}.
*/
public final String getBuildNowText() {
// TODO is it worthwhile to define a replacement for AbstractProject.BUILD_NOW_TEXT?
// TODO JENKINS-26147 use replacement for AbstractProject.BUILD_NOW_TEXT
// TODO move these messages (& translations) to this package
return isParameterized() ? hudson.model.Messages.AbstractProject_build_with_parameters() : hudson.model.Messages.AbstractProject_BuildNow();
}
/**
* Checks for the existence of a specific trigger on a job.
* @param <T> a trigger type
* @param job a job
* @param clazz the type of the trigger
* @return a configured trigger of the requested type, or null if there is none such, or {@code job} is not a {@link ParameterizedJob}
* @since 1.621
*/
public static @CheckForNull <T extends Trigger<?>> T getTrigger(Job<?,?> job, Class<T> clazz) {
if (!(job instanceof ParameterizedJob)) {
return null;
}
for (Trigger<?> t : ((ParameterizedJob) job).getTriggers().values()) {
if (clazz.isInstance(t)) {
return clazz.cast(t);
}
}
return null;
}
/**
* Marker for job using this mixin.
*/
......@@ -265,6 +306,7 @@ public abstract class ParameterizedJobMixIn<JobT extends Job<JobT, RunT> & Param
* Gets currently configured triggers.
* You may use {@code <p:config-trigger/>} to configure them.
* @return a map from trigger kind to instance
* @see #getTrigger
*/
Map<TriggerDescriptor,Trigger<?>> getTriggers();
......
......@@ -32,6 +32,7 @@ import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.AutoCompletionCandidates;
import hudson.model.Cause;
import hudson.model.CauseAction;
import hudson.model.DependencyGraph;
import hudson.model.Item;
import hudson.model.ItemGroup;
......@@ -237,11 +238,7 @@ public final class ReverseBuildTrigger extends Trigger<Job> implements Dependenc
continue;
}
String name = ModelHyperlinkNote.encodeTo(trigger.job) + " #" + trigger.job.getNextBuildNumber();
if (new ParameterizedJobMixIn() {
@Override protected Job asJob() {
return trigger.job;
}
}.scheduleBuild(new Cause.UpstreamCause(r))) {
if (ParameterizedJobMixIn.scheduleBuild2(trigger.job, -1, new CauseAction(new Cause.UpstreamCause(r))) != null) {
listener.getLogger().println(hudson.tasks.Messages.BuildTrigger_Triggering(name));
} else {
listener.getLogger().println(hudson.tasks.Messages.BuildTrigger_InQueue(name));
......@@ -257,10 +254,9 @@ public final class ReverseBuildTrigger extends Trigger<Job> implements Dependenc
if (jenkins == null) {
return;
}
for (ParameterizedJobMixIn.ParameterizedJob p : jenkins.getAllItems(ParameterizedJobMixIn.ParameterizedJob.class)) {
Trigger<?> _t = p.getTriggers().get(jenkins.getDescriptorByType(DescriptorImpl.class));
if (_t instanceof ReverseBuildTrigger) {
ReverseBuildTrigger t = (ReverseBuildTrigger) _t;
for (Job<?,?> p : jenkins.getAllItems(Job.class)) {
ReverseBuildTrigger t = ParameterizedJobMixIn.getTrigger(p, ReverseBuildTrigger.class);
if (t != null) {
String revised = Items.computeRelativeNamesAfterRenaming(oldFullName, newFullName, t.upstreamProjects, p.getParent());
if (!revised.equals(t.upstreamProjects)) {
t.upstreamProjects = revised;
......
......@@ -213,16 +213,16 @@ THE SOFTWARE.
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4jVersion}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${slf4jVersion}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency><!-- [JENKINS-4433] -->
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.samba.jcifs</groupId>
<artifactId>jcifs</artifactId>
......@@ -663,6 +663,7 @@ THE SOFTWARE.
<bannedDependencies>
<excludes>
<exclude>org.sonatype.sisu:sisu-guice</exclude>
<exclude>log4j:log4j</exclude>
</excludes>
</bannedDependencies>
</rules>
......
package lib.form;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import hudson.model.RootAction;
import junit.framework.Assert;
import net.sf.json.JSONObject;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
import org.kohsuke.stapler.StaplerRequest;
/**
* @author Kohsuke Kawaguchi
*/
public class RowSetTest {
@Rule
public JenkinsRule j = new JenkinsRule();
@Test
public void json() throws Exception {
HtmlPage p = j.createWebClient().goTo("test/test1");
j.submit(p.getFormByName("config"));
}
@TestExtension
public static class Subject implements RootAction {
public void doSubmitTest1(StaplerRequest req) throws Exception {
JSONObject json = req.getSubmittedForm();
json.remove("crumb");
System.out.println(json);
JSONObject expected = JSONObject.fromObject(
"{'a':'aaa','b':'bbb','c':{'c1':'ccc1','c2':'ccc2'},'d':{'d1':'d1','d2':'d2'}}");
Assert.assertEquals(json,expected);
}
@Override
public String getIconFileName() {
return null;
}
@Override
public String getDisplayName() {
return null;
}
@Override
public String getUrlName() {
return "test";
}
}
}
<!--
The MIT License
Copyright (c) 2011, CloudBees, 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">
<l:layout title="Test JSON structure submitted from rowSet">
<l:main-panel>
<f:form method="post" name="config" action="submitTest1">
<f:entry title="a" field="a">
<f:textbox value="aaa" />
</f:entry>
<f:rowSet><!-- rowSet without name will be just inlined -->
<f:entry title="b" field="b">
<f:textbox value="bbb" />
</f:entry>
</f:rowSet>
<f:rowSet name="c"><!-- creates an intermediate node -->
<f:entry title="c1" field="c1">
<f:textbox value="ccc1" />
</f:entry>
<f:entry title="c2" field="c2">
<f:textbox value="ccc2" />
</f:entry>
</f:rowSet>
<f:entry title="d" field="d">
<f:checkbox id="d-checkbox" checked="true" />
</f:entry>
<f:rowSet ref="d-checkbox"><!-- this puts -->
<f:entry title="d1" field="d1">
<f:textbox value="d1"/>
</f:entry>
<f:entry title="d2" field="d2">
<f:textbox value="d2"/>
</f:entry>
</f:rowSet>
<f:bottomButtonBar>
<f:submit value="${%Save}"/>
</f:bottomButtonBar>
</f:form>
</l:main-panel>
</l:layout>
</j:jelly>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册