提交 de53fea5 编写于 作者: O Oliver Gondža

Merge branch 'master' into override-whyKeepLog

......@@ -55,12 +55,52 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=bug>
FutureImpl does not cancel its start future.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25514">issue 25514</a>)
<li class=bug>
Flyweight tasks were under some conditions actually being run on heavyweight executors.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-10944">issue 10944</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-24519">issue 24519</a>)
<li class=bug>
Folder loading broken when child item loading throws exception.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-22811">issue 22811</a>)
<li class=bug>
Amend <code>JAVA_HOME</code> check to work with JDK 9.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25601">issue 25601</a>)
<li class=bug>
CLI <code>list-jobs</code> command should display raw name, not display name, where they differ.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25338">issue 25338</a>)
<li class=rfe>
Show queue item parameters in tool tip.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-22311">issue 22311</a>)
<li class=bug>
Better support functional tests from Gradle-based plugins.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-26331">issue 26331</a>)
</ul>
</div><!--=TRUNK-END=-->
<!-- these changes are controlled by the release process. DO NOT MODIFY -->
<div id="rc" style="display:none;"><!--=BEGIN=-->
<h3><a name=v1.597>What's new in 1.597</a> <!--=DATE=--></h3>
<ul class=image>
<li class='major rfe'>
<b><tt>JENKINS_HOME</tt> layout change</b>: builds are now keyed by build numbers and not timestamps.
See <a href="https://wiki.jenkins-ci.org/display/JENKINS/JENKINS-24380+Migration">Wiki</a> for details
and downgrade.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-24380">issue 24380</a>)
<li class=bug>
Do not throw exception on <code>/signup</code> when not possible.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-11172">issue 11172</a>)
<li class=bug>
Tool installer which downloads and unpacks archives should not fail the build if the tool already exists and the server returns an error code.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-26196">issue 26196</a>)
<li class=bug>
Fingerprint compaction aggravated lazy-loading performance issues.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-19392">issue 19392</a>)
<li class=bug>
Possible unreleased workspace lock if SCM polling fails during setup.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-23568">issue 23568</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-26201">issue 26201</a>)
<li class=bug>
Misleading description of the 'workspace' permission.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-20148">issue 20148</a>)
......@@ -71,11 +111,8 @@ Upcoming changes</a>
Add range check for H(X-Y) syntax.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25897">issue 25897</a>)
</ul>
</div><!--=TRUNK-END=-->
<!-- these changes are controlled by the release process. DO NOT MODIFY -->
<div id="rc" style="display:none;"><!--=BEGIN=-->
<h3><a name=v1.596>What's new in 1.596</a> <!--=DATE=--></h3>
</div><!--=END=-->
<h3><a name=v1.596>What's new in 1.596</a> (2015/01/04)</h3>
<ul class=image>
<li class=bug>
Build page was broken in Hungarian localization while building.
......@@ -84,7 +121,6 @@ Upcoming changes</a>
Allow breaking label and node lists.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25989">issue 25989</a>)
</ul>
</div><!--=END=-->
<h3><a name=v1.595>What's new in 1.595</a> (2014/12/21)</h3>
<ul class=image>
<li class=bug>
......@@ -126,7 +162,7 @@ Upcoming changes</a>
<ul class=image>
<li class=bug>
Always use forward slashes in path separators during in ZIP archives generated by Directory Browser
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-22514">issue 22514</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-22514">issue 22514</a>)
</ul>
<h3><a name=v1.590>What's new in 1.590</a> (2014/11/16)</h3>
<ul class=image>
......@@ -138,7 +174,7 @@ Upcoming changes</a>
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25400">issue 25400</a>)
<li class=bug>
Fixed various real or potential resource leaks discovered by <a href="https://scan.coverity.com/projects/94/">Coverity Scan</code>
(<a href="https://github.com/jenkinsci/jenkins/pull/1434">pull request 1434</a>)
(<a href="https://github.com/jenkinsci/jenkins/pull/1434">pull request 1434</a>)
<li class=rfe>
API changes: Expose <code>AbstractProject.AbstractProjectDescriptor#validateLabelExpression</code> for plugins.
(<a href="https://github.com/jenkinsci/jenkins/pull/1456">pull request 1456</a>)
......@@ -147,7 +183,7 @@ Upcoming changes</a>
(<a href="https://github.com/jenkinsci/jenkins/pull/1458">pull request 1458</a>)
<li class=rfe>
API method to get non-null <code>Jenkins</code> instance with internal validation
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-23339">issue 23339</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-23339">issue 23339</a>)
</ul>
<h3><a name=v1.589>What's new in 1.589</a> (2014/11/09)</h3>
<ul class=image>
......@@ -193,7 +229,7 @@ Upcoming changes</a>
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25019">issue 25019</a>)
<li class=bug>
Existing <code>FileParameter</code>s should be handled as different values to avoid merging of queued builds
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-19017">issue 19017</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-19017">issue 19017</a>)
</ul>
<h3><a name=v1.585>What's new in 1.585</a> (2014/10/19)</h3>
<ul class=image>
......@@ -213,22 +249,22 @@ Upcoming changes</a>
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25065">issue 25065</a>)
<li class=bug>
Greater-than characters are not escaped in HTML outputs like e-mails
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-16184">issue 16184</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-16184">issue 16184</a>)
<li class=bug>
Thread starvation from <code>OldDataMonitor</code>.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-24763">issue 24763</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-24763">issue 24763</a>)
<li class=bug>
Integer overflow in quiet-down timeout calculation
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-24914">issue 24914</a>)
Integer overflow in quiet-down timeout calculation
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-24914">issue 24914</a>)
<li class=bug>
Don't put session IDs in URLs even when cookies are disabled.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-22358">issue 22358</a>)
<li class=rfe>
Show keep build log reason in tool tips
(<a href="https://github.com/jenkinsci/jenkins/pull/1422">pull request 1422</a>)
(<a href="https://github.com/jenkinsci/jenkins/pull/1422">pull request 1422</a>)
<li class=bug>
Do not disable projects, which do not support such operation (like Matrix configurations)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-24340">issue 24340</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-24340">issue 24340</a>)
<li class=rfe>
Improved the scalability of SSH slaves plugin caused by global lock in <tt>SecureRandom</tt>
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-20108">issue 20108</a>)
......@@ -482,7 +518,7 @@ Upcoming changes</a>
<ul class=image>
<li class="rfe">
UI redesign: Use Helvetica as default font
(<a href="https://github.com/jenkinsci/jenkins/pull/1315">pull 1315</a>,
(<a href="https://github.com/jenkinsci/jenkins/pull/1315">pull 1315</a>,
<a href="https://issues.jenkins-ci.org/browse/JENKINS-23840">issue 23840</a>)
<li class="bug">
Synchronization issue during tool installation
......@@ -625,7 +661,7 @@ Upcoming changes</a>
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-20499">issue 20499</a>)
<li class="bug">
Fixed <code>NullPointerException</code> on plugin installations when invalid update center is set
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-20031">issue 20031</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-20031">issue 20031</a>)
<li class="bug">
Use DISABLED_ANIME icon while building a disabled project
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-8358">issue 8358</a>)
......@@ -638,22 +674,22 @@ Upcoming changes</a>
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-18116">issue 18116</a>)
<li class="bug">
Properly close input streams in <code>FileParameterValue</code>
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-22693">issue 22693</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-22693">issue 22693</a>)
<li class="bug">
Incorrect failure age in the JUnit test results
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-18626">issue 18626</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-18626">issue 18626</a>)
<li class="bug">
Fixed deletion links for JVM Crash error logs
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-22617">issue 22617</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-22617">issue 22617</a>)
<li class="rfe">
Distinguish &quot;nodes for label offline&quot; from &quot;no nodes for label&quot;
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-17114">issue 17114</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-17114">issue 17114</a>)
<li class="rfe">
Add causes to queue item tool tip
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-19250">issue 19250</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-19250">issue 19250</a>)
<li class="rfe">
RPM: added JENKINS_HTTPS_KEYSTORE and JENKINS_HTTPS_KEYSTORE_PASSWORD options to Jenkins sysconfig file
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-11673">issue 11673</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-11673">issue 11673</a>)
<li class="bug">
RPM: Do not install jenkins.repo file
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-22690">issue 22690</a>)
......@@ -1003,7 +1039,7 @@ Upcoming changes</a>
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-19559">issue 19559</a>)
<li class=bug>
Fix autocompletion for items in folders.
(<a href="https://github.com/jenkinsci/jenkins/pull/1124">pull request 1124</a>)
(<a href="https://github.com/jenkinsci/jenkins/pull/1124">pull request 1124</a>)
</ul>
<h3><a name=v1.552>What's new in 1.552</a> (2014/02/24)</h3>
<ul class=image>
......@@ -1310,7 +1346,7 @@ Upcoming changes</a>
Added CLI commands that manipulate views
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-19996">issue 19996</a>)
<li class=rfe>
Improved the /cli help screen.
Improved the /cli help screen.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-20023">issue 20023</a>)
<li class=bug>
Polling-triggered jobs get scheduled en-mass on start-up if slaves aren't online yet.
......@@ -1380,7 +1416,7 @@ Upcoming changes</a>
<a href="https://issues.jenkins-ci.org/browse/JENKINS-18879">issue 18879</a>,
<a href="https://issues.jenkins-ci.org/browse/JENKINS-19619">issue 19619</a>)
<li class='major rfe'>
Upgrade bundled iplugin versions: ssh-slaves to 1.4, ssh-credentials to 1.5.3 and
Upgrade bundled iplugin versions: ssh-slaves to 1.4, ssh-credentials to 1.5.3 and
credentials to 1.8.3
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-19945">issue 19945</a>)
<li class='major rfe'>
......@@ -1970,7 +2006,7 @@ Upcoming changes</a>
”My Views" links leads to 404 Not Found.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-17317">issue 17317</a>)
<li class=bug>
Quoting Issue with JDK Installer with Windows Installer.
Quoting Issue with JDK Installer with Windows Installer.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-5408">issue 5408</a>)
<li class=bug>
Restored compatibility in <code>ArtifactArchiver</code> signature; broken in 1.509 and could affect plugins.
......@@ -2046,7 +2082,7 @@ Upcoming changes</a>
ChangeLog should produce some output even if some (plugin) annotator fails
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-17084">issue 17084</a>)
<li class=bug>
View name should not allow "..".
View name should not allow "..".
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-16608">issue 16608</a>)
</ul>
<h3><a name=v1.508>What's new in 1.508</a> (2013/03/25)</h3>
......@@ -2078,7 +2114,7 @@ Upcoming changes</a>
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-16089">issue 16089</a>)
<li class=bug>
Wrong build result in post build steps after failed pre build step in maven projects.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-17177">issue 17177</a>)
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-17177">issue 17177</a>)
</ul>
<h3><a name=v1.506>What's new in 1.506</a> (2013/03/17)</h3>
<ul class=image>
......@@ -2267,7 +2303,7 @@ Upcoming changes</a>
<li class=bug>
Fixed NullPointerException when copying from existing Maven job
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-16499">issue 16499</a>)
</ul>
<h3><a name=v1.500>What's new in 1.500</a> (2013/01/26)</h3>
<ul class=image>
......
......@@ -3,14 +3,15 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>pom</artifactId>
<groupId>org.jenkins-ci.main</groupId>
<version>1.597-SNAPSHOT</version>
<artifactId>pom</artifactId>
<version>1.598-SNAPSHOT</version>
</parent>
<artifactId>cli</artifactId>
<name>Jenkins CLI</name>
<name>Jenkins cli</name>
<description>Command line interface for Jenkins</description>
<dependencies>
<dependency>
......@@ -93,5 +94,4 @@
</plugin>
</plugins>
</build>
</project>
package hudson.cli;
import static org.junit.Assert.assertEquals;
import hudson.remoting.FastPipedInputStream;
import hudson.remoting.FastPipedOutputStream;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
......@@ -13,7 +14,8 @@ import java.io.IOException;
/**
* @author Kohsuke Kawaguchi
*/
public class ConnectionTest extends Assert {
public class ConnectionTest {
Throwable e;
private Connection c1;
private Connection c2;
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
......@@ -22,23 +23,19 @@ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>1.597-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
<version>1.598-SNAPSHOT</version>
</parent>
<artifactId>jenkins-core</artifactId>
<name>Jenkins core</name>
<description>
Contains the core Jenkins code and view files to render HTML.
</description>
<description>Jenkins core code and view files to render HTML.</description>
<properties>
<staplerFork>true</staplerFork>
......@@ -96,7 +93,6 @@ THE SOFTWARE.
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<classifier>no_aop</classifier>
</dependency>
<dependency> <!-- for compatibility only; all new code should use JNR -->
......@@ -862,7 +858,7 @@ THE SOFTWARE.
</build>
</profile>
<profile>
<!-- run FindBugs for better error detection. run as "mvn -Pfindbugs install site" -->
<!-- Run FindBugs for better error detection. Run as "mvn -Pfindbugs install site". -->
<id>findbugs</id>
<reporting>
<plugins>
......@@ -880,9 +876,7 @@ THE SOFTWARE.
</reporting>
</profile>
<profile>
<!--
Obtain code coverage report. This is done by running Unit tests on our own and suppressing surefire.
-->
<!-- Obtain code coverage report. This is done by running Unit tests on our own and suppressing surefire. -->
<id>cobertura</id>
<build>
<plugins>
......
......@@ -734,32 +734,34 @@ public final class FilePath implements Serializable {
* that supports upgrade and downgrade. Specifically,
*
* <ul>
* <li>If the target directory doesn't exist {@linkplain #mkdirs() it'll be created}.
* <li>The timestamp of the .tgz file is left in the installation directory upon extraction.
* <li>If the timestamp left in the directory doesn't match with the timestamp of the current archive file,
* <li>If the target directory doesn't exist {@linkplain #mkdirs() it will be created}.
* <li>The timestamp of the archive is left in the installation directory upon extraction.
* <li>If the timestamp left in the directory does not match the timestamp of the current archive file,
* the directory contents will be discarded and the archive file will be re-extracted.
* <li>If the connection is refused but the target directory already exists, it is left alone.
* </ul>
*
* @param archive
* The resource that represents the tgz/zip file. This URL must support the "Last-Modified" header.
* (Most common usage is to get this from {@link ClassLoader#getResource(String)})
* The resource that represents the tgz/zip file. This URL must support the {@code Last-Modified} header.
* (For example, you could use {@link ClassLoader#getResource}.)
* @param listener
* If non-null, a message will be printed to this listener once this method decides to
* extract an archive.
* extract an archive, or if there is any issue.
* @param message a message to be printed in case extraction will proceed.
* @return
* true if the archive was extracted. false if the extraction was skipped because the target directory
* was considered up to date.
* @since 1.299
*/
public boolean installIfNecessaryFrom(URL archive, TaskListener listener, String message) throws IOException, InterruptedException {
public boolean installIfNecessaryFrom(@Nonnull URL archive, @CheckForNull TaskListener listener, @Nonnull String message) throws IOException, InterruptedException {
try {
FilePath timestamp = this.child(".timestamp");
long lastModified = timestamp.lastModified();
URLConnection con;
try {
con = ProxyConfiguration.open(archive);
if (timestamp.exists()) {
con.setIfModifiedSince(timestamp.lastModified());
if (lastModified != 0) {
con.setIfModifiedSince(lastModified);
}
con.connect();
} catch (IOException x) {
......@@ -774,15 +776,21 @@ public final class FilePath implements Serializable {
}
}
if (con instanceof HttpURLConnection
&& ((HttpURLConnection)con).getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
return false;
if (lastModified != 0 && con instanceof HttpURLConnection) {
HttpURLConnection httpCon = (HttpURLConnection) con;
int responseCode = httpCon.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
return false;
} else if (responseCode != HttpURLConnection.HTTP_OK) {
listener.getLogger().println("Skipping installation of " + archive + " to " + remote + " due to server error: " + responseCode + " " + httpCon.getResponseMessage());
return false;
}
}
long sourceTimestamp = con.getLastModified();
if(this.exists()) {
if(timestamp.exists() && sourceTimestamp ==timestamp.lastModified())
if (lastModified != 0 && sourceTimestamp == lastModified)
return false; // already up to date
this.deleteContents();
} else {
......
......@@ -82,7 +82,7 @@ public class ListJobsCommand extends CLICommand {
// Print all jobs.
for (TopLevelItem item : jobs) {
stdout.println(item.getDisplayName());
stdout.println(item.getName());
}
return 0;
......
......@@ -751,8 +751,13 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
for (JobProperty<? super P> p : Util.fixNull(properties))
ta.addAll(p.getJobActions((P)this));
for (TransientProjectActionFactory tpaf : TransientProjectActionFactory.all())
ta.addAll(Util.fixNull(tpaf.createFor(this))); // be defensive against null
for (TransientProjectActionFactory tpaf : TransientProjectActionFactory.all()) {
try {
ta.addAll(Util.fixNull(tpaf.createFor(this))); // be defensive against null
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Could not load actions from " + tpaf + " for " + this, e);
}
}
return ta;
}
......
......@@ -30,6 +30,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.ModelObjectWithContextMenu;
import jenkins.model.TransientActionFactory;
import org.kohsuke.stapler.StaplerRequest;
......@@ -91,7 +94,11 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
List<Action> _actions = new ArrayList<Action>(getActions());
for (TransientActionFactory<?> taf : ExtensionList.lookup(TransientActionFactory.class)) {
if (taf.type().isInstance(this)) {
_actions.addAll(createFor(taf));
try {
_actions.addAll(createFor(taf));
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Could not load actions from " + taf + " for " + this, e);
}
}
}
return Collections.unmodifiableList(_actions);
......@@ -177,4 +184,6 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
@Override public ContextMenu doContextMenu(StaplerRequest request, StaplerResponse response) throws Exception {
return new ContextMenu().from(this,request,response);
}
private static final Logger LOGGER = Logger.getLogger(Actionable.class.getName());
}
......@@ -909,7 +909,11 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable {
if (formData!=null) {
for (Object o : JSONArray.fromObject(formData)) {
JSONObject jo = (JSONObject)o;
String kind = jo.getString("kind");
String kind = jo.optString("$class", null);
if (kind == null) {
// Legacy: Remove once plugins have been staged onto $class
kind = jo.getString("kind");
}
Descriptor<T> d = find(descriptors, kind);
if (d != null) {
items.add(d.newInstance(req, jo));
......
......@@ -114,7 +114,7 @@ public abstract class ItemGroupMixIn {
item.onLoad(parent, subdir.getName());
}
configurations.put(key.call(item), item);
} catch (IOException e) {
} catch (Exception e) {
Logger.getLogger(ItemGroupMixIn.class.getName()).log(Level.WARNING, "could not load " + subdir, e);
}
}
......
......@@ -182,7 +182,11 @@ public final class JDK extends ToolInstallation implements NodeSpecific<JDK>, En
@Override protected FormValidation checkHomeDirectory(File value) {
File toolsJar = new File(value,"lib/tools.jar");
File mac = new File(value,"lib/dt.jar");
if(!toolsJar.exists() && !mac.exists())
// JENKINS-25601: JDK 9+ no longer has tools.jar. Keep the existing dt.jar/tools.jar checks to be safe.
File javac = new File(value, "bin/javac");
File javacExe = new File(value, "bin/javac.exe");
if(!toolsJar.exists() && !mac.exists() && !javac.exists() && !javacExe.exists())
return FormValidation.error(Messages.Hudson_NotJDKDir(value));
return FormValidation.ok();
......
......@@ -105,7 +105,10 @@ import javax.annotation.Nonnull;
import static javax.servlet.http.HttpServletResponse.*;
import jenkins.model.ModelObjectWithChildren;
import jenkins.model.RunIdMigrator;
import jenkins.model.lazy.LazyBuildMixIn;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
* A job is an runnable entity under the monitoring of Hudson.
......@@ -162,6 +165,9 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
// this should have been DescribableList but now it's too late
protected CopyOnWriteList<JobProperty<? super JobT>> properties = new CopyOnWriteList<JobProperty<? super JobT>>();
@Restricted(NoExternalUse.class)
public transient RunIdMigrator runIdMigrator;
protected Job(ItemGroup parent, String name) {
super(parent, name);
}
......@@ -172,11 +178,21 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
holdOffBuildUntilSave = holdOffBuildUntilUserSave;
}
@Override public void onCreatedFromScratch() {
super.onCreatedFromScratch();
runIdMigrator = new RunIdMigrator();
runIdMigrator.created(getBuildDir());
}
@Override
public void onLoad(ItemGroup<? extends Item> parent, String name)
throws IOException {
super.onLoad(parent, name);
File buildDir = getBuildDir();
runIdMigrator = new RunIdMigrator();
runIdMigrator.migrate(buildDir, Jenkins.getInstance().getRootDir());
TextFile f = getNextBuildNumberFile();
if (f.exists()) {
// starting 1.28, we store nextBuildNumber in a separate file.
......@@ -188,7 +204,7 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
}
} catch (NumberFormatException e) {
// try to infer the value of the next build number from the existing build records. See JENKINS-11563
File[] folders = this.getBuildDir().listFiles(new FileFilter() {
File[] folders = buildDir.listFiles(new FileFilter() {
public boolean accept(File file) {
return file.isDirectory() && file.getName().matches("[0-9]+");
}
......@@ -638,8 +654,7 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
File oldBuildDir = getBuildDir();
super.movedTo(destination, newItem, destDir);
File newBuildDir = getBuildDir();
if (oldBuildDir.isDirectory() && !newBuildDir.isDirectory()) {
FileUtils.forceMkdir(destDir.getParentFile());
if (oldBuildDir.isDirectory()) {
FileUtils.moveDirectory(oldBuildDir, newBuildDir);
}
}
......
......@@ -140,7 +140,7 @@ public abstract class LoadBalancer implements ExtensionPoint {
return new LoadBalancer() {
@Override
public Mapping map(Task task, MappingWorksheet worksheet) {
if (Queue.ifBlockedByHudsonShutdown(task)) {
if (Queue.isBlockedByShutdown(task)) {
// if we are quieting down, don't start anything new so that
// all executors will be eventually free.
return null;
......
......@@ -51,7 +51,7 @@ import org.kohsuke.stapler.export.ExportedBean;
*
* <p>
* The actual meaning and the purpose of parameters are entirely up to users, so
* what the concrete parameter implmentation is pluggable. Write subclasses
* what the concrete parameter implementation is pluggable. Write subclasses
* in a plugin and put {@link Extension} on the descriptor to register them.
*
* <p>
......@@ -82,8 +82,8 @@ import org.kohsuke.stapler.export.ExportedBean;
* <h2>Assocaited Views</h2>
* <h4>config.jelly</h4>
* <p>
* {@link ParameterDefinition} class uses <tt>config.jelly</tt> to provide contribute a form
* fragment in the job configuration screen. Values entered there is fed back to
* {@link ParameterDefinition} class uses <tt>config.jelly</tt> to contribute a form
* fragment in the job configuration screen. Values entered there are fed back to
* {@link ParameterDescriptor#newInstance(StaplerRequest, JSONObject)} to create {@link ParameterDefinition}s.
*
* <h4>index.jelly</h4>
......
......@@ -52,6 +52,9 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.triggers.SCMTriggerItem;
/**
......@@ -234,15 +237,37 @@ public abstract class Project<P extends Project<P,B>,B extends Build<P,B>>
protected List<Action> createTransientActions() {
List<Action> r = super.createTransientActions();
for (BuildStep step : getBuildersList())
r.addAll(step.getProjectActions(this));
for (BuildStep step : getPublishersList())
r.addAll(step.getProjectActions(this));
for (BuildWrapper step : getBuildWrappers().values())
r.addAll(step.getProjectActions(this));
for (Trigger trigger : triggers())
r.addAll(trigger.getProjectActions());
for (BuildStep step : getBuildersList()) {
try {
r.addAll(step.getProjectActions(this));
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error loading build step.", e);
}
}
for (BuildStep step : getPublishersList()) {
try {
r.addAll(step.getProjectActions(this));
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error loading publisher.", e);
}
}
for (BuildWrapper step : getBuildWrappers().values()) {
try {
r.addAll(step.getProjectActions(this));
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error loading build wrapper.", e);
}
}
for (Trigger trigger : triggers()) {
try {
r.addAll(trigger.getProjectActions());
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error loading trigger.", e);
}
}
return r;
}
private static final Logger LOGGER = Logger.getLogger(Project.class.getName());
}
......@@ -106,7 +106,6 @@ import javax.servlet.ServletException;
import jenkins.model.Jenkins;
import jenkins.security.QueueItemAuthenticator;
import jenkins.security.QueueItemAuthenticatorConfiguration;
import jenkins.util.AtmostOneTaskExecutor;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
......@@ -1098,8 +1097,11 @@ public class Queue extends ResourceController implements Saveable {
for (BlockedItem p : new ArrayList<BlockedItem>(blockedProjects.values())) {// copy as we'll mutate the list
if (!isBuildBlocked(p) && allowNewBuildableTask(p.task)) {
// ready to be executed
p.leave(this);
makeBuildable(new BuildableItem(p));
Runnable r = makeBuildable(new BuildableItem(p));
if (r != null) {
p.leave(this);
r.run();
}
}
}
}
......@@ -1115,7 +1117,12 @@ public class Queue extends ResourceController implements Saveable {
Task p = top.task;
if (!isBuildBlocked(top) && allowNewBuildableTask(p)) {
// ready to be executed immediately
makeBuildable(new BuildableItem(top));
Runnable r = makeBuildable(new BuildableItem(top));
if (r != null) {
r.run();
} else {
new BlockedItem(top).enter(this);
}
} else {
// this can't be built now because another build is in progress
// set this project aside.
......@@ -1164,10 +1171,14 @@ public class Queue extends ResourceController implements Saveable {
}
}
private void makeBuildable(BuildableItem p) {
if(Jenkins.FLYWEIGHT_SUPPORT && p.task instanceof FlyweightTask && !ifBlockedByHudsonShutdown(p.task)) {
/**
* Tries to make an item ready to build.
* @param p a proposed buildable item
* @return a thunk to actually prepare it (after leaving an earlier list), or null if it cannot be run now
*/
private @CheckForNull Runnable makeBuildable(final BuildableItem p) {
if (p.task instanceof FlyweightTask) {
if (!isBlockedByShutdown(p.task)) {
Jenkins h = Jenkins.getInstance();
Map<Node,Integer> hashSource = new HashMap<Node, Integer>(h.getNodes().size());
......@@ -1183,19 +1194,27 @@ public class Queue extends ResourceController implements Saveable {
Label lbl = p.getAssignedLabel();
for (Node n : hash.list(p.task.getFullDisplayName())) {
Computer c = n.toComputer();
final Computer c = n.toComputer();
if (c==null || c.isOffline()) continue;
if (lbl!=null && !lbl.contains(n)) continue;
if (n.canTake(p) != null) continue;
c.startFlyWeightTask(new WorkUnitContext(p).createWorkUnit(p.task));
makePending(p);
return;
return new Runnable() {
@Override public void run() {
c.startFlyWeightTask(new WorkUnitContext(p).createWorkUnit(p.task));
makePending(p);
}
};
}
}
// if the execution get here, it means we couldn't schedule it anywhere.
// so do the scheduling like other normal jobs.
return null;
} else { // regular heavyweight task
return new Runnable() {
@Override public void run() {
p.enter(Queue.this);
}
};
}
p.enter(this);
}
......@@ -1211,7 +1230,18 @@ public class Queue extends ResourceController implements Saveable {
return pendings.add(p);
}
/** @deprecated Use {@link #isBlockedByShutdown} instead. */
public static boolean ifBlockedByHudsonShutdown(Task task) {
return isBlockedByShutdown(task);
}
/**
* Checks whether a task should not be scheduled because {@link Jenkins#isQuietingDown()}.
* @param task some queue task
* @return true if {@link Jenkins#isQuietingDown()} unless this is a {@link NonBlockingTask}
* @since 1.598
*/
public static boolean isBlockedByShutdown(Task task) {
return Jenkins.getInstance().isQuietingDown() && !(task instanceof NonBlockingTask);
}
......@@ -1235,7 +1265,7 @@ public class Queue extends ResourceController implements Saveable {
/**
* Marks {@link Task}s that are not affected by the {@linkplain Jenkins#isQuietingDown()} quieting down},
* because these tasks keep other tasks executing.
*
* @see #isBlockedByShutdown
* @since 1.336
*/
public interface NonBlockingTask extends Task {}
......@@ -1926,7 +1956,7 @@ public class Queue extends ResourceController implements Saveable {
public CauseOfBlockage getCauseOfBlockage() {
Jenkins jenkins = Jenkins.getInstance();
if(ifBlockedByHudsonShutdown(task))
if(isBlockedByShutdown(task))
return CauseOfBlockage.fromMessage(Messages._Queue_HudsonIsAboutToShutDown());
Label label = getAssignedLabel();
......
......@@ -47,6 +47,7 @@ import hudson.model.Descriptor.FormException;
import hudson.model.Run.RunExecution;
import hudson.model.listeners.RunListener;
import hudson.model.listeners.SaveableListener;
import hudson.model.queue.SubTask;
import hudson.search.SearchIndexBuilder;
import hudson.security.ACL;
import hudson.security.AccessControlled;
......@@ -74,7 +75,6 @@ import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -108,6 +108,7 @@ import jenkins.model.PeepholePermalink;
import jenkins.model.RunAction2;
import jenkins.model.StandardArtifactManager;
import jenkins.model.lazy.BuildReference;
import jenkins.model.lazy.LazyBuildMixIn;
import jenkins.util.VirtualFile;
import jenkins.util.io.OnMaster;
import net.sf.json.JSONObject;
......@@ -146,7 +147,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
* In earlier versions &lt; 1.24, this number is not unique nor continuous,
* but going forward, it will, and this really replaces the build id.
*/
public /*final*/ int number;
public transient /*final*/ int number;
/**
* Previous build. Can be null.
......@@ -172,10 +173,13 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
*/
/* does not compile on JDK 7: private*/ volatile transient RunT previousBuildInProgress;
/** ID as used for historical build records; otherwise null. */
private @CheckForNull String id;
/**
* When the build is scheduled.
*/
protected transient final long timestamp;
protected /*final*/ long timestamp;
/**
* When the build has started running.
......@@ -266,23 +270,12 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
*/
private @CheckForNull ArtifactManager artifactManager;
private static final SimpleDateFormat CANONICAL_ID_FORMATTER = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
public static final ThreadLocal<SimpleDateFormat> ID_FORMATTER = new IDFormatterProvider();
private static final class IDFormatterProvider extends ThreadLocal<SimpleDateFormat> {
@Override
protected SimpleDateFormat initialValue() {
synchronized (CANONICAL_ID_FORMATTER) {
return (SimpleDateFormat) CANONICAL_ID_FORMATTER.clone();
}
}
};
/**
* Creates a new {@link Run}.
* @param job Owner job
*/
protected Run(@Nonnull JobT job) throws IOException {
this(job, new GregorianCalendar());
this(job, System.currentTimeMillis());
this.number = project.assignBuildNumber();
LOGGER.log(FINER, "new {0} @{1}", new Object[] {this, hashCode()});
}
......@@ -290,24 +283,29 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
/**
* Constructor for creating a {@link Run} object in
* an arbitrary state.
* {@link #number} must be set manually.
* <p>May be used in a {@link SubTask#createExecutable} (instead of calling {@link LazyBuildMixIn#newBuild}).
* For example, {@code MatrixConfiguration.newBuild} does this
* so that the {@link #timestamp} as well as {@link #number} are shared with the parent build.
*/
protected Run(@Nonnull JobT job, @Nonnull Calendar timestamp) {
this(job,timestamp.getTimeInMillis());
}
/** @see #Run(Job, Calendar) */
protected Run(@Nonnull JobT job, long timestamp) {
this.project = job;
this.timestamp = timestamp;
this.state = State.NOT_STARTED;
getRootDir().mkdirs();
}
/**
* Loads a run from a log file.
*/
protected Run(@Nonnull JobT project, @Nonnull File buildDir) throws IOException {
this(project, parseTimestampFromBuildDir(buildDir));
this.project = project;
this.previousBuildInProgress = _this(); // loaded builds are always completed
number = Integer.parseInt(buildDir.getName());
reload();
}
......@@ -390,33 +388,6 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
}
}
static class InvalidDirectoryNameException extends IOException {
InvalidDirectoryNameException(File buildDir) {
super("Invalid directory name " + buildDir);
}
}
/*package*/ static long parseTimestampFromBuildDir(@Nonnull File buildDir)
throws IOException, InvalidDirectoryNameException {
try {
if(Util.isSymlink(buildDir)) {
// "Util.resolveSymlink(file)" resolves NTFS symlinks.
File target = Util.resolveSymlinkToFile(buildDir);
if(target != null)
buildDir = target;
}
// canonicalization to ensure we are looking at the ID in the directory name
// as opposed to build numbers which are used in symlinks
// (just in case the symlink check above did not work)
buildDir = buildDir.getCanonicalFile();
return ID_FORMATTER.get().parse(buildDir.getName()).getTime();
} catch (ParseException e) {
throw new InvalidDirectoryNameException(buildDir);
} catch (InterruptedException e) {
throw new IOException("Interrupted while resolving symlink directory "+buildDir,e);
}
}
/**
* Obtains 'this' in a more type safe signature.
*/
......@@ -1000,21 +971,13 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
/**
* Unique ID of this build.
* Usually the decimal form of {@link #number}, but may be a formatted timestamp for historical builds.
*/
@Exported
public @Nonnull String getId() {
return ID_FORMATTER.get().format(new Date(timestamp));
return id != null ? id : Integer.toString(number);
}
/**
* Get the date formatter used to convert the directory name in to a timestamp.
* This is nasty exposure of private data, but needed all the time the directory
* containing the build is used as it's timestamp.
*/
public static @Nonnull DateFormat getIDFormatter() {
return ID_FORMATTER.get();
}
@Override
public @CheckForNull Descriptor getDescriptorByName(String className) {
return Jenkins.getInstance().getDescriptorByName(className);
......@@ -1027,7 +990,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
*/
@Override
public @Nonnull File getRootDir() {
return new File(project.getBuildDir(),getId());
return new File(project.getBuildDir(), Integer.toString(number));
}
/**
......@@ -1502,10 +1465,6 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
RunListener.fireDeleted(this);
synchronized (this) { // avoid holding a lock while calling plugin impls of onDeleted
// if we have a symlink, delete it, too
File link = new File(project.getBuildDir(), String.valueOf(getNumber()));
link.delete();
File tmp = new File(rootDir.getParentFile(),'.'+rootDir.getName());
if (tmp.exists()) {
......@@ -1832,8 +1791,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
}
/**
* Creates a symlink from build number to ID.
* Also makes sure that {@code lastSuccessful} and {@code lastStable} legacy links in the project’s root directory exist.
* Makes sure that {@code lastSuccessful} and {@code lastStable} legacy links in the project’s root directory exist.
* Normally you do not need to call this explicitly, since {@link #execute} does so,
* but this may be needed if you are creating synthetic {@link Run}s as part of a container project (such as Maven builds in a module set).
* You should also ensure that {@link RunListener#fireStarted} and {@link RunListener#fireCompleted} are called.
......@@ -1842,7 +1800,6 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
* @since 1.530
*/
public final void updateSymlinks(@Nonnull TaskListener listener) throws InterruptedException {
Util.createSymlink(getParent().getBuildDir(), getId(), String.valueOf(getNumber()), listener);
createSymlink(listener, "lastSuccessful", PermalinkProjectAction.Permalink.LAST_SUCCESSFUL_BUILD);
createSymlink(listener, "lastStable", PermalinkProjectAction.Permalink.LAST_STABLE_BUILD);
}
......
......@@ -24,10 +24,7 @@
package hudson.model;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
......@@ -38,10 +35,14 @@ import java.util.logging.Level;
import static java.util.logging.Level.*;
import java.util.logging.Logger;
import jenkins.model.RunIdMigrator;
import jenkins.model.lazy.AbstractLazyLoadRunMap;
import static jenkins.model.lazy.AbstractLazyLoadRunMap.Direction.*;
import jenkins.model.lazy.BuildReference;
import jenkins.model.lazy.LazyBuildMixIn;
import org.apache.commons.collections.comparators.ReverseComparator;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
* {@link Map} from build number to {@link Run}.
......@@ -61,6 +62,10 @@ public final class RunMap<R extends Run<?,R>> extends AbstractLazyLoadRunMap<R>
private Constructor<R> cons;
/** Normally overwritten by {@link LazyBuildMixIn#onLoad} or {@link LazyBuildMixIn#onCreatedFromScratch}, in turn created during {@link Job#onLoad}. */
@Restricted(NoExternalUse.class)
public RunIdMigrator runIdMigrator = new RunIdMigrator();
// TODO: before first complete build
// patch up next/previous build link
......@@ -118,6 +123,7 @@ public final class RunMap<R extends Run<?,R>> extends AbstractLazyLoadRunMap<R>
@Override
public boolean removeValue(R run) {
run.dropLinks();
runIdMigrator.delete(dir, run.getId());
return super.removeValue(run);
}
......@@ -169,11 +175,32 @@ public final class RunMap<R extends Run<?,R>> extends AbstractLazyLoadRunMap<R>
return r.getId();
}
/**
* Add a <em>new</em> build to the map.
* Do not use when loading existing builds (use {@link #put(Integer, Object)}).
*/
@Override
public R put(R r) {
// Defense against JENKINS-23152 and its ilk.
File rootDir = r.getRootDir();
if (rootDir.isDirectory()) {
throw new IllegalStateException(rootDir + " already existed; will not overwite with " + r);
}
proposeNewNumber(r.getNumber());
rootDir.mkdirs();
return super._put(r);
}
@Override public R getById(String id) {
int n;
try {
n = Integer.parseInt(id);
} catch (NumberFormatException x) {
n = runIdMigrator.findNumber(id);
}
return getByNumber(n);
}
/**
* Reuses the same reference as much as we can.
* <p>
......@@ -185,31 +212,6 @@ public final class RunMap<R extends Run<?,R>> extends AbstractLazyLoadRunMap<R>
return r.createReference();
}
@Override
protected FilenameFilter createDirectoryFilter() {
final SimpleDateFormat formatter = Run.ID_FORMATTER.get();
return new FilenameFilter() {
@Override public boolean accept(File dir, String name) {
if (name.startsWith("0000")) {
// JENKINS-1461 sometimes create bogus data directories with impossible dates, such as year 0, April 31st,
// or August 0th. Date object doesn't roundtrip those, so we eventually fail to load this data.
// Don't even bother trying.
return false;
}
try {
if (formatter.format(formatter.parse(name)).equals(name)) {
return true;
}
} catch (ParseException e) {
// fall through
}
LOGGER.log(FINE, "Skipping {0} in {1}", new Object[] {name, dir});
return false;
}
};
}
@Override
protected R retrieve(File d) throws IOException {
if(new File(d,"build.xml").exists()) {
......@@ -221,17 +223,6 @@ public final class RunMap<R extends Run<?,R>> extends AbstractLazyLoadRunMap<R>
LOGGER.log(FINEST, "Loaded " + b.getFullDisplayName() + " in " + Thread.currentThread().getName(), new ThisIsHowItsLoaded());
}
return b;
} catch (Run.InvalidDirectoryNameException x) {
Level lvl;
try {
Integer.parseInt(d.getName());
// JENKINS-15587: just an mangled symlink
lvl = Level.FINE;
} catch (NumberFormatException x2) {
// potentially a real build dir, maybe a bug
lvl = Level.WARNING;
}
LOGGER.log(lvl, "skipping non-build directory {0}", d);
} catch (IOException e) {
LOGGER.log(Level.WARNING, "could not load " + d, e);
} catch (InstantiationError e) {
......
......@@ -84,6 +84,14 @@ public final class FutureImpl extends AsyncFutureImpl<Executable> implements Que
}
}
@Override
public synchronized void setAsCancelled() {
super.setAsCancelled();
if (!start.isDone()) {
start.setAsCancelled();
}
}
synchronized void addExecutor(@Nonnull Executor executor) {
this.executors.add(executor);
}
......
......@@ -96,7 +96,7 @@ public class TokenBasedRememberMeServices2 extends TokenBasedRememberMeServices
logger.debug("Did not send remember-me cookie because 'Remember Me' is disabled in " +
"security configuration (principal did set parameter '" + getParameter() + "')");
}
// XXX log warning when receiving remember-me request despite the feature being disabled?
// TODO log warning when receiving remember-me request despite the feature being disabled?
return;
}
......
......@@ -63,7 +63,7 @@ public class ComputerRetentionWork extends PeriodicWork {
if (!nextCheck.containsKey(c) || startRun > nextCheck.get(c)) {
// at the moment I don't trust strategies to wait more than 60 minutes
// strategies need to wait at least one minute
final long waitInMins = Math.min(1, Math.max(60, c.getRetentionStrategy().check(c)));
final long waitInMins = Math.max(1, Math.min(60, c.getRetentionStrategy().check(c)));
nextCheck.put(c, startRun + waitInMins*1000*60 /*MINS->MILLIS*/);
}
}
......
......@@ -70,7 +70,7 @@ public abstract class AbstractCommandInstaller extends ToolInstaller {
@Override
public FilePath performInstallation(ToolInstallation tool, Node node, TaskListener log) throws IOException, InterruptedException {
FilePath dir = preferredLocation(tool, node);
// XXX support Windows batch scripts, Unix scripts with interpreter line, etc. (see CommandInterpreter subclasses)
// TODO support Unix scripts with interpreter line (see Shell.buildCommandLine)
FilePath script = dir.createTextTempFile("hudson", getCommandFileExtension(), command);
try {
String cmd[] = getCommandCall(script);
......
......@@ -23,27 +23,24 @@
*/
package hudson.triggers;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import java.util.Timer;
import hudson.model.AperiodicWork;
import hudson.model.PeriodicWork;
import hudson.security.ACL;
import java.util.TimerTask;
import java.util.logging.Logger;
import java.util.logging.Level;
import hudson.security.ACL;
import java.util.logging.Logger;
import jenkins.util.Timer;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
/**
* {@link Timer} wrapper so that a fatal error in {@link TimerTask}
* won't terminate the timer.
* Wrapper so that a fatal error in {@link TimerTask} will not terminate the timer.
*
* <p>
* {@link Trigger#timer} is a shared timer instance that can be used inside Hudson to
* schedule a recurring work.
*
* {@link Timer#get} is a shared timer instance that can be used inside Jenkins to schedule recurring work.
* But the usual usage is automatic via {@link PeriodicWork} or {@link AperiodicWork}.
* @author Kohsuke Kawaguchi
* @since 1.124
* @see Trigger#timer
*/
public abstract class SafeTimerTask extends TimerTask {
public final void run() {
......
......@@ -214,8 +214,7 @@ public abstract class Trigger<J extends Item> implements Describable<Trigger<?>>
checkTriggers(cal);
} catch (Throwable e) {
LOGGER.log(Level.WARNING,"Cron thread throw an exception",e);
// bug in the code. Don't let the thread die.
e.printStackTrace();
// SafeTimerTask.run would also catch this, but be sure to increment cal too.
}
cal.add(Calendar.MINUTE,1);
......
/*
* The MIT License
*
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Tom Huybrechts
*
*
* 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
......@@ -25,17 +25,22 @@ package hudson.util;
import hudson.ExtensionPoint;
import hudson.security.SecurityRealm;
import jenkins.model.Jenkins;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Servlet {@link Filter} that chains multiple {@link Filter}s, provided by plugins
......@@ -51,61 +56,82 @@ import java.util.Vector;
* @see SecurityRealm
*/
public class PluginServletFilter implements Filter, ExtensionPoint {
private final List<Filter> list = new CopyOnWriteArrayList<Filter>();
private /*almost final*/ FilterConfig config;
private static final List<Filter> LIST = new Vector<Filter>();
private static FilterConfig filterConfig;
public PluginServletFilter() {
/**
* For backward compatibility with plugins that might register filters before Jenkins.getInstance()
* starts functioning, when we are not sure which Jenkins instance a filter belongs to, put it here,
* and let the first Jenkins instance take over.
*/
private static final List<Filter> LEGACY = new Vector<Filter>();
private static final String KEY = PluginServletFilter.class.getName();
/**
* Lookup the instance from servlet context.
*/
private static PluginServletFilter getInstance(ServletContext c) {
return (PluginServletFilter)c.getAttribute(KEY);
}
@edu.umd.cs.findbugs.annotations.SuppressWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
public void init(FilterConfig filterConfig) throws ServletException {
PluginServletFilter.filterConfig = filterConfig;
synchronized (LIST) {
for (Filter f : LIST) {
f.init(filterConfig);
}
}
public void init(FilterConfig config) throws ServletException {
this.config = config;
synchronized (LEGACY) {
list.addAll(LEGACY);
LEGACY.clear();
}
for (Filter f : list) {
f.init(config);
}
config.getServletContext().setAttribute(KEY,this);
}
public static void addFilter(Filter filter) throws ServletException {
synchronized (LIST) {
if (filterConfig != null) {
filter.init(filterConfig);
}
LIST.add(filter);
}
Jenkins j = Jenkins.getInstance();
if (j==null) {
// report who is doing legacy registration
LOGGER.log(Level.WARNING, "Filter instance is registered too early: "+filter, new Exception());
LEGACY.add(filter);
} else {
PluginServletFilter container = getInstance(j.servletContext);
filter.init(container.config);
container.list.add(filter);
}
}
public static void removeFilter(Filter filter) throws ServletException {
synchronized (LIST) {
LIST.remove(filter);
}
Jenkins j = Jenkins.getInstance();
if (j==null) {
LEGACY.remove(filter);
} else {
getInstance(j.servletContext).list.remove(filter);
}
}
public void doFilter(ServletRequest request, ServletResponse response, final FilterChain chain) throws IOException, ServletException {
new FilterChain() {
private int position=0;
// capture the array for thread-safety
private final Filter[] filters = LIST.toArray(new Filter[LIST.size()]);
private final Iterator<Filter> itr = list.iterator();
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if(position==filters.length) {
if(itr.hasNext()) {
// call next
itr.next().doFilter(request, response, this);
} else {
// reached to the end
chain.doFilter(request,response);
} else {
// call next
filters[position++].doFilter(request,response,this);
}
}
}.doFilter(request,response);
}
public void destroy() {
synchronized (LIST) {
for (Filter f : LIST)
f.destroy();
for (Filter f : list) {
f.destroy();
}
list.clear();
}
private static final Logger LOGGER = Logger.getLogger(PluginServletFilter.class.getName());
}
package jenkins.diagnostics.ooom;
import hudson.FilePath;
import hudson.model.Job;
import hudson.model.TaskListener;
import java.io.File;
import java.io.IOException;
/**
* ID and build number of one build.
*/
public final class BuildPtr implements Comparable<BuildPtr> {
final Job job;
final File buildDir;
/**
* Timestamp build ID.
*/
public final String id;
/**
* Build number found from the disk.
*/
public final int n;
/**
* Position of this build according to the ordering induced by {@link #n}
*/
int posByN;
/**
* Position of this build according to the ordering induced by {@link #id}
*/
int posByID;
BuildPtr(Job job, File buildDir, int n) {
this.job = job;
this.n = n;
this.id = buildDir.getName();
this.buildDir = buildDir;
}
@Override
public String toString() {
return buildDir.toString()+":#"+n;
}
/**
* If this build and that build are inconsistent, in that
* their numbers and timestamps are ordering in the wrong direction.
*/
public boolean isInconsistentWith(BuildPtr that) {
return signOfCompare(this.posByN,that.posByN) * signOfCompare(this.posByID,that.posByID) < 0;
}
/**
* sign of (a-b).
*/
private static int signOfCompare(int a, int b) {
if (a>b) return 1;
if (a<b) return -1;
return 0;
}
/**
* Fix the problem by moving the out of order builds into a place that Jenkins won't look at.
*
* TODO: another way to fix this is by adjusting the ID and pretend that the build happened
* at a different timestamp.
*/
public void fix(TaskListener listener) throws IOException, InterruptedException {
File dir = new File(job.getRootDir(), "outOfOrderBuilds");
dir.mkdirs();
File dst = new File(dir, buildDir.getName());
listener.getLogger().println("Renaming "+buildDir);
listener.getLogger().println(" -> "+dst);
if (!buildDir.renameTo(dst)) {
FilePath bd = new FilePath(buildDir);
bd.copyRecursiveTo(new FilePath(dst));
bd.deleteRecursive();
}
// if there's a symlink delete it
new File(buildDir.getParentFile(),String.valueOf(n)).delete();
}
@Override
public int compareTo(BuildPtr that) {
return this.id.compareTo(that.id);
}
}
package jenkins.diagnostics.ooom;
import hudson.Extension;
import hudson.model.AsyncPeriodicWork;
import hudson.model.Job;
import hudson.model.TaskListener;
import hudson.util.TimeUnit2;
import jenkins.model.Jenkins;
import javax.inject.Inject;
import java.io.IOException;
/**
* Discovers {@link Problem}s periodically in the background and
* pass them on to {@link OutOfOrderBuildMonitor}.
*
* @author Kohsuke Kawaguchi
*/
@Extension
public class OutOfOrderBuildDetector extends AsyncPeriodicWork {
@Inject
private OutOfOrderBuildMonitor monitor;
public OutOfOrderBuildDetector() {
super("Out of order build detection");
}
@Override
protected void execute(TaskListener listener) throws IOException, InterruptedException {
execute(listener, 10*1000);
}
/**
* Performs the check synchronously.
*
* @param delay
* delay in the number of milli-seconds to reduce the load on I/O.
*/
public void execute(TaskListener listener, int delay) throws InterruptedException {
for (Job j : Jenkins.getInstance().getAllItems(Job.class)) {
listener.getLogger().println("Scanning " + j.getFullName());
Problem p = Problem.find(j);
if (p!=null) {
monitor.addProblem(p);
listener.getLogger().println(" found problems: "+p);
}
Thread.sleep(delay);
}
}
@Override
public long getRecurrencePeriod() {
return TimeUnit2.DAYS.toMillis(1);
}
}
package jenkins.diagnostics.ooom;
import hudson.Extension;
import hudson.model.TaskListener;
import hudson.util.HttpResponses;
import jenkins.management.AsynchronousAdministrativeMonitor;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.interceptor.RequirePOST;
import java.io.File;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Reports any {@link Problem}s found and report them in the "Manage Jenkins" page.
*
* @author Kohsuke Kawaguchi
*/
@Extension
public class OutOfOrderBuildMonitor extends AsynchronousAdministrativeMonitor {
private final Set<Problem> problems = Collections.synchronizedSet(new LinkedHashSet<Problem>());
@Override
public boolean isActivated() {
return !problems.isEmpty() || getLogFile().exists();
}
void addProblem(Problem p) {
problems.add(p);
}
public Set<Problem> getProblems() {
return Collections.unmodifiableSet(new LinkedHashSet<Problem>(problems));
}
@RequirePOST
public HttpResponse doFix() {
start(false);
return HttpResponses.forwardToPreviousPage();
}
/**
* Discards the current log file so that the "stuff is completed" message will be gone.
*/
@RequirePOST
public HttpResponse doDismiss() {
getLogFile().delete();
return HttpResponses.forwardToPreviousPage();
}
@Override
public String getDisplayName() {
return "Fix Out-of-order Builds";
}
@Override
public File getLogFile() {
return super.getLogFile();
}
@Override
protected void fix(TaskListener listener) throws Exception {
Set<Problem> problems = getProblems();
for (Problem problem : problems) {
problem.fix(listener);
}
this.problems.removeAll(problems);
}
}
package jenkins.diagnostics.ooom;
import hudson.AbortException;
import hudson.Util;
import hudson.model.AbstractProject;
import hudson.model.Job;
import hudson.model.Run;
import hudson.model.TaskListener;
import jenkins.model.lazy.AbstractLazyLoadRunMap;
import org.xml.sax.InputSource;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Logger;
import static java.util.logging.Level.*;
/**
* Look at build numbers in build.xml and compare them with build IDs (timestamps).
*
* When they are inconsistent (newer build timestamp-wise has an older build number),
* it'll confuse the binary search in {@link AbstractLazyLoadRunMap}, so detect them and report them.
*
* @author Kohsuke Kawaguchi
*/
public final class Problem {
public final Job job;
/**
* A smallest set of builds whose removals would correct the order
* inconsistency problem.
*/
private final Set<BuildPtr> offenders = new TreeSet<BuildPtr>();
/**
* Scans the inconsistencies and return the information about it.
*
* @return null if no problems were found.
*/
public static Problem find(Job j) {
Problem p = new Problem(j);
if (p.countInconsistencies()==0) return null;
return p;
}
private Problem(Job j) {
this.job = j;
new Inspector().inspect();
}
public Set<BuildPtr> getOffenders() {
return Collections.unmodifiableSet(offenders);
}
/**
* Number of inconsistencies, which is the number of builds whose IDs
* have to be messed around on disk to collect problems.
*/
public int countInconsistencies() {
return offenders.size();
}
/**
* Equality is based on the job.
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Problem problem = (Problem) o;
return job.equals(problem.job);
}
@Override
public int hashCode() {
return job.hashCode();
}
@Override
public String toString() {
return job.getFullName() + " " + Util.join(offenders);
}
public void fix(TaskListener listener) throws IOException, InterruptedException {
listener.getLogger().println("Fixing problems in " + job.getFullName());
for (BuildPtr o : offenders) {
o.fix(listener);
}
if (job instanceof AbstractProject) {
// let all the current references go and build a new one
AbstractProject a = (AbstractProject) job;
a._getRuns().purgeCache();
}
}
/**
* Finds the problems and builds up the data model of {@link Problem}.
*/
class Inspector {
/**
* All the builds sorted by their {@link BuildPtr#n}
*/
private List<BuildPtr> byN;
/**
* All the builds sorted by their {@link BuildPtr#id}
*/
private List<BuildPtr> byId;
private final XPathExpression xpath;
Inspector() {
try {
xpath = XPathFactory.newInstance().newXPath().compile("/*/number/text()");
} catch (XPathExpressionException e) {
throw new AssertionError(e);
}
}
/**
* Simply report inconsistencies in the ordering.
*/
protected void inspect() {
Map<Integer, BuildPtr> builds = scan();
byN = new ArrayList<BuildPtr>(builds.values());
// this is already sorted by BuildPtr.n
int i=0;
for (BuildPtr b : byN) {
b.posByN = i++;
}
byId = new ArrayList<BuildPtr>(byN);
Collections.sort(byId);
i=0;
for (BuildPtr b : byId) {
b.posByID = i++;
}
while (true) {
BuildPtr b = pick();
if (b==null)
break;
offenders.add(b);
}
}
/**
* Find the most inconsistent build, a build whose removal
* would reduce the # of inconsistencies by the most.
*
* This process takes {@link #offenders} into account.
*
* @return null if there's no more build to remove.
*/
private BuildPtr pick() {
BuildPtr worst=null;
int worstScore=0;
for (BuildPtr b : byN) {
if (offenders.contains(b))
continue;
int score = score(b);
if (score>worstScore) {
worst = b;
worstScore = score;
}
}
return worst;
}
/**
* Count the number of other builds the given build is inconsistent with,
* excluding inconsistencies with {@link #offenders} (since those inconsistencies
* are already effectively resolved by fixing offenders.)
*/
private int score(BuildPtr b) {
int i=0;
for (BuildPtr a : byN) {
if (offenders.contains(a))
continue;
if (a.isInconsistentWith(b))
i++;
}
return i;
}
/**
* Looks at the builds directory of the given job and builds up the full map of build number to its ID.
*/
protected SortedMap<Integer,BuildPtr> scan() {
LOGGER.fine("Inspecting "+job);
SortedMap<Integer,BuildPtr> builds = new TreeMap<Integer,BuildPtr>();
File[] files = job.getBuildDir().listFiles();
if (files==null) return builds;
for (File build : files) {
try {
LOGGER.finer("Inspecting " + build);
if (isInt(build.getName())) {
// if this is a number, then it must be a build number
String s = loadBuildNumberFromBuildXml(build);
if (!s.equals(build.getName())) {
LOGGER.warning(build+" contains build number "+s);
// this index is invalid.
if (build.delete()) {
// index should be a symlink, and if so we can just delete it without losing data.
LOGGER.info("Removed problematic index "+build);
} else {
// the deltion will fail if 'build' isn't just a symlink but an actual directory.
// That is good, as we don't want to delete any real data.
LOGGER.warning("Couldn't delete " + build);
}
}
continue;
}
if (isID(build.getName())) {
String bn = loadBuildNumberFromBuildXml(build);
if (bn==null) {
LOGGER.log(WARNING, "Failed to parse "+build);
continue;
}
int n;
try {
n = Integer.parseInt(bn);
} catch (NumberFormatException e) {
LOGGER.log(WARNING, "Expected number in " + build + " but found " + bn, e);
continue;
}
BuildPtr b = new BuildPtr(job,build,n);
BuildPtr o = builds.put(n, b);
if (o != null) {
LOGGER.log(WARNING, "Multiple builds have the same number: {0} vs. {1}", new Object[] {o, b});
offenders.add(b.compareTo(o) > 0 ? o : b);
}
}
} catch (XPathExpressionException e) {
LOGGER.log(WARNING, "Failed to inspect "+build, e);
} catch (AbortException e) {
LOGGER.log(WARNING, "Failed to inspect "+build+": "+e.getMessage());
} catch (IOException e) {
LOGGER.log(WARNING, "Failed to inspect "+build, e);
}
}
return builds;
}
private boolean isInt(String s) {
try {
Integer.parseInt(s);
return true;
} catch (NumberFormatException e) {
return false;
}
}
private boolean isID(String s) {
try {
Run.ID_FORMATTER.get().parse(s);
return true;
} catch (ParseException e) {
return false;
}
}
private String loadBuildNumberFromBuildXml(File dir) throws XPathExpressionException, IOException {
File buildXml = new File(dir, "build.xml");
if (!buildXml.exists())
throw new AbortException(buildXml+" doesn't exist");
String systemId = buildXml.toURI().toURL().toExternalForm();
return (String)xpath.evaluate(new InputSource(systemId), XPathConstants.STRING);
}
}
private static final Logger LOGGER = Logger.getLogger(Problem.class.getName());
}
......@@ -44,7 +44,7 @@ public class GlobalProjectNamingStrategyConfiguration extends GlobalConfiguratio
final JSONObject optJSONObject = json.optJSONObject("useProjectNamingStrategy");
if (optJSONObject != null) {
final JSONObject strategyObject = optJSONObject.getJSONObject("namingStrategy");
final String className = strategyObject.getString("stapler-class");
final String className = strategyObject.getString("$class");
try {
Class clazz = Class.forName(className, true, Jenkins.getInstance().getPluginManager().uberClassLoader);
final ProjectNamingStrategy strategy = (ProjectNamingStrategy) req.bindJSON(clazz, strategyObject);
......
......@@ -3585,7 +3585,11 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
* Sign up for the user account.
*/
public void doSignup( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
req.getView(getSecurityRealm(), "signup.jelly").forward(req, rsp);
if (getSecurityRealm().allowsSignup()) {
req.getView(getSecurityRealm(), "signup.jelly").forward(req, rsp);
return;
}
req.getView(SecurityRealm.class, "signup.jelly").forward(req, rsp);
}
/**
......@@ -4179,9 +4183,9 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
public static boolean PARALLEL_LOAD = Configuration.getBooleanConfigParameter("parallelLoad", true);
public static boolean KILL_AFTER_LOAD = Configuration.getBooleanConfigParameter("killAfterLoad", false);
/**
* Enabled by default as of 1.337. Will keep it for a while just in case we have some serious problems.
* @deprecated No longer used.
*/
public static boolean FLYWEIGHT_SUPPORT = Configuration.getBooleanConfigParameter("flyweightSupport", true);
public static boolean FLYWEIGHT_SUPPORT = true;
/**
* Tentative switch to activate the concurrent build behavior.
......
/*
* The MIT License
*
* Copyright 2014 Jesse Glick.
*
* 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 jenkins.model;
import hudson.Extension;
import hudson.Util;
import hudson.model.Job;
import hudson.model.RootAction;
import hudson.model.Run;
import hudson.util.AtomicFileWriter;
import hudson.util.StreamTaskListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.time.FastDateFormat;
import org.apache.tools.ant.BuildException;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.StaplerProxy;
import org.kohsuke.stapler.framework.io.WriterOutputStream;
import static java.util.logging.Level.*;
/**
* Converts legacy {@code builds} directories to the current format.
*
* There would be one instance associated with each {@link Job}, to retain ID -> build# mapping.
*
* The {@link Job#getBuildDir} is passed to every method call (rather than being cached) in case it is moved.
*/
@Restricted(NoExternalUse.class)
public final class RunIdMigrator {
private final DateFormat legacyIdFormatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
static final Logger LOGGER = Logger.getLogger(RunIdMigrator.class.getName());
private static final String MAP_FILE = "legacyIds";
/** avoids wasting a map for new jobs */
private static final Map<String,Integer> EMPTY = new TreeMap<String,Integer>();
/**
* Did we record "unmigrate" instruction for this $JENKINS_HOME? Yes if it's in the set.
*/
private static final Set<File> offeredToUnmigrate = Collections.synchronizedSet(new HashSet<File>());
private @Nonnull Map<String,Integer> idToNumber = EMPTY;
public RunIdMigrator() {}
/**
* @return whether there was a file to load
*/
private boolean load(File dir) {
File f = new File(dir, MAP_FILE);
if (!f.isFile()) {
return false;
}
if (f.length() == 0) {
return true;
}
idToNumber = new TreeMap<String,Integer>();
try {
for (String line : FileUtils.readLines(f)) {
int i = line.indexOf(' ');
idToNumber.put(line.substring(0, i), Integer.parseInt(line.substring(i + 1)));
}
} catch (Exception x) { // IOException, IndexOutOfBoundsException, NumberFormatException
LOGGER.log(WARNING, "could not read from " + f, x);
}
return true;
}
private void save(File dir) {
File f = new File(dir, MAP_FILE);
try {
AtomicFileWriter w = new AtomicFileWriter(f);
try {
for (Map.Entry<String,Integer> entry : idToNumber.entrySet()) {
w.write(entry.getKey() + ' ' + entry.getValue() + '\n');
}
w.commit();
} finally {
w.abort();
}
} catch (IOException x) {
LOGGER.log(WARNING, "could not save changes to " + f, x);
}
}
/**
* Called when a job is first created.
* Just saves an empty marker indicating that this job needs no migration.
* @param dir as in {@link Job#getBuildDir}
*/
public void created(File dir) {
save(dir);
}
/**
* Perform one-time migration if this has not been done already.
* Where previously there would be a {@code 2014-01-02_03-04-05/build.xml} specifying {@code <number>99</number>} plus a symlink {@code 99 → 2014-01-02_03-04-05},
* after migration there will be just {@code 99/build.xml} specifying {@code <id>2014-01-02_03-04-05</id>} and {@code <timestamp>…</timestamp>} according to local time zone at time of migration.
* Newly created builds are untouched.
* Does not throw {@link IOException} since we make a best effort to migrate but do not consider it fatal to job loading if we cannot.
* @param dir as in {@link Job#getBuildDir}
* @param jenkinsHome root directory of Jenkins (for logging only)
* @return true if migration was performed
*/
public synchronized boolean migrate(File dir, @CheckForNull File jenkinsHome) {
if (load(dir)) {
LOGGER.log(FINER, "migration already performed for {0}", dir);
return false;
}
if (!dir.isDirectory()) {
LOGGER.log(/* normal during Job.movedTo */FINE, "{0} was unexpectedly missing", dir);
return false;
}
LOGGER.log(INFO, "Migrating build records in {0}", dir);
doMigrate(dir);
save(dir);
if (jenkinsHome != null && offeredToUnmigrate.add(jenkinsHome))
LOGGER.log(WARNING, "Build record migration (https://wiki.jenkins-ci.org/display/JENKINS/JENKINS-24380+Migration) is one-way. If you need to downgrade Jenkins, run: {0}", getUnmigrationCommandLine(jenkinsHome));
return true;
}
private static String getUnmigrationCommandLine(File jenkinsHome) {
StringBuilder cp = new StringBuilder();
for (Class<?> c : new Class<?>[] {RunIdMigrator.class, /* TODO how to calculate transitive dependencies automatically? */Charsets.class, WriterOutputStream.class, BuildException.class, FastDateFormat.class}) {
URL location = c.getProtectionDomain().getCodeSource().getLocation();
String locationS = location.toString();
if (location.getProtocol().equals("file")) {
try {
locationS = new File(location.toURI()).getAbsolutePath();
} catch (URISyntaxException x) {
// never mind
}
}
if (cp.length() > 0) {
cp.append(File.pathSeparator);
}
cp.append(locationS);
}
return String.format("java -classpath \"%s\" %s \"%s\"", cp, RunIdMigrator.class.getName(), jenkinsHome);
}
private static final Pattern NUMBER_ELT = Pattern.compile("(?m)^ <number>(\\d+)</number>(\r?\n)");
private void doMigrate(File dir) {
idToNumber = new TreeMap<String,Integer>();
File[] kids = dir.listFiles();
// Need to process symlinks first so we can rename to them.
List<File> kidsList = new ArrayList<File>(Arrays.asList(kids));
Iterator<File> it = kidsList.iterator();
while (it.hasNext()) {
File kid = it.next();
String name = kid.getName();
try {
String link = Util.resolveSymlink(kid);
if (link == null) {
continue;
}
try {
Integer.parseInt(name);
if (kid.delete()) {
LOGGER.log(FINE, "deleted build number symlink {0} → {1}", new Object[] {name, link});
} else {
LOGGER.log(WARNING, "could not delete build number symlink {0} → {1}", new Object[] {name, link});
}
} catch (NumberFormatException x) {
LOGGER.log(FINE, "skipping other symlink {0} → {1}", new Object[] {name, link});
}
it.remove();
} catch (Exception x) {
LOGGER.log(WARNING, "failed to process " + kid, x);
}
}
it = kidsList.iterator();
while (it.hasNext()) {
File kid = it.next();
try {
String name = kid.getName();
try {
Integer.parseInt(name);
LOGGER.log(FINE, "skipping new build dir {0}", name);
continue;
} catch (NumberFormatException x) {
// OK, next…
}
if (!kid.isDirectory()) {
LOGGER.log(FINE, "skipping non-directory {0}", name);
continue;
}
long timestamp;
try {
synchronized (legacyIdFormatter) {
timestamp = legacyIdFormatter.parse(name).getTime();
}
} catch (ParseException x) {
LOGGER.log(WARNING, "found unexpected dir {0}", name);
continue;
}
File buildXml = new File(kid, "build.xml");
if (!buildXml.isFile()) {
LOGGER.log(WARNING, "found no build.xml in {0}", name);
continue;
}
String xml = FileUtils.readFileToString(buildXml, Charsets.UTF_8);
Matcher m = NUMBER_ELT.matcher(xml);
if (!m.find()) {
LOGGER.log(WARNING, "could not find <number> in {0}/build.xml", name);
continue;
}
int number = Integer.parseInt(m.group(1));
String nl = m.group(2);
xml = m.replaceFirst(" <id>" + name + "</id>" + nl + " <timestamp>" + timestamp + "</timestamp>" + nl);
File newKid = new File(dir, Integer.toString(number));
if (!kid.renameTo(newKid)) {
LOGGER.log(WARNING, "failed to rename {0} to {1}", new Object[] {name, number});
continue;
}
FileUtils.writeStringToFile(new File(newKid, "build.xml"), xml, Charsets.UTF_8);
LOGGER.log(FINE, "fully processed {0} → {1}", new Object[] {name, number});
idToNumber.put(name, number);
} catch (Exception x) {
LOGGER.log(WARNING, "failed to process " + kid, x);
}
}
}
/**
* Look up a historical run by ID.
* @param id a nonnumeric ID which may be a valid {@link Run#getId}
* @return the corresponding {@link Run#number}, or 0 if unknown
*/
public synchronized int findNumber(@Nonnull String id) {
Integer number = idToNumber.get(id);
return number != null ? number : 0;
}
/**
* Delete the record of a build.
* @param dir as in {@link Job#getBuildDir}
* @param id a {@link Run#getId}
*/
public synchronized void delete(File dir, String id) {
if (idToNumber.remove(id) != null) {
save(dir);
}
}
/**
* Reverses the migration, in case you want to revert to the older format.
* @param args one parameter, {@code $JENKINS_HOME}
*/
public static void main(String... args) throws Exception {
if (args.length != 1) {
throw new Exception("pass one parameter, $JENKINS_HOME");
}
File root = new File(args[0]);
File jobs = new File(root, "jobs");
if (!jobs.isDirectory()) {
throw new FileNotFoundException("no such $JENKINS_HOME " + root);
}
new RunIdMigrator().unmigrateJobsDir(jobs);
}
private void unmigrateJobsDir(File jobs) throws Exception {
for (File job : jobs.listFiles()) {
File[] kids = job.listFiles();
if (kids == null) {
continue;
}
for (File kid : kids) {
if (!kid.isDirectory()) {
continue;
}
if (kid.getName().equals("builds")) {
unmigrateBuildsDir(kid);
} else {
// Might be jobs, modules, promotions, etc.; we assume an ItemGroup.getRootDirFor implementation returns grandchildren.
unmigrateJobsDir(kid);
}
}
}
}
private static final Pattern ID_ELT = Pattern.compile("(?m)^ <id>([0-9_-]+)</id>(\r?\n)");
private static final Pattern TIMESTAMP_ELT = Pattern.compile("(?m)^ <timestamp>(\\d+)</timestamp>(\r?\n)");
/** Inverse of {@link #doMigrate}. */
private void unmigrateBuildsDir(File builds) throws Exception {
File mapFile = new File(builds, MAP_FILE);
if (!mapFile.isFile()) {
System.err.println(builds + " does not look to have been migrated yet; skipping");
return;
}
for (File build : builds.listFiles()) {
int number;
try {
number = Integer.parseInt(build.getName());
} catch (NumberFormatException x) {
continue;
}
File buildXml = new File(build, "build.xml");
if (!buildXml.isFile()) {
System.err.println(buildXml + " did not exist");
continue;
}
String xml = FileUtils.readFileToString(buildXml, Charsets.UTF_8);
Matcher m = TIMESTAMP_ELT.matcher(xml);
if (!m.find()) {
System.err.println(buildXml + " did not contain <timestamp> as expected");
continue;
}
long timestamp = Long.parseLong(m.group(1));
String nl = m.group(2);
xml = m.replaceFirst(" <number>" + number + "</number>" + nl);
m = ID_ELT.matcher(xml);
String id;
if (m.find()) {
id = m.group(1);
xml = m.replaceFirst("");
} else {
// Post-migration build. We give it a new ID based on its timestamp.
id = legacyIdFormatter.format(new Date(timestamp));
}
FileUtils.write(buildXml, xml, Charsets.UTF_8);
if (!build.renameTo(new File(builds, id))) {
System.err.println(build + " could not be renamed");
}
Util.createSymlink(builds, id, Integer.toString(number), StreamTaskListener.fromStderr());
}
Util.deleteFile(mapFile);
System.err.println(builds + " has been restored to its original format");
}
/**
* Expose unmigration instruction to the user.
*/
@Extension
public static class UnmigrationInstruction implements RootAction, StaplerProxy {
@Override
public String getIconFileName() {
return null;
}
@Override
public String getDisplayName() {
return null;
}
@Override
public String getUrlName() {
return "JENKINS-24380";
}
@Override
public Object getTarget() {
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
return this;
}
public String getCommand() {
return RunIdMigrator.getUnmigrationCommandLine(Jenkins.getInstance().getRootDir());
}
}
}
......@@ -66,7 +66,7 @@ public final class BuildReference<R> {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BuildReference that = (BuildReference) o;
BuildReference<?> that = (BuildReference) o;
return id.equals(that.id);
}
......@@ -76,6 +76,11 @@ public final class BuildReference<R> {
return id.hashCode();
}
@Override public String toString() {
R r = get();
return r != null ? r.toString() : id;
}
/**
* An abstraction of {@link Reference}.
* @since 1.548
......
......@@ -47,6 +47,7 @@ import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import static java.util.logging.Level.FINER;
import jenkins.model.RunIdMigrator;
/**
* Makes it easier to use a lazy {@link RunMap} from a {@link Job} implementation.
......@@ -63,9 +64,6 @@ public abstract class LazyBuildMixIn<JobT extends Job<JobT,RunT> & Queue.Task &
@SuppressWarnings("deprecation") // [JENKINS-15156] builds accessed before onLoad or onCreatedFromScratch called
private @Nonnull RunMap<RunT> builds = new RunMap<RunT>();
// keep track of the previous time we started a build
private long lastBuildStartTime;
/**
* Initializes this mixin.
* Call this from a constructor and {@link AbstractItem#onLoad} to make sure it is always initialized.
......@@ -92,14 +90,14 @@ public abstract class LazyBuildMixIn<JobT extends Job<JobT,RunT> & Queue.Task &
}
/**
* Something to be called from {@link AbstractItem#onCreatedFromScratch}.
* Something to be called from {@link Job#onCreatedFromScratch}.
*/
public final void onCreatedFromScratch() {
builds = createBuildRunMap();
}
/**
* Something to be called from {@link AbstractItem#onLoad}.
* Something to be called from {@link Job#onLoad}.
*/
@SuppressWarnings("unchecked")
public void onLoad(ItemGroup<? extends Item> parent, String name) throws IOException {
......@@ -123,7 +121,8 @@ public abstract class LazyBuildMixIn<JobT extends Job<JobT,RunT> & Queue.Task &
// if we are reloading, keep all those that are still building intact
for (RunT r : currentBuilds.getLoadedBuilds().values()) {
if (r.isBuilding()) {
_builds.put(r);
// Do not use RunMap.put(Run):
_builds.put(r.getNumber(), r);
}
}
}
......@@ -131,11 +130,15 @@ public abstract class LazyBuildMixIn<JobT extends Job<JobT,RunT> & Queue.Task &
}
private RunMap<RunT> createBuildRunMap() {
return new RunMap<RunT>(asJob().getBuildDir(), new RunMap.Constructor<RunT>() {
RunMap<RunT> r = new RunMap<RunT>(asJob().getBuildDir(), new RunMap.Constructor<RunT>() {
@Override public RunT create(File dir) throws IOException {
return loadBuild(dir);
}
});
RunIdMigrator runIdMigrator = asJob().runIdMigrator;
assert runIdMigrator != null;
r.runIdMigrator = runIdMigrator;
return r;
}
/**
......@@ -169,19 +172,7 @@ public abstract class LazyBuildMixIn<JobT extends Job<JobT,RunT> & Queue.Task &
* Calls the ({@link Job}) constructor of {@link #getBuildClass}.
* Suitable for {@link SubTask#createExecutable}.
*/
@SuppressWarnings("SleepWhileHoldingLock")
@edu.umd.cs.findbugs.annotations.SuppressWarnings("SWL_SLEEP_WITH_LOCK_HELD")
public final synchronized RunT newBuild() throws IOException {
// make sure we don't start two builds in the same second
// so the build directories will be different too
long timeSinceLast = System.currentTimeMillis() - lastBuildStartTime;
if (timeSinceLast < 1000) {
try {
Thread.sleep(1000 - timeSinceLast);
} catch (InterruptedException e) {
}
}
lastBuildStartTime = System.currentTimeMillis();
try {
RunT lastBuild = getBuildClass().getConstructor(asJob().getClass()).newInstance(asJob());
builds.put(lastBuild);
......
......@@ -834,7 +834,7 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
// designated to use a specific loader first
// (this one or the parent one)
// XXX - shouldn't this always return false in isolated mode?
// TODO shouldn't this always return false in isolated mode?
boolean useParentFirst = parentFirst;
......@@ -1573,4 +1573,4 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener {
return new AntClassLoader(parent, project, path, parentFirst);
}
}
\ No newline at end of file
}
/*
* The MIT License
*
* Copyright 2015 Jesse Glick.
*
* 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 jenkins.util;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Executor service that logs otherwise uncaught errors.
* TODO is there anything in Guava for this?
*/
class ErrorLoggingScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
private static final Logger LOGGER = Logger.getLogger(ErrorLoggingScheduledThreadPoolExecutor.class.getName());
ErrorLoggingScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize);
}
ErrorLoggingScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, threadFactory);
}
ErrorLoggingScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) {
super(corePoolSize, handler);
}
ErrorLoggingScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, threadFactory, handler);
}
@Override protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
Future<?> f = (Future<?>) r;
if (f.isDone()) { // TODO super Javadoc does not suggest this, but without it, we hang in FutureTask.awaitDone!
try {
f.get(/* just to be on the safe side, do not wait */0, TimeUnit.NANOSECONDS);
} catch (TimeoutException x) {
// should not happen, right?
} catch (CancellationException x) {
// probably best to ignore this
} catch (ExecutionException x) {
t = x.getCause();
} catch (InterruptedException x) {
Thread.currentThread().interrupt();
}
}
}
if (t != null) {
LOGGER.log(Level.WARNING, "failure in task not wrapped in SafeTimerTask", t);
}
}
}
package jenkins.util;
import hudson.util.DaemonThreadFactory;
import hudson.util.NamingThreadFactory;
import javax.annotation.Nonnull;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
* Holds the {@link ScheduledExecutorService} for running all background tasks in Jenkins.
* This ExecutorService will create additional threads to execute due (enabled) tasks.
......@@ -42,7 +39,7 @@ public class Timer {
if (executorService == null) {
// corePoolSize is set to 10, but will only be created if needed.
// ScheduledThreadPoolExecutor "acts as a fixed-sized pool using corePoolSize threads"
executorService = Executors.newScheduledThreadPool(10, new NamingThreadFactory(new DaemonThreadFactory(), "jenkins.util.Timer"));
executorService = new ErrorLoggingScheduledThreadPoolExecutor(10, new NamingThreadFactory(new DaemonThreadFactory(), "jenkins.util.Timer"));
}
return executorService;
}
......
<!--
The MIT License
Copyright (c) 2014 Daniel Beck
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">
<st:statusCode value="404" />
<st:include page="sidepanel.jelly" from="${app}" it="${app}" />
<l:layout title="${%Signup not supported}">
<l:main-panel>
<h1>${%Sign up}</h1>
${%This is not supported in the current configuration.}
</l:main-panel>
</l:layout>
</j:jelly>
......@@ -56,9 +56,7 @@ THE SOFTWARE.
<j:set var="descriptor" value="${d}"/>
<j:set var="instance"
value="${it.launcher.descriptor==d ? it.launcher : null}"/>
<f:invisibleEntry>
<input type="hidden" name="stapler-class" value="${d.clazz.name}" />
</f:invisibleEntry>
<f:class-entry descriptor="${d}" />
<st:include from="${d}" page="${d.configPage}" optional="true"/>
</f:dropdownListBlock>
</j:forEach>
......@@ -76,9 +74,7 @@ THE SOFTWARE.
<j:set var="descriptor" value="${d}"/>
<j:set var="instance"
value="${it.retentionStrategy.descriptor==d ? it.retentionStrategy : null}"/>
<tr><td>
<input type="hidden" name="stapler-class" value="${d.clazz.name}" />
</td></tr>
<f:class-entry descriptor="${d}" />
<st:include from="${d}" page="${d.configPage}" optional="true"/>
</f:dropdownListBlock>
</j:if>
......
......@@ -40,7 +40,7 @@ THE SOFTWARE.
<l:icon class="icon-grey icon-sm"/>
</div>
<!-- Don't use math unless needed, in case nextBuildNumber is not numeric -->
<div class="display-name">
<div class="display-name" title="${%Expected build number}">
#${queuedItems.size()==1 ? it.owner.nextBuildNumber
: it.owner.nextBuildNumber+queuedItems.size()-i-1}
</div>
......@@ -55,7 +55,11 @@ THE SOFTWARE.
(${%pending})
</j:otherwise>
</j:choose>
${item.params}
<j:if test="${!item.params.isEmpty()}">
<div style="float:right;margin-right:10px;">
<a href="#" tooltip="Build Parameters:${h.escape(item.params)}"><l:icon class="icon-notepad icon-sm" /></a>
</div>
</j:if>
</div>
<div class="pane build-controls">
<div class="build-stop">
......
Problem.DisplayName={0} builds have inconsistent timestamps in <a href="{2}">{1}</a>
\ No newline at end of file
/*
The MIT License
Copyright (c) 2013, 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.
*/
package jenkins.diagnostics.ooom.OutOfOrderBuildMonitor
import jenkins.diagnostics.ooom.BuildPtr
import jenkins.diagnostics.ooom.Problem;
def f = namespace(lib.FormTagLib)
if (my.isFixingActive()) {
div(class:"info") {
raw _("inProgress",my.url)
}
} else if (my.logFile.exists()) {
form(method:"POST",action:"${my.url}/dismiss",name:"dismissOutOfOrderBuilds") {
raw _("completed",my.url)
f.submit(name:"dismiss",value:_("Dismiss this message"))
}
}
if (!my.problems.isEmpty()) {
form(method:"POST",action:"${my.url}/fix",name:"fixOutOfOrderBuilds") {
div(class:"warning") {
raw _("buildsAreOutOfOrder")
}
ul {
my.problems.each { Problem p ->
li {
raw(_("problem",
p.countInconsistencies(),
p.job.fullDisplayName,
rootURL+'/'+p.job.url))
text(" : ")
p.offenders.each { BuildPtr o ->
a(href:rootURL+'/'+p.job.url+'/'+o.n, "#${o.n}")
raw(" ")
}
}
}
}
div(align:"right") {
f.submit(name:"fix",value:_("Correct those problems by moving offending records to a backup folder"))
}
}
}
problem={0} builds in <a href="{2}">{1}</a>
buildsAreOutOfOrder=Some projects have builds whose timestamps are inconsistent. \
<a href="http://jenkins-ci.org/issue/18289">These will confuse Jenkins when it tries to look up build records</a>.
inProgress=Out-of-order builds are being swept under the carpet. <a href="{0}/log">You can check the log</a>.
completed=Out-of-order builds were swept under the carpet. <a href="{0}/log">You can check the log</a>.
\ No newline at end of file
<!--
The MIT License
Copyright (c) 2014, 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" xmlns:i="jelly:fmt">
<l:layout title="Jenkins">
<l:header />
<l:main-panel>
<p>
To reverse the effect of <a href="http://jenkins-ci.org/issue/24380">JENKINS-24380</a> fix, run the following command
on the server. See <a href="https://wiki.jenkins-ci.org/display/JENKINS/JENKINS-24380+Migration">Wiki page</a>
for more details:
</p>
<table style="width:100%">
<tr>
<td style="line-height:2em; width:80%">
<input type="text" value="${it.command}" style="width:100%"/>
</td>
<td style="line-height:2em">
<l:copyButton message="${%Copied}" text="${it.command}"/>
</td>
</tr>
</table>
</l:main-panel>
</l:layout>
</j:jelly>
<!--
The MIT License
Copyright (c) 2014, Google, 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">
<st:documentation>
Invisible &lt;f:entry> type for embedding a descriptor's $class field.
<st:attribute name="clazz">
The describable class that we are instantiating via structured form submission.
</st:attribute>
<st:attribute name="descriptor">
The descriptor of the describable that we are instantiating via
structured form submission. Mutually exclusive with clazz.
</st:attribute>
</st:documentation>
<j:set var="clazz" value="${attrs.clazz ?: attrs.descriptor.clazz.name}" />
<f:invisibleEntry>
<!-- Legacy: Remove once plugins have been staged onto $class -->
<input type="hidden" name="stapler-class" value="${clazz}" />
<!-- Legacy: Remove once plugins have been staged onto $class -->
<j:if test="${attrs.descriptor != null}">
<input type="hidden" name="kind" value="${attrs.descriptor.id}" />
</j:if>
<input type="hidden" name="$class" value="${clazz}" />
</f:invisibleEntry>
</j:jelly>
\ No newline at end of file
......@@ -53,9 +53,7 @@ THE SOFTWARE.
title="${d.displayName}" checked="${instance.descriptor==d}">
<j:set var="descriptor" value="${d}" />
<j:set var="instance" value="${instance.descriptor==d?instance:null}" />
<f:invisibleEntry>
<input type="hidden" name="stapler-class" value="${descriptor.clazz.name}"/>
</f:invisibleEntry>
<f:class-entry descriptor="${descriptor}" />
<st:include from="${d}" page="${d.configPage}" optional="true" />
</f:radioBlock>
</j:forEach>
......
......@@ -50,11 +50,7 @@ THE SOFTWARE.
</j:when>
<j:when test="${dropdownListMode=='generateEntries'}">
<!-- sandwich them by markers so that we know what to show/hide -->
<tr class="dropdownList-start rowvg-start">
<j:if test="${!empty(attrs.staplerClass)}">
<td style="display:none"><input type="hidden" name="stapler-class" value="${attrs.staplerClass}"/></td>
</j:if>
</tr>
<tr class="dropdownList-start rowvg-start" />
<j:choose>
<j:when test="${attrs.lazy!=null and !attrs.selected}">
<l:renderOnDemand capture="${attrs.lazy}" tag="tr">
......@@ -65,6 +61,9 @@ THE SOFTWARE.
<d:invokeBody />
</j:otherwise>
</j:choose>
<j:if test="${!empty(attrs.staplerClass)}">
<f:class-entry clazz="${attrs.staplerClass}" />
</j:if>
<tr class="dropdownList-end rowvg-end" />
</j:when>
</j:choose>
......
......@@ -91,14 +91,13 @@ THE SOFTWARE.
<d:invokeBody/>
<f:class-entry descriptor="${descriptor}" />
<f:block>
<div align="right">
<input type="hidden" name="stapler-class" value="${descriptor.clazz.name}" />
<f:repeatableDeleteButton value="${attrs.deleteCaption}" />
</div>
</f:block>
</table>
<input type="hidden" name="kind" value="${descriptor.id}" />
</d:tag>
</d:taglib>
......
......@@ -47,11 +47,7 @@ THE SOFTWARE.
<j:set var="descriptor" value="${d}" />
<j:set var="instance" value="${currentDescriptor==d?currentInstance:null}" />
<st:include from="${d}" page="${d.configPage}" optional="true" />
<f:invisibleEntry><!-- this tells Stapler which block is for which class -->
<input type="hidden" name="stapler-class" value="${d.clazz.name}" />
<input type="hidden" name="kind" value="${descriptor.id}"/>
</f:invisibleEntry>
<f:class-entry descriptor="${d}" />
</f:radioBlock>
</j:forEach>
</table>
......
/*
* The MIT License
*
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
*
* 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
......@@ -23,8 +23,10 @@
*/
package hudson;
import static org.junit.Assert.assertEquals;
import hudson.model.Saveable;
import junit.framework.TestCase;
import org.junit.Test;
import java.io.IOException;
......@@ -33,7 +35,7 @@ import java.io.IOException;
*
* @author Kohsuke Kawaguchi
*/
public class BulkChangeTest extends TestCase {
public class BulkChangeTest {
private class Point implements Saveable {
/**
......@@ -68,7 +70,8 @@ public class BulkChangeTest extends TestCase {
/**
* If there is no BulkChange, we should see two saves.
*/
public void testNoBulkChange() throws Exception {
@Test
public void noBulkChange() throws Exception {
Point pt = new Point();
pt.set(0,0);
assertEquals(2,pt.saveCount);
......@@ -77,7 +80,8 @@ public class BulkChangeTest extends TestCase {
/**
* With a {@link BulkChange}, this will become just one save.
*/
public void testBulkChange() throws Exception {
@Test
public void bulkChange() throws Exception {
Point pt = new Point();
BulkChange bc = new BulkChange(pt);
try {
......@@ -91,7 +95,8 @@ public class BulkChangeTest extends TestCase {
/**
* {@link BulkChange}s can be nested.
*/
public void testNestedBulkChange() throws Exception {
@Test
public void nestedBulkChange() throws Exception {
Point pt = new Point();
Point _ = new Point();
BulkChange bc1 = new BulkChange(pt);
......
package hudson;
import junit.framework.TestCase;
import hudson.remoting.Channel;
import hudson.remoting.FastPipedInputStream;
import hudson.remoting.FastPipedOutputStream;
import java.io.IOException;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import hudson.remoting.Channel;
import hudson.remoting.FastPipedInputStream;
import hudson.remoting.FastPipedOutputStream;
import java.util.concurrent.Future;
import org.junit.rules.ExternalResource;
/**
* Test that uses a connected channel.
*
* @author Kohsuke Kawaguchi
*/
public abstract class ChannelTestCase extends TestCase {
public final class ChannelRule extends ExternalResource {
/**
* Two channels that are connected to each other, but shares the same classloader.
*/
protected Channel french;
protected Channel british;
private ExecutorService executors = Executors.newCachedThreadPool();
public Channel french;
public Channel british;
private ExecutorService executors;
@Override
protected void setUp() throws Exception {
super.setUp();
@Override protected void before() throws Exception {
executors = Executors.newCachedThreadPool();
final FastPipedInputStream p1i = new FastPipedInputStream();
final FastPipedInputStream p2i = new FastPipedInputStream();
final FastPipedOutputStream p1o = new FastPipedOutputStream(p1i);
......@@ -47,8 +45,7 @@ public abstract class ChannelTestCase extends TestCase {
british = f2.get();
}
@Override
protected void tearDown() throws Exception {
@Override protected void after() {
try {
french.close(); // this will automatically initiate the close on the other channel, too.
french.join();
......@@ -98,6 +95,8 @@ public abstract class ChannelTestCase extends TestCase {
*/
e.printStackTrace();
} catch (InterruptedException x) {
throw new AssertionError(x);
}
executors.shutdownNow();
}
......
/*
* The MIT License
*
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
*
* 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
......@@ -23,8 +23,10 @@
*/
package hudson;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import hudson.EnvVars.OverrideOrderCalculator;
import junit.framework.TestCase;
import java.util.Arrays;
import java.util.Collections;
......@@ -32,25 +34,26 @@ import java.util.HashSet;
import java.util.List;
import com.google.common.collect.Sets;
import org.junit.Test;
/**
* @author Kohsuke Kawaguchi
*/
public class EnvVarsTest extends TestCase {
/**
* Makes sure that {@link EnvVars} behave in case-insensitive way.
*/
public void test1() {
public class EnvVarsTest {
@Test
public void caseInsensitive() {
EnvVars ev = new EnvVars(Collections.singletonMap("Path","A:B:C"));
assertTrue(ev.containsKey("PATH"));
assertEquals("A:B:C",ev.get("PATH"));
}
public void testOverrideExpandingAll() throws Exception {
@Test
public void overrideExpandingAll() {
EnvVars env = new EnvVars();
env.put("PATH", "orig");
env.put("A", "Value1");
EnvVars overrides = new EnvVars();
overrides.put("PATH", "append" + Platform.current().pathSeparator + "${PATH}");
overrides.put("B", "${A}Value2");
......@@ -58,14 +61,15 @@ public class EnvVarsTest extends TestCase {
overrides.put("D", "${E}");
overrides.put("E", "Value3");
overrides.put("PATH+TEST", "another");
env.overrideExpandingAll(overrides);
assertEquals("Value1Value2Value3", env.get("C"));
assertEquals("another" + Platform.current().pathSeparator + "append" + Platform.current().pathSeparator + "orig", env.get("PATH"));
}
public void testOverrideOrderCalculatorSimple() {
@Test
public void overrideOrderCalculatorSimple() {
EnvVars env = new EnvVars();
EnvVars overrides = new EnvVars();
overrides.put("A", "NoReference");
......@@ -73,14 +77,15 @@ public class EnvVarsTest extends TestCase {
overrides.put("B", "Refer1${A}");
overrides.put("C", "Refer2${B}");
overrides.put("D", "Refer3${B}${Nosuch}");
OverrideOrderCalculator calc = new OverrideOrderCalculator(env, overrides);
List<String> order = calc.getOrderedVariableNames();
assertEquals(Arrays.asList("A", "B", "C", "D", "A+B"), order);
}
public void testOverrideOrderCalculatorInOrder() {
@Test
public void overrideOrderCalculatorInOrder() {
EnvVars env = new EnvVars();
EnvVars overrides = new EnvVars();
overrides.put("A", "NoReference");
......@@ -88,45 +93,48 @@ public class EnvVarsTest extends TestCase {
overrides.put("C", "${B}");
overrides.put("D", "${E}");
overrides.put("E", "${C}");
OverrideOrderCalculator calc = new OverrideOrderCalculator(env, overrides);
List<String> order = calc.getOrderedVariableNames();
assertEquals(Arrays.asList("A", "B", "C", "E", "D"), order);
}
public void testOverrideOrderCalculatorMultiple() {
@Test
public void overrideOrderCalculatorMultiple() {
EnvVars env = new EnvVars();
EnvVars overrides = new EnvVars();
overrides.put("A", "Noreference");
overrides.put("B", "${A}");
overrides.put("C", "${A}${B}");
OverrideOrderCalculator calc = new OverrideOrderCalculator(env, overrides);
List<String> order = calc.getOrderedVariableNames();
assertEquals(Arrays.asList("A", "B", "C"), order);
}
public void testOverrideOrderCalculatorSelfReference() {
@Test
public void overrideOrderCalculatorSelfReference() {
EnvVars env = new EnvVars();
EnvVars overrides = new EnvVars();
overrides.put("PATH", "some;${PATH}");
OverrideOrderCalculator calc = new OverrideOrderCalculator(env, overrides);
List<String> order = calc.getOrderedVariableNames();
assertEquals(Arrays.asList("PATH"), order);
}
public void testOverrideOrderCalculatorCyclic() {
@Test
public void overrideOrderCalculatorCyclic() {
EnvVars env = new EnvVars();
env.put("C", "Existing");
EnvVars overrides = new EnvVars();
overrides.put("A", "${B}");
overrides.put("B", "${C}"); // This will be ignored.
overrides.put("C", "${A}");
overrides.put("D", "${C}${E}");
overrides.put("E", "${C}${D}");
OverrideOrderCalculator calc = new OverrideOrderCalculator(env, overrides);
List<String> order = calc.getOrderedVariableNames();
assertEquals(Arrays.asList("B", "A", "C"), order.subList(0, 3));
......
......@@ -27,13 +27,7 @@ import hudson.FilePath.TarCompression;
import hudson.model.TaskListener;
import hudson.remoting.VirtualChannel;
import hudson.util.NullStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Chmod;
import org.jvnet.hudson.test.Bug;
import org.mockito.Mockito;
import hudson.util.StreamTaskListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
......@@ -41,6 +35,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
......@@ -56,18 +51,29 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Chmod;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.jvnet.hudson.test.Issue;
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
/**
* @author Kohsuke Kawaguchi
*/
public class FilePathTest extends ChannelTestCase {
public class FilePathTest {
public void testCopyTo() throws Exception {
File tmp = File.createTempFile("testCopyTo","");
FilePath f = new FilePath(french,tmp.getPath());
@Rule public ChannelRule channels = new ChannelRule();
@Rule public TemporaryFolder temp = new TemporaryFolder();
@Test public void copyTo() throws Exception {
File tmp = temp.newFile();
FilePath f = new FilePath(channels.french, tmp.getPath());
f.copyTo(new NullStream());
assertTrue("target does not exist", tmp.exists());
assertTrue("could not delete target " + tmp.getPath(), tmp.delete());
......@@ -79,12 +85,12 @@ public class FilePathTest extends ChannelTestCase {
*/
// TODO: this test is much too slow to be a traditional unit test. Should be extracted into some stress test
// which is no part of the default test harness?
public void testNoFileLeakInCopyTo() throws Exception {
@Test public void noFileLeakInCopyTo() throws Exception {
for (int j=0; j<2500; j++) {
File tmp = File.createTempFile("testCopyFrom","");
File tmp = temp.newFile();
FilePath f = new FilePath(tmp);
File tmp2 = File.createTempFile("testCopyTo","");
FilePath f2 = new FilePath(british,tmp2.getPath());
File tmp2 = temp.newFile();
FilePath f2 = new FilePath(channels.british, tmp2.getPath());
f.copyTo(f2);
......@@ -105,11 +111,10 @@ public class FilePathTest extends ChannelTestCase {
*
* Also see JENKINS-7897
*/
@Bug(7871)
public void testNoRaceConditionInCopyTo() throws Exception {
final File tmp = File.createTempFile("testNoRaceConditionInCopyTo","");
@Issue("JENKINS-7871")
@Test public void noRaceConditionInCopyTo() throws Exception {
final File tmp = temp.newFile();
try {
int fileSize = 90000;
givenSomeContentInFile(tmp, fileSize);
......@@ -119,9 +124,6 @@ public class FilePathTest extends ChannelTestCase {
// THEN copied count was always equal the expected size
for (Future<Integer> f : results)
assertEquals(fileSize,f.get().intValue());
} finally {
tmp.delete();
}
}
private void givenSomeContentInFile(File file, int size) throws IOException {
......@@ -173,7 +175,7 @@ public class FilePathTest extends ChannelTestCase {
}
}
FilePath f = new FilePath(french, file.getPath());
FilePath f = new FilePath(channels.french, file.getPath());
Sink sink = new Sink();
f.copyTo(sink);
return sink.count;
......@@ -189,31 +191,24 @@ public class FilePathTest extends ChannelTestCase {
}
}
public void testRepeatCopyRecursiveTo() throws Exception {
@Test public void repeatCopyRecursiveTo() throws Exception {
// local->local copy used to return 0 if all files were "up to date"
// should return number of files processed, whether or not they were copied or already current
File tmp = Util.createTempDir(), src = new File(tmp, "src"), dst = new File(tmp, "dst");
try {
assertTrue(src.mkdir());
assertTrue(dst.mkdir());
File src = temp.newFolder("src");
File dst = temp.newFolder("dst");
File.createTempFile("foo", ".tmp", src);
FilePath fp = new FilePath(src);
assertEquals(1, fp.copyRecursiveTo(new FilePath(dst)));
// copy again should still report 1
assertEquals(1, fp.copyRecursiveTo(new FilePath(dst)));
} finally {
Util.deleteRecursive(tmp);
}
}
@Bug(9540)
public void testErrorMessageInRemoteCopyRecursive() throws Exception {
File tmp = Util.createTempDir();
try {
File src = new File(tmp, "src");
File dst = new File(tmp, "dst");
@Issue("JENKINS-9540")
@Test public void errorMessageInRemoteCopyRecursive() throws Exception {
File src = temp.newFolder("src");
File dst = temp.newFolder("dst");
FilePath from = new FilePath(src);
FilePath to = new FilePath(british, dst.getAbsolutePath());
FilePath to = new FilePath(channels.british, dst.getAbsolutePath());
for (int i = 0; i < 10000; i++) {
// TODO is there a simpler way to force the TarOutputStream to be flushed and the reader to start?
// Have not found a way to make the failure guaranteed.
......@@ -241,24 +236,17 @@ public class FilePathTest extends ChannelTestCase {
} finally {
toF.chmod(700);
}
} finally {
Util.deleteRecursive(tmp);
}
}
public void testArchiveBug4039() throws Exception {
File tmp = Util.createTempDir();
try {
FilePath d = new FilePath(french,tmp.getPath());
@Issue("JENKINS-4039")
@Test public void archiveBug() throws Exception {
FilePath d = new FilePath(channels.french, temp.getRoot().getPath());
d.child("test").touch(0);
d.zip(new NullOutputStream());
d.zip(new NullOutputStream(),"**/*");
} finally {
Util.deleteRecursive(tmp);
}
}
public void testNormalization() throws Exception {
@Test public void normalization() throws Exception {
compare("abc/def\\ghi","abc/def\\ghi"); // allow mixed separators
{// basic '.' trimming
......@@ -285,7 +273,7 @@ public class FilePathTest extends ChannelTestCase {
compare("abc/..",".");
compare(".",".");
// @Bug(5951)
// @Issue("JENKINS-5951")
compare("C:\\Hudson\\jobs\\foo\\workspace/../../otherjob/workspace/build.xml",
"C:\\Hudson\\jobs/otherjob/workspace/build.xml");
// Other cases that failed before
......@@ -311,8 +299,8 @@ public class FilePathTest extends ChannelTestCase {
assertEquals(answer,new FilePath((VirtualChannel)null,original).getRemote());
}
// @Bug(6494)
public void testGetParent() throws Exception {
@Issue("JENKINS-6494")
@Test public void getParent() throws Exception {
FilePath fp = new FilePath((VirtualChannel)null, "/abc/def");
assertEquals("/abc", (fp = fp.getParent()).getRemote());
assertEquals("/", (fp = fp.getParent()).getRemote());
......@@ -338,9 +326,8 @@ public class FilePathTest extends ChannelTestCase {
return new FilePath(building);
}
public void testList() throws Exception {
File baseDir = Util.createTempDir();
try {
@Test public void list() throws Exception {
File baseDir = temp.getRoot();
final Set<FilePath> expected = new HashSet<FilePath>();
expected.add(createFilePath(baseDir, "top", "sub", "app.log"));
expected.add(createFilePath(baseDir, "top", "sub", "trace.log"));
......@@ -348,14 +335,10 @@ public class FilePathTest extends ChannelTestCase {
expected.add(createFilePath(baseDir, "top", "db", "trace.log"));
final FilePath[] result = new FilePath(baseDir).list("**");
assertEquals(expected, new HashSet<FilePath>(Arrays.asList(result)));
} finally {
Util.deleteRecursive(baseDir);
}
}
public void testListWithExcludes() throws Exception {
File baseDir = Util.createTempDir();
try {
@Test public void listWithExcludes() throws Exception {
File baseDir = temp.getRoot();
final Set<FilePath> expected = new HashSet<FilePath>();
expected.add(createFilePath(baseDir, "top", "sub", "app.log"));
createFilePath(baseDir, "top", "sub", "trace.log");
......@@ -363,14 +346,10 @@ public class FilePathTest extends ChannelTestCase {
createFilePath(baseDir, "top", "db", "trace.log");
final FilePath[] result = new FilePath(baseDir).list("**", "**/trace.log");
assertEquals(expected, new HashSet<FilePath>(Arrays.asList(result)));
} finally {
Util.deleteRecursive(baseDir);
}
}
public void testListWithDefaultExcludes() throws Exception {
File baseDir = Util.createTempDir();
try {
@Test public void listWithDefaultExcludes() throws Exception {
File baseDir = temp.getRoot();
final Set<FilePath> expected = new HashSet<FilePath>();
expected.add(createFilePath(baseDir, "top", "sub", "backup~"));
expected.add(createFilePath(baseDir, "top", "CVS", "somefile,v"));
......@@ -379,13 +358,10 @@ public class FilePathTest extends ChannelTestCase {
assertEquals(0, new FilePath(baseDir).list("**", "").length);
final FilePath[] result = new FilePath(baseDir).list("**", "", false);
assertEquals(expected, new HashSet<FilePath>(Arrays.asList(result)));
} finally {
Util.deleteRecursive(baseDir);
}
}
@Bug(11073)
public void testIsUnix() {
@Issue("JENKINS-11073")
@Test public void isUnix() {
VirtualChannel dummy = Mockito.mock(VirtualChannel.class);
FilePath winPath = new FilePath(dummy,
" c:\\app\\hudson\\workspace\\3.8-jelly-db\\jdk/jdk1.6.0_21/label/sqlserver/profile/sqlserver\\acceptance-tests\\distribution.zip");
......@@ -408,9 +384,8 @@ public class FilePathTest extends ChannelTestCase {
* Also tries to check that a problem with setting the last-modified date on Windows doesn't fail the whole copy
* - well at least when running this test on a Windows OS. See JENKINS-11073
*/
public void testCopyToWithPermission() throws IOException, InterruptedException {
File tmp = Util.createTempDir();
try {
@Test public void copyToWithPermission() throws IOException, InterruptedException {
File tmp = temp.getRoot();
File child = new File(tmp,"child");
FilePath childP = new FilePath(child);
childP.touch(4711);
......@@ -421,7 +396,7 @@ public class FilePathTest extends ChannelTestCase {
chmodTask.setPerm("0400");
chmodTask.execute();
FilePath copy = new FilePath(british,tmp.getPath()).child("copy");
FilePath copy = new FilePath(channels.british, tmp.getPath()).child("copy");
childP.copyToWithPermission(copy);
assertEquals(childP.mode(),copy.mode());
......@@ -433,19 +408,15 @@ public class FilePathTest extends ChannelTestCase {
// Windows seems to have random failures when setting the timestamp on newly generated
// files. So test that:
for (int i=0; i<100; i++) {
copy = new FilePath(british,tmp.getPath()).child("copy"+i);
copy = new FilePath(channels.british, tmp.getPath()).child("copy"+i);
childP.copyToWithPermission(copy);
}
} finally {
Util.deleteRecursive(tmp);
}
}
public void testSymlinkInTar() throws Exception {
@Test public void symlinkInTar() throws Exception {
if (Functions.isWindows()) return; // can't test on Windows
FilePath tmp = new FilePath(Util.createTempDir());
try {
FilePath tmp = new FilePath(temp.getRoot());
FilePath in = tmp.child("in");
in.mkdirs();
in.child("c").touch(0);
......@@ -458,13 +429,10 @@ public class FilePathTest extends ChannelTestCase {
tar.untar(dst, TarCompression.NONE);
assertEquals("c",dst.child("b").readLink());
} finally {
tmp.deleteRecursive();
}
}
@Bug(13649)
public void testMultiSegmentRelativePaths() throws Exception {
@Issue("JENKINS-13649")
@Test public void multiSegmentRelativePaths() throws Exception {
VirtualChannel d = Mockito.mock(VirtualChannel.class);
FilePath winPath = new FilePath(d, "c:\\app\\jenkins\\workspace");
FilePath nixPath = new FilePath(d, "/opt/jenkins/workspace");
......@@ -477,10 +445,9 @@ public class FilePathTest extends ChannelTestCase {
assertEquals("/opt/jenkins/workspace/foo/bar/manchu", new FilePath(nixPath, "foo/bar/manchu").getRemote());
}
public void testValidateAntFileMask() throws Exception {
File tmp = Util.createTempDir();
try {
FilePath d = new FilePath(french, tmp.getPath());
@Test public void validateAntFileMask() throws Exception {
File tmp = temp.getRoot();
FilePath d = new FilePath(channels.french, tmp.getPath());
d.child("d1/d2/d3").mkdirs();
d.child("d1/d2/d3/f.txt").touch(0);
d.child("d1/d2/d3/f.html").touch(0);
......@@ -492,9 +459,6 @@ public class FilePathTest extends ChannelTestCase {
assertValidateAntFileMask(Messages.FilePath_validateAntFileMask_doesntMatchAnything("index.htm"), d, "index.htm");
assertValidateAntFileMask(Messages.FilePath_validateAntFileMask_doesntMatchAndSuggest("f.html", "d1/d2/d3/f.html"), d, "f.html");
// TODO lots more to test, e.g. multiple patterns separated by commas; ought to have full code coverage for this method
} finally {
Util.deleteRecursive(tmp);
}
}
@SuppressWarnings("deprecation")
......@@ -504,10 +468,9 @@ public class FilePathTest extends ChannelTestCase {
@Issue("JENKINS-7214")
@SuppressWarnings("deprecation")
public void testValidateAntFileMaskBounded() throws Exception {
File tmp = Util.createTempDir();
try {
FilePath d = new FilePath(french, tmp.getPath());
@Test public void validateAntFileMaskBounded() throws Exception {
File tmp = temp.getRoot();
FilePath d = new FilePath(channels.french, tmp.getPath());
FilePath d2 = d.child("d1/d2");
d2.mkdirs();
for (int i = 0; i < 100; i++) {
......@@ -524,16 +487,12 @@ public class FilePathTest extends ChannelTestCase {
} catch (InterruptedException x) {
// good
}
} finally {
Util.deleteRecursive(tmp);
}
}
@Bug(15418)
public void testDeleteLongPathOnWindows() throws Exception {
File tmp = Util.createTempDir();
try {
FilePath d = new FilePath(french, tmp.getPath());
@Issue("JENKINS-15418")
@Test public void deleteLongPathOnWindows() throws Exception {
File tmp = temp.getRoot();
FilePath d = new FilePath(channels.french, tmp.getPath());
// construct a very long path
StringBuilder sb = new StringBuilder();
......@@ -551,16 +510,11 @@ public class FilePathTest extends ChannelTestCase {
Util.deleteRecursive(firstDirectory);
assertFalse("Could not delete directory!", firstDirectory.exists());
} finally {
Util.deleteRecursive(tmp);
}
}
@Bug(16215)
public void testInstallIfNecessaryAvoidsExcessiveDownloadsByUsingIfModifiedSince() throws Exception {
final File tmp = Util.createTempDir();
try {
@Issue("JENKINS-16215")
@Test public void installIfNecessaryAvoidsExcessiveDownloadsByUsingIfModifiedSince() throws Exception {
File tmp = temp.getRoot();
final FilePath d = new FilePath(tmp);
d.child(".timestamp").touch(123000);
......@@ -574,15 +528,11 @@ public class FilePathTest extends ChannelTestCase {
assertFalse(d.installIfNecessaryFrom(url, null, null));
verify(con).setIfModifiedSince(123000);
} finally {
Util.deleteRecursive(tmp);
}
}
@Bug(16215)
public void testInstallIfNecessaryPerformsInstallation() throws Exception {
final File tmp = Util.createTempDir();
try {
@Issue("JENKINS-16215")
@Test public void installIfNecessaryPerformsInstallation() throws Exception {
File tmp = temp.getRoot();
final FilePath d = new FilePath(tmp);
final HttpURLConnection con = mock(HttpURLConnection.class);
......@@ -595,9 +545,25 @@ public class FilePathTest extends ChannelTestCase {
.thenReturn(someZippedContent());
assertTrue(d.installIfNecessaryFrom(url, null, null));
} finally {
Util.deleteRecursive(tmp);
}
}
@Issue("JENKINS-26196")
@Test public void installIfNecessarySkipsDownloadWhenErroneous() throws Exception {
File tmp = temp.getRoot();
final FilePath d = new FilePath(tmp);
d.child(".timestamp").touch(123000);
final HttpURLConnection con = mock(HttpURLConnection.class);
final URL url = someUrlToZipFile(con);
when(con.getResponseCode()).thenReturn(HttpURLConnection.HTTP_GATEWAY_TIMEOUT);
when(con.getResponseMessage()).thenReturn("Gateway Timeout");
when(con.getInputStream()).thenThrow(new ConnectException());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String message = "going ahead";
assertFalse(d.installIfNecessaryFrom(url, new StreamTaskListener(baos), message));
verify(con).setIfModifiedSince(123000);
String log = baos.toString();
assertFalse(log, log.contains(message));
assertTrue(log, log.contains("504 Gateway Timeout"));
}
private URL someUrlToZipFile(final URLConnection con) throws IOException {
......@@ -622,11 +588,9 @@ public class FilePathTest extends ChannelTestCase {
return new ByteArrayInputStream(buf.toByteArray());
}
@Bug(16846)
public void testMoveAllChildrenTo() throws IOException, InterruptedException {
final File tmp = Util.createTempDir();
try
{
@Issue("JENKINS-16846")
@Test public void moveAllChildrenTo() throws IOException, InterruptedException {
File tmp = temp.getRoot();
final String dirname = "sub";
final File top = new File(tmp, "test");
final File sub = new File(top, dirname);
......@@ -644,10 +608,5 @@ public class FilePathTest extends ChannelTestCase {
// test conflict subdir
src.moveAllChildrenTo(dst);
}
finally
{
Util.deleteRecursive(tmp);
}
}
}
......@@ -40,7 +40,7 @@ import static org.junit.Assert.*;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.Issue;
import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
......@@ -67,7 +67,7 @@ public class FunctionsTest {
}
@Test
@Bug(7725)
@Issue("JENKINS-7725")
public void testGetActionUrl_absoluteUriWithoutAuthority(){
String[] uris = {
"mailto:nobody@example.com",
......@@ -190,7 +190,7 @@ public class FunctionsTest {
assertEquals("job/i/", result);
}
@Bug(17713)
@Issue("JENKINS-17713")
@PrepareForTest({Stapler.class, Jenkins.class})
@Test public void getRelativeLinkTo_MavenModules() throws Exception {
Jenkins j = createMockJenkins();
......@@ -285,7 +285,7 @@ public class FunctionsTest {
}
@Test
@Bug(16630)
@Issue("JENKINS-16630")
public void testHumanReadableFileSize(){
Locale defaultLocale = Locale.getDefault();
try{
......@@ -303,7 +303,7 @@ public class FunctionsTest {
}
}
@Bug(17030)
@Issue("JENKINS-17030")
@Test
public void testBreakableString() {
......@@ -332,7 +332,7 @@ public class FunctionsTest {
);
}
@Bug(20800)
@Issue("JENKINS-20800")
@Test public void printLogRecordHtml() throws Exception {
LogRecord lr = new LogRecord(Level.INFO, "Bad input <xml/>");
lr.setLoggerName("test");
......
......@@ -29,26 +29,31 @@ import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.util.ProcessTree;
import hudson.util.StreamTaskListener;
import java.io.ByteArrayOutputStream;
import java.io.File;
import jenkins.security.MasterToSlaveCallable;
import org.apache.commons.io.FileUtils;
import org.jvnet.hudson.test.Bug;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.jvnet.hudson.test.Issue;
import java.io.ByteArrayOutputStream;
import java.io.File;
public class LauncherTest {
@Rule public ChannelRule channels = new ChannelRule();
@Rule public TemporaryFolder temp = new TemporaryFolder();
public class LauncherTest extends ChannelTestCase {
@Bug(4611)
public void testRemoteKill() throws Exception {
@Issue("JENKINS-4611")
@Test public void remoteKill() throws Exception {
if (File.pathSeparatorChar != ':') {
System.err.println("Skipping, currently Unix-specific test");
return;
}
File tmp = File.createTempFile("testRemoteKill", "");
tmp.delete();
File tmp = temp.newFile();
try {
FilePath f = new FilePath(french, tmp.getPath());
FilePath f = new FilePath(channels.french, tmp.getPath());
Launcher l = f.createLauncher(StreamTaskListener.fromStderr());
Proc p = l.launch().cmds("sh", "-c", "echo $$$$ > "+tmp+"; sleep 30").stdout(System.out).stderr(System.err).start();
while (!tmp.exists())
......@@ -58,7 +63,7 @@ public class LauncherTest extends ChannelTestCase {
assertTrue(p.join()!=0);
long end = System.currentTimeMillis();
assertTrue("join finished promptly", (end - start < 15000));
french.call(NOOP); // this only returns after the other side of the channel has finished executing cancellation
channels.french.call(NOOP); // this only returns after the other side of the channel has finished executing cancellation
Thread.sleep(2000); // more delay to make sure it's gone
assertNull("process should be gone",ProcessTree.get().get(Integer.parseInt(FileUtils.readFileToString(tmp).trim())));
......@@ -69,9 +74,6 @@ public class LauncherTest extends ChannelTestCase {
// hudson.model.Hudson.instance.nodes[0].rootPath.createLauncher(new hudson.util.StreamTaskListener(System.err)).
// launch().cmds("sleep", "1d").stdout(System.out).stderr(System.err).start().kill()
// hangs and on slave machine pgrep sleep => one process; after manual kill, script returns.
} finally {
tmp.delete();
}
}
private static final Callable<Object,RuntimeException> NOOP = new MasterToSlaveCallable<Object,RuntimeException>() {
......@@ -80,8 +82,8 @@ public class LauncherTest extends ChannelTestCase {
}
};
@Bug(15733)
public void testDecorateByEnv() throws Exception {
@Issue("JENKINS-15733")
@Test public void decorateByEnv() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
TaskListener l = new StreamBuildListener(baos);
Launcher base = new Launcher.LocalLauncher(l);
......@@ -93,8 +95,8 @@ public class LauncherTest extends ChannelTestCase {
assertTrue(log, log.contains("val1 val2"));
}
@Bug(18368)
public void testDecoratedByEnvMaintainsIsUnix() throws Exception {
@Issue("JENKINS-18368")
@Test public void decoratedByEnvMaintainsIsUnix() throws Exception {
ByteArrayOutputStream output = new ByteArrayOutputStream();
TaskListener listener = new StreamBuildListener(output);
Launcher remoteLauncher = new Launcher.RemoteLauncher(listener, FilePath.localChannel, false);
......@@ -105,8 +107,8 @@ public class LauncherTest extends ChannelTestCase {
assertEquals(true, decorated.isUnix());
}
@Bug(18368)
public void testDecoratedByPrefixMaintainsIsUnix() throws Exception {
@Issue("JENKINS-18368")
@Test public void decoratedByPrefixMaintainsIsUnix() throws Exception {
ByteArrayOutputStream output = new ByteArrayOutputStream();
TaskListener listener = new StreamBuildListener(output);
Launcher remoteLauncher = new Launcher.RemoteLauncher(listener, FilePath.localChannel, false);
......
/*
* The MIT License
*
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
*
* 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
......@@ -23,8 +23,10 @@
*/
package hudson;
import junit.framework.TestCase;
import static org.junit.Assert.assertEquals;
import hudson.MarkupText.SubText;
import org.junit.Test;
import java.util.List;
import java.util.regex.Pattern;
......@@ -32,7 +34,9 @@ import java.util.regex.Pattern;
/**
* @author Kohsuke Kawaguchi
*/
public class MarkupTextTest extends TestCase {
public class MarkupTextTest {
@Test
public void test1() {
MarkupText t = new MarkupText("I fixed issue #155. The rest is trick text: xissue #155 issue #123x");
for (SubText st : t.findTokens(pattern)) {
......@@ -43,7 +47,8 @@ public class MarkupTextTest extends TestCase {
assertEquals("I fixed <155>issue #155<155>. The rest is trick text: xissue #155 issue #123x", t.toString(false));
}
public void testBoundary() {
@Test
public void boundary() {
MarkupText t = new MarkupText("issue #155---issue #123");
for (SubText st : t.findTokens(pattern))
st.surroundWith("<$1>","<$1>");
......@@ -51,7 +56,8 @@ public class MarkupTextTest extends TestCase {
assertEquals("<155>issue #155<155>---<123>issue #123<123>", t.toString(false));
}
public void testFindTokensOnSubText() {
@Test
public void findTokensOnSubText() {
MarkupText t = new MarkupText("Fixed 2 issues in this commit, fixing issue 155, 145");
List<SubText> tokens = t.findTokens(Pattern.compile("issue .*"));
assertEquals("Expected one token", 1, tokens.size());
......@@ -62,7 +68,8 @@ public class MarkupTextTest extends TestCase {
assertEquals("Fixed 2 issues in this commit, fixing issue <155>155<155>, <145>145<145>", t.toString(false));
}
public void testLiteralTextSurround() {
@Test
public void literalTextSurround() {
MarkupText text = new MarkupText("AAA test AAA");
for(SubText token : text.findTokens(Pattern.compile("AAA"))) {
token.surroundWithLiteral("$9","$9");
......@@ -73,7 +80,8 @@ public class MarkupTextTest extends TestCase {
/**
* Start/end tag nesting should be correct regardless of the order tags are added.
*/
public void testAdjacent() {
@Test
public void adjacent() {
MarkupText text = new MarkupText("abcdef");
text.addMarkup(0,3,"$","$");
text.addMarkup(3,6,"#","#");
......@@ -85,7 +93,8 @@ public class MarkupTextTest extends TestCase {
assertEquals("$abc$#def#",text.toString(false));
}
public void testEscape() {
@Test
public void escape() {
MarkupText text = new MarkupText("&&&");
assertEquals("&amp;&amp;&amp;",text.toString(false));
......@@ -94,15 +103,17 @@ public class MarkupTextTest extends TestCase {
assertEquals("&amp;<foo>&amp;&nbsp;&amp;",text.toString(false));
}
public void testPreEscape() {
@Test
public void preEscape() {
MarkupText text = new MarkupText("Line\n2 & 3\n<End>\n");
assertEquals("Line\n2 &amp; 3\n&lt;End&gt;\n", text.toString(true));
text.addMarkup(4, "<hr/>");
assertEquals("Line<hr/>\n2 &amp; 3\n&lt;End&gt;\n", text.toString(true));
}
/* @Bug(6252) */
public void testSubTextSubText() {
/* @Issue("JENKINS-6252") */
@Test
public void subTextSubText() {
MarkupText text = new MarkupText("abcdefgh");
SubText sub = text.subText(2, 7);
assertEquals("cdefg", sub.getText());
......
......@@ -23,13 +23,16 @@
*/
package hudson;
import junit.framework.TestCase;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import java.net.Proxy;
public class ProxyConfigurationTest extends TestCase {
public class ProxyConfigurationTest {
public void testNoProxyHost() {
@Test
public void noProxyHost() {
String noProxyHost = "*.example.com|192.168.*";
assertEquals(Proxy.Type.HTTP, ProxyConfiguration.createProxy("test.example.co.jp", "proxy.example.com", 8080, noProxyHost).type());
assertEquals(Proxy.Type.DIRECT, ProxyConfiguration.createProxy("test.example.com", "proxy.example.com", 8080, noProxyHost).type());
......
......@@ -35,7 +35,7 @@ import java.io.IOException;
import static org.junit.Assert.*;
import org.junit.Assume;
import org.junit.Test;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.Issue;
import hudson.util.StreamTaskListener;
import java.io.FileOutputStream;
......@@ -281,7 +281,7 @@ public class UtilTest {
* Compute 'known-correct' digests and see if I still get them when computed concurrently
* to another digest.
*/
@Bug(10346)
@Issue("JENKINS-10346")
@Test
public void testDigestThreadSafety() throws InterruptedException {
String a = "abcdefgh";
......
......@@ -68,7 +68,7 @@ public class ListJobsCommandTest {
/*
@Test
@Bug(18393)
@Issue("JENKINS-18393")
public void failForMatrixProject() throws Exception {
final MatrixProject matrix = mock(MatrixProject.class);
......
......@@ -28,7 +28,7 @@ import hudson.MarkupText;
import org.junit.Test;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.Issue;
/**
* @author Alan Harder
......@@ -59,7 +59,7 @@ public class UrlAnnotatorTest {
}
@Test
@Bug(19866)
@Issue("JENKINS-19866")
public void annotateFileScheme() {
assertEquals(
"Get this <a href='file://here/in/this/folder/'>file://here/in/this/folder/</a>.",
......
......@@ -29,11 +29,11 @@ import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.junit.Test;
import static org.junit.Assert.*;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.Issue;
public class LogRecorderTest {
@Bug(17983)
@Issue("JENKINS-17983")
@Test public void targetIncludes() {
assertTrue(includes("hudson", "hudson"));
assertFalse(includes("hudson", "hudsone"));
......
......@@ -3,9 +3,11 @@
*/
package hudson.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.util.Collection;
import org.junit.Assert;
import org.junit.Test;
/**
......@@ -42,7 +44,7 @@ public class AbstractItemTest {
final String displayName = "testDisplayName";
StubAbstractItem i = new StubAbstractItem();
i.setDisplayName(displayName);
Assert.assertEquals(displayName, i.getDisplayName());
assertEquals(displayName, i.getDisplayName());
}
@Test
......@@ -51,7 +53,7 @@ public class AbstractItemTest {
StubAbstractItem i = new StubAbstractItem();
i.doSetName(name);
// assert that if the displayname is not set, the name is actually returned
Assert.assertEquals(name, i.getDisplayName());
assertEquals(name, i.getDisplayName());
}
......@@ -61,7 +63,7 @@ public class AbstractItemTest {
StubAbstractItem i = new StubAbstractItem();
i.doSetName(name);
Assert.assertEquals(i.getName(), i.getSearchName());
assertEquals(i.getName(), i.getSearchName());
}
@Test
......@@ -70,11 +72,11 @@ public class AbstractItemTest {
final String displayName = "displayName";
StubAbstractItem i = new StubAbstractItem();
i.doSetName(projectName);
Assert.assertEquals(projectName, i.getName());
Assert.assertNull(i.getDisplayNameOrNull());
assertEquals(projectName, i.getName());
assertNull(i.getDisplayNameOrNull());
i.setDisplayName(displayName);
Assert.assertEquals(displayName, i.getDisplayNameOrNull());
assertEquals(displayName, i.getDisplayNameOrNull());
}
@Test
......@@ -83,10 +85,10 @@ public class AbstractItemTest {
final String displayName = "displayName";
StubAbstractItem i = new StubAbstractItem();
i.doSetName(projectName);
Assert.assertNull(i.getDisplayNameOrNull());
assertNull(i.getDisplayNameOrNull());
i.setDisplayNameOrNull(displayName);
Assert.assertEquals(displayName, i.getDisplayNameOrNull());
Assert.assertEquals(displayName, i.getDisplayName());
assertEquals(displayName, i.getDisplayNameOrNull());
assertEquals(displayName, i.getDisplayName());
}
}
package hudson.model;
import junit.framework.TestCase;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* @author Kohsuke Kawaguchi
*/
public class BallColorTest extends TestCase {
public void testHtmlColor() {
public class BallColorTest {
@Test
public void htmlColor() {
assertEquals("#EF2929",BallColor.RED.getHtmlBaseColor());
}
public void testIconClassName() {
@Test
public void iconClassName() {
assertEquals("icon-red",BallColor.RED.getIconClassName());
assertEquals("icon-aborted-anime",BallColor.ABORTED_ANIME.getIconClassName());
}
......
......@@ -23,7 +23,9 @@
*/
package hudson.model;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
public class DisplayNameListenerTest {
......@@ -38,11 +40,11 @@ public class DisplayNameListenerTest {
dest.setDisplayName("this should be cleared");
// make sure the displayname and the name are different at this point
Assert.assertFalse(dest.getName().equals(dest.getDisplayName()));
assertFalse(dest.getName().equals(dest.getDisplayName()));
listener.onCopied(src, dest);
// make sure the displayname is equals to the name as it should be null
Assert.assertEquals(dest.getName(), dest.getDisplayName());
assertEquals(dest.getName(), dest.getDisplayName());
}
@Test
......@@ -56,7 +58,7 @@ public class DisplayNameListenerTest {
listener.onRenamed(src, oldName, newName);
Assert.assertEquals(newName, src.getDisplayName());
assertEquals(newName, src.getDisplayName());
}
@Test
......@@ -72,6 +74,6 @@ public class DisplayNameListenerTest {
listener.onRenamed(src, oldName, oldName);
// make sure displayname is still intact
Assert.assertEquals(displayName, src.getDisplayName());
assertEquals(displayName, src.getDisplayName());
}
}
......@@ -24,8 +24,10 @@
package hudson.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import java.io.File;
import org.junit.Assert;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
......@@ -41,8 +43,8 @@ public class FileParameterValueTest {
final FileParameterValue param1 = new FileParameterValue(paramName, new File("ws_param1.txt"), "param1.txt");
final FileParameterValue param2 = new FileParameterValue(paramName, new File("ws_param2.txt"), "param2.txt");
Assert.assertNotEquals("Files with same locations shoud be considered as different", param1, param2);
Assert.assertNotEquals("Files with same locations shoud be considered as different", param2, param1);
assertNotEquals("Files with same locations shoud be considered as different", param1, param2);
assertNotEquals("Files with same locations shoud be considered as different", param2, param1);
}
@Test public void compareNullParams() {
......@@ -52,13 +54,13 @@ public class FileParameterValueTest {
FileParameterValue nullParam2 = new FileParameterValue(null, new File("null_param2.txt"), "null_param2.txt");
// Combine nulls
Assert.assertEquals(nullParam1, nullParam1);
Assert.assertEquals(nullParam1, nullParam2);
Assert.assertEquals(nullParam2, nullParam1);
Assert.assertEquals(nullParam2, nullParam2);
assertEquals(nullParam1, nullParam1);
assertEquals(nullParam1, nullParam2);
assertEquals(nullParam2, nullParam1);
assertEquals(nullParam2, nullParam2);
// Compare with non-null
Assert.assertNotEquals(nullParam1, nonNullParam);
Assert.assertNotEquals(nonNullParam, nullParam1);
assertNotEquals(nullParam1, nonNullParam);
assertNotEquals(nonNullParam, nullParam1);
}
}
package hudson.model;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
......@@ -16,7 +15,7 @@ public class JobTest {
j.setDisplayNameOrNull(displayName);
// make sure the displayname has been set
Assert.assertEquals(displayName, j.getDisplayName());
assertEquals(displayName, j.getDisplayName());
}
@Test
......@@ -26,6 +25,6 @@ public class JobTest {
j.setDisplayNameOrNull("");
// make sure the getDisplayName returns the project name
Assert.assertEquals(StubJob.DEFAULT_STUB_JOB_NAME, j.getDisplayName());
assertEquals(StubJob.DEFAULT_STUB_JOB_NAME, j.getDisplayName());
}
}
......@@ -24,10 +24,10 @@
package hudson.model;
import hudson.model.MultiStageTimeSeries.TimeScale;
import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;
import org.jfree.chart.JFreeChart;
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
......@@ -38,8 +38,10 @@ import java.io.IOException;
/**
* @author Kohsuke Kawaguchi
*/
public class LoadStatisticsTest extends TestCase {
public void testGraph() throws IOException {
public class LoadStatisticsTest {
@Test
public void graph() throws IOException {
LoadStatistics ls = new LoadStatistics(0, 0) {
public int computeIdleExecutors() {
throw new UnsupportedOperationException();
......
......@@ -11,7 +11,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import org.junit.runner.RunWith;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.Issue;
import static org.powermock.api.mockito.PowerMockito.mock;
import org.powermock.modules.junit4.PowerMockRunner;
......@@ -88,7 +88,7 @@ public class ParametersActionTest {
}
@Test
@Bug(15094)
@Issue("JENKINS-15094")
public void checkNullParamaterValues() {
SubTask subtask = mock(SubTask.class);
Build build = mock(Build.class);
......
/*
* The MIT License
*
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Stephen Connolly
*
*
* 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
......@@ -23,14 +23,19 @@
*/
package hudson.model;
import junit.framework.TestCase;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Random;
import org.junit.Before;
import org.junit.Test;
/**
* @author Stephen Connolly
*/
public class ResourceListTest extends TestCase {
public class ResourceListTest {
private Resource a1, a2, a3, a4, a;
private Resource b1, b2, b3, b4, b;
private Resource c1, c2, c3, c4, c;
......@@ -41,6 +46,7 @@ public class ResourceListTest extends TestCase {
private ResourceList y;
private ResourceList z;
@Before
public void setUp() {
entropy = new Random(0);
a = new Resource("A" + entropy.nextLong());
......@@ -67,7 +73,8 @@ public class ResourceListTest extends TestCase {
z = new ResourceList();
}
public void testEmptyLists() throws Exception {
@Test
public void emptyLists() {
z.r(a);
ResourceList w = new ResourceList();
w.w(a);
......@@ -79,7 +86,8 @@ public class ResourceListTest extends TestCase {
assertFalse("Write vs Empty", w.isCollidingWith(x));
}
public void testSimpleR() throws Exception {
@Test
public void simpleR() {
x.r(a);
y.r(b);
z.r(a);
......@@ -92,7 +100,8 @@ public class ResourceListTest extends TestCase {
assertFalse("Read-Read", y.isCollidingWith(z));
}
public void testSimpleRW() throws Exception {
@Test
public void simpleRW() {
x.r(a);
y.r(b);
z.w(a);
......@@ -105,7 +114,8 @@ public class ResourceListTest extends TestCase {
assertFalse("Read-Write different resources", y.isCollidingWith(z));
}
public void testSimpleW() throws Exception {
@Test
public void simpleW() {
x.w(a);
y.w(b);
z.w(a);
......@@ -127,7 +137,8 @@ public class ResourceListTest extends TestCase {
assertTrue(z.isCollidingWith(w));
}
public void testParentChildR() throws Exception {
@Test
public void parentChildR() {
x.r(a1);
x.r(a2);
y.r(a3);
......@@ -141,7 +152,8 @@ public class ResourceListTest extends TestCase {
assertFalse("Reads should never conflict", y.isCollidingWith(z));
}
public void testParentChildW() throws Exception {
@Test
public void parentChildW() {
x.w(a1);
x.w(a2);
y.w(a3);
......@@ -155,7 +167,8 @@ public class ResourceListTest extends TestCase {
assertTrue("Taking parent resource assumes all children are taken too", y.isCollidingWith(z));
}
public void testParentChildR3() throws Exception {
@Test
public void parentChildR3() {
x.r(c1);
x.r(c2);
y.r(c3);
......@@ -169,7 +182,8 @@ public class ResourceListTest extends TestCase {
assertFalse("Reads should never conflict", y.isCollidingWith(z));
}
public void testParentChildW3() throws Exception {
@Test
public void parentChildW3() {
x.w(c1);
x.w(c2);
y.w(c3);
......@@ -199,7 +213,8 @@ public class ResourceListTest extends TestCase {
assertTrue("Total count = 4, limit is 3", x.isCollidingWith(v));
}
public void testMultiWrite1() throws Exception {
@Test
public void multiWrite1() {
y.w(e);
assertFalse(x.isCollidingWith(y));
assertFalse(y.isCollidingWith(x));
......@@ -217,7 +232,8 @@ public class ResourceListTest extends TestCase {
}
}
public void testMultiWriteN() throws Exception {
@Test
public void multiWriteN() {
y.w(f);
for (int i=0; i<f.numConcurrentWrite; i++) {
assertFalse("Total = W" + i + ", Limit = W" + f.numConcurrentWrite, x.isCollidingWith(y));
......@@ -232,7 +248,8 @@ public class ResourceListTest extends TestCase {
}
}
public void testMultiRead1() throws Exception {
@Test
public void multiRead1() {
y.r(e);
for (int i = 0; i < fWriteCount; i++) {
assertFalse("Total = R" + (i + 1) + ", Limit = W1", x.isCollidingWith(y));
......@@ -247,7 +264,8 @@ public class ResourceListTest extends TestCase {
}
}
public void testMultiReadN() throws Exception {
@Test
public void multiReadN() {
y.r(f);
for (int i = 0; i < fWriteCount; i++) {
assertFalse("Total = R" + (i + 1) + ", Limit = W" + fWriteCount, x.isCollidingWith(y));
......@@ -261,5 +279,4 @@ public class ResourceListTest extends TestCase {
x.r(f);
}
}
}
......@@ -24,12 +24,7 @@
package hudson.model;
import hudson.Util;
import hudson.model.Run.Artifact;
import hudson.util.StreamTaskListener;
import java.io.File;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.Callable;
......@@ -39,11 +34,11 @@ import java.util.concurrent.Executors;
import static org.junit.Assert.*;
import org.junit.Test;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.Issue;
public class RunTest {
@Bug(15816)
@Issue("JENKINS-15816")
@SuppressWarnings({"unchecked", "rawtypes"})
@Test public void timezoneOfID() throws Exception {
TimeZone origTZ = TimeZone.getDefault();
......@@ -87,32 +82,6 @@ public class RunTest {
}
@Bug(15587)
@Test
public void testParseTimestampFromBuildDir() throws Exception {
//Assume.assumeTrue(!Functions.isWindows() || (NTFS && JAVA7) || ...);
String buildDateTime = "2012-12-21_04-02-28";
int buildNumber = 155;
StreamTaskListener l = StreamTaskListener.fromStdout();
File tempDir = Util.createTempDir();
File buildDir = new File(tempDir, buildDateTime);
assertEquals(true, buildDir.mkdir());
File buildDirSymLink = new File(tempDir, Integer.toString(buildNumber));
try {
buildDir.mkdir();
Util.createSymlink(tempDir, buildDir.getAbsolutePath(), buildDirSymLink.getName(), l);
long time = Run.parseTimestampFromBuildDir(buildDirSymLink);
assertEquals(buildDateTime, Run.ID_FORMATTER.get().format(new Date(time)));
} finally {
Util.deleteRecursive(tempDir);
}
}
private List<? extends Run<?, ?>.Artifact> createArtifactList(String... paths) throws Exception {
Run r = new Run(new StubJob(), 0) {};
Run.ArtifactList list = r.new ArtifactList();
......
package hudson.model;
import junit.framework.TestCase;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import hudson.console.AnnotatedLargeText;
import hudson.security.ACL;
import hudson.security.Permission;
import hudson.security.PermissionGroup;
import org.acegisecurity.Authentication;
import org.junit.Test;
/**
* @author Jerome Lacoste
*/
public class TaskActionTest extends TestCase {
public class TaskActionTest {
private static class MyTaskThread extends TaskThread {
MyTaskThread(TaskAction taskAction) {
......@@ -21,22 +21,22 @@ public class TaskActionTest extends TestCase {
}
protected void perform(TaskListener listener) throws Exception {
listener.hyperlink("/localpath", "a link");
}
}
}
private static class MyTaskAction extends TaskAction {
void start() {
workerThread = new MyTaskThread(this);
workerThread.start();
}
}
public String getIconFileName() {
return "Iconfilename";
}
public String getDisplayName() {
return "My Task Thread";
}
public String getUrlName() {
return "xyz";
}
......@@ -53,7 +53,8 @@ public class TaskActionTest extends TestCase {
}
}
public void testAnnotatedText() throws Exception {
@Test
public void annotatedText() throws Exception {
MyTaskAction action = new MyTaskAction();
action.start();
AnnotatedLargeText annotatedText = action.obtainLog();
......
/*
* The MIT License
*
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
*
* 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
......@@ -23,16 +23,21 @@
*/
package hudson.model;
import junit.framework.TestCase;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
/**
* @author Kohsuke Kawaguchi
*/
public class TimeSeriesTest extends TestCase {
public class TimeSeriesTest {
@Test
public void test1() {
TimeSeries ts = new TimeSeries(0,1-0.1f,100);
float last;
assertEquals(0f,last=ts.getLatest());
float last = ts.getLatest();
assertEquals(0f,last, 0f);
for( int i=0; i<100; i++ ) {
assertEquals(ts.getHistory().length,i+1);
ts.update(1);
......
......@@ -13,7 +13,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.For;
import org.jvnet.hudson.test.Url;
......@@ -276,7 +276,7 @@ public class CronTabDayOfWeekLocaleTest {
}
@Test
@Bug(12357)
@Issue("JENKINS-12357")
public void isSundayAndNextRunIsNextSunday7() throws Exception {
final Calendar cal = Calendar.getInstance(locale);
cal.set(2011, 0, 16, 1, 0, 0); // Sunday, Jan 16th 2011, 01:00
......
......@@ -6,7 +6,7 @@ import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.For;
import java.text.DateFormat;
......@@ -42,7 +42,7 @@ public class CronTabEventualityTest {
}
@Test
@Bug(12388)
@Issue("JENKINS-12388")
public void testYearlyWillBeEventuallyTriggeredWithinOneYear() throws ANTLRException {
Calendar start = new GregorianCalendar(2012, 0, 11, 22, 33); // Jan 11th 2012 22:33
Calendar limit = createLimit(start, Calendar.YEAR, 1);
......@@ -50,7 +50,7 @@ public class CronTabEventualityTest {
}
@Test
@Bug(12388)
@Issue("JENKINS-12388")
public void testAnnuallyWillBeEventuallyTriggeredWithinOneYear() throws ANTLRException {
Calendar start = new GregorianCalendar(2012, 0, 11, 22, 33); // Jan 11th 2012 22:33
Calendar limit = createLimit(start, Calendar.YEAR, 1);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册