提交 cd075243 编写于 作者: C christ66

Merge remote-tracking branch 'upstream/master' into JENKINS-22811

Conflicts:
	test/src/test/java/hudson/model/ItemGroupMixInTest.java
......@@ -17,6 +17,12 @@ build
# vim
*~
# OS X
.DS_Store
# mvn versions:set
pom.xml.versionsBackup
war/images/16x16
war/images/24x24
war/images/32x32
......
......@@ -55,23 +55,76 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=>
<li class=bug>
FutureImpl does not cancel its start future.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25514">issue 25514</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.593>What's new in 1.593</a> <!--=DATE=--></h3>
<h3><a name=v1.597>What's new in 1.597</a> <!--=DATE=--></h3>
<ul class=image>
<li class=rfe>
Dynamic Single/Multi line Build History layout.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25381">issue 25381</a>,
<a href="https://issues.jenkins-ci.org/browse/JENKINS-25393">issue 25393</a>,
<a href="https://issues.jenkins-ci.org/browse/JENKINS-24687">issue 24687</a>,
<a href="https://issues.jenkins-ci.org/browse/JENKINS-24589">issue 24589</a>)
<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-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>)
<li class=bug>
Run parameters should show display name if set, rather than build numbers.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25174">issue 25174</a>)
<li class=bug>
Add range check for H(X-Y) syntax.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25897">issue 25897</a>)
</ul>
</div><!--=END=-->
<h3><a name=v1.593>What's new in 1.593</a> (2014/11/30)</h3>
<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.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-26155">issue 26155</a>)
<li class=bug>
Allow breaking label and node lists.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25989">issue 25989</a>)
</ul>
<h3><a name=v1.595>What's new in 1.595</a> (2014/12/21)</h3>
<ul class=image>
<li class=bug>
Spurious warnings in the log after deleting builds.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25788">issue 25788</a>)
<li class=bug>
Master labels disappear when system configuration is updated.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-23966">issue 23966</a>)
<li class=bug>
Updated icon-set dependency to version 1.0.5.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25499">issue 25499</a>,
<a href="https://issues.jenkins-ci.org/browse/JENKINS-25498">issue 25498</a>)
</ul>
<h3><a name=v1.594>What's new in 1.594</a> (2014/12/14)</h3>
<ul class=image>
<li class=bug>
After recent Java security updates, Jenkins would not gracefully recover from a deleted <code>secrets/master.key</code>.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25937">issue 25937</a>)
<li class=bug>
<i>Restrict where this project can be run</i> regressed in 1.589 when using the ClearCase plugin.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-25533">issue 25533</a>)
</ul>
<h3><a name=v1.593>What's new in 1.593</a> (2014/12/07)</h3>
<ul class=image>
<li class=rfe>
Dynamic Single/Multi line Build History layout.
......@@ -90,7 +143,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>
......@@ -102,7 +155,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>)
......@@ -111,7 +164,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>
......@@ -157,7 +210,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>
......@@ -177,22 +230,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>)
......@@ -446,7 +499,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
......@@ -589,7 +642,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>)
......@@ -602,22 +655,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>)
......@@ -967,7 +1020,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>
......@@ -1274,7 +1327,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.
......@@ -1344,7 +1397,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'>
......@@ -1934,7 +1987,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.
......@@ -2010,7 +2063,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>
......@@ -2042,7 +2095,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>
......@@ -2231,7 +2284,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.594-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>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
......@@ -22,27 +23,23 @@ 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.594-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>
<stapler.version>1.233</stapler.version>
<stapler.version>1.234</stapler.version>
<spring.version>2.5.6.SEC03</spring.version>
<groovy.version>1.8.9</groovy.version>
</properties>
......@@ -51,7 +48,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.jenkins-ci.plugins.icon-shim</groupId>
<artifactId>icon-set</artifactId>
<version>1.0.3</version>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
......@@ -862,7 +859,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 +877,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>
......
......@@ -110,7 +110,7 @@ throws ANTLRException
}
| ("H" "(")=> "H" "(" s=token "-" e=token ")" ( "/" d=token )?
{
bits = doHash(s,e,d);
bits = doHash(s,e,d,field);
}
| "H" ( "/" d=token )?
{
......
......@@ -359,6 +359,7 @@ public class EnvVars extends TreeMap<String,String> {
/**
* Add a key/value but only if the value is not-null. Otherwise no-op.
* @since 1.556
*/
public void putIfNotNull(String key, String value) {
if (value!=null)
......
......@@ -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 {
......
/*
* The MIT License
*
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Stephen Connolly, 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
......@@ -175,7 +175,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
* as the 2nd part can be repeated for each Hudson instance.
*/
private boolean pluginListed = false;
/**
* Strategy for creating and initializing plugins
*/
......@@ -192,7 +192,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
this.rootDir = rootDir;
if(!rootDir.exists())
rootDir.mkdirs();
strategy = createPluginStrategy();
// load up rules for the core first
......@@ -297,18 +297,18 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
r.add(p);
}
}
@Override
protected void reactOnCycle(PluginWrapper q, List<PluginWrapper> cycle)
throws hudson.util.CyclicGraphDetector.CycleDetectedException {
LOGGER.log(Level.SEVERE, "found cycle in plugin dependencies: (root="+q+", deactivating all involved) "+Util.join(cycle," -> "));
for (PluginWrapper pluginWrapper : cycle) {
pluginWrapper.setHasCycleDependency(true);
failedPlugins.add(new FailedPlugin(pluginWrapper.getShortName(), new CycleDetectedException(cycle)));
}
}
};
cgd.run(getPlugins());
......@@ -410,7 +410,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
/*
* contains operation that considers xxx.hpi and xxx.jpi as equal
* this is necessary since the bundled plugins are still called *.hpi
* this is necessary since the bundled plugins are still called *.hpi
*/
private boolean containsHpiJpi(Collection<String> bundledPlugins, String name) {
return bundledPlugins.contains(name.replaceAll("\\.hpi",".jpi"))
......@@ -484,7 +484,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
} catch (ReactorException e) {
throw new IOException("Failed to initialize "+ sn +" plugin",e);
}
// recalculate dependencies of plugins optionally depending the newly deployed one.
for (PluginWrapper depender: plugins) {
if (depender.equals(p)) {
......@@ -504,8 +504,8 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
}
}
LOGGER.info("Plugin " + sn + " dynamically installed");
LOGGER.info("Plugin " + p.getShortName()+":"+p.getVersion() + " dynamically installed");
}
/**
......@@ -532,7 +532,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
// normalization first, if the old file exists.
rename(new File(rootDir,legacyName),file);
rename(new File(rootDir,legacyName+".pinned"),pinFile);
// update file if:
// - no file exists today
// - bundled version and current version differs (by timestamp), and the file isn't pinned.
......@@ -589,7 +589,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
/**
* Creates a hudson.PluginStrategy, looking at the corresponding system property.
* Creates a hudson.PluginStrategy, looking at the corresponding system property.
*/
protected PluginStrategy createPluginStrategy() {
String strategyName = System.getProperty(PluginStrategy.class.getName());
......@@ -602,7 +602,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
LOGGER.info("Plugin strategy: " + strategyName);
return (PluginStrategy) strategy;
} else {
LOGGER.warning("Plugin strategy (" + strategyName +
LOGGER.warning("Plugin strategy (" + strategyName +
") is not an instance of hudson.PluginStrategy");
}
} catch (ClassNotFoundException e) {
......@@ -614,7 +614,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
LOGGER.info("Falling back to ClassicPluginStrategy");
}
// default and fallback
return new ClassicPluginStrategy(this);
}
......@@ -801,7 +801,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
rsp.sendRedirect("../updateCenter/");
}
/**
* Bare-minimum configuration mechanism to change the update center.
......@@ -816,7 +816,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
sites.remove(s);
}
sites.add(new UpdateSite(UpdateCenter.ID_DEFAULT, site));
return HttpResponses.redirectToContextRoot();
}
......@@ -835,7 +835,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
return new HttpRedirect("advanced");
}
/**
* Uploads a plugin.
*/
......@@ -851,8 +851,8 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
if("".equals(fileName)){
return new HttpRedirect("advanced");
}
// we allow the upload of the new jpi's and the legacy hpi's
if(!fileName.endsWith(".jpi") && !fileName.endsWith(".hpi")){
// we allow the upload of the new jpi's and the legacy hpi's
if(!fileName.endsWith(".jpi") && !fileName.endsWith(".hpi")){
throw new Failure(hudson.model.Messages.Hudson_NotAPlugin(fileName));
}
......@@ -1175,10 +1175,10 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
private static final Logger LOGGER = Logger.getLogger(PluginManager.class.getName());
public static boolean FAST_LOOKUP = !Boolean.getBoolean(PluginManager.class.getName()+".noFastLookup");
public static final Permission UPLOAD_PLUGINS = new Permission(Jenkins.PERMISSIONS, "UploadPlugins", Messages._PluginManager_UploadPluginsPermission_Description(),Jenkins.ADMINISTER,PermissionScope.JENKINS);
public static final Permission CONFIGURE_UPDATECENTER = new Permission(Jenkins.PERMISSIONS, "ConfigureUpdateCenter", Messages._PluginManager_ConfigureUpdateCenterPermission_Description(),Jenkins.ADMINISTER,PermissionScope.JENKINS);
/**
* Remembers why a plugin failed to deploy.
*/
......@@ -1202,17 +1202,17 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
/*package*/ static final class PluginInstanceStore {
final Map<PluginWrapper,Plugin> store = new Hashtable<PluginWrapper,Plugin>();
}
/**
* {@link AdministrativeMonitor} that checks if there are any plugins with cycle dependencies.
*/
@Extension
public static final class PluginCycleDependenciesMonitor extends AdministrativeMonitor {
private transient volatile boolean isActive = false;
private transient volatile List<String> pluginsWithCycle;
private transient volatile List<String> pluginsWithCycle;
public boolean isActivated() {
if(pluginsWithCycle == null){
pluginsWithCycle = new ArrayList<String>();
......@@ -1230,16 +1230,16 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
return pluginsWithCycle;
}
}
/**
* {@link AdministrativeMonitor} that informs the administrator about a required plugin update.
* @since 1.491
*/
@Extension
public static final class PluginUpdateMonitor extends AdministrativeMonitor {
private Map<String, PluginUpdateInfo> pluginsToBeUpdated = new HashMap<String, PluginManager.PluginUpdateMonitor.PluginUpdateInfo>();
/**
* Convenience method to ease access to this monitor, this allows other plugins to register required updates.
* @return this monitor.
......@@ -1247,10 +1247,10 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
public static final PluginUpdateMonitor getInstance() {
return ExtensionList.lookup(PluginUpdateMonitor.class).get(0);
}
/**
* Report to the administrator if the plugin with the given name is older then the required version.
*
*
* @param pluginName shortName of the plugin (artifactId)
* @param requiredVersion the lowest version which is OK (e.g. 2.2.2)
* @param message the message to show (plain text)
......@@ -1267,20 +1267,20 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
public boolean isActivated() {
return !pluginsToBeUpdated.isEmpty();
}
/**
* adds a message about a plugin to the manage screen
* adds a message about a plugin to the manage screen
* @param pluginName the plugins name
* @param message the message to be displayed
*/
public void addPluginToUpdate(String pluginName, String message) {
this.pluginsToBeUpdated.put(pluginName, new PluginUpdateInfo(pluginName, message));
}
public Collection<PluginUpdateInfo> getPluginsToBeUpdated() {
return pluginsToBeUpdated.values();
}
public static class PluginUpdateInfo {
public final String pluginName;
public final String message;
......@@ -1290,5 +1290,5 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
}
}
}
}
......@@ -1451,9 +1451,9 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
// OTOH, if a concurrent build is chosen, the user is willing to create a multiple workspace,
// so better throughput is achieved over time (modulo the initial cost of creating that many workspaces)
// by having multiple workspaces
WorkspaceList.Lease lease = l.acquire(ws, !concurrentBuild);
Node node = lb.getBuiltOn();
Launcher launcher = ws.createLauncher(listener).decorateByEnv(getEnvironment(node,listener));
WorkspaceList.Lease lease = l.acquire(ws, !concurrentBuild);
try {
listener.getLogger().println("Polling SCM changes on " + node.getSelfLabel().getName());
LOGGER.fine("Polling SCM changes of " + getName());
......@@ -1836,13 +1836,14 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
else
scmCheckoutStrategy = null;
if(req.hasParameter("_.assignedLabelString")) {
if(json.optBoolean("hasSlaveAffinity", json.has("label"))) {
assignedNode = Util.fixEmptyAndTrim(json.optString("label"));
} else if(req.hasParameter("_.assignedLabelString")) {
// Workaround for JENKINS-25372 while plugin is being updated.
// Keep this condition second for JENKINS-25533
LOGGER.log(Level.WARNING, "label assignment is using legacy '_.assignedLabelString'");
assignedNode = Util.fixEmptyAndTrim(req.getParameter("_.assignedLabelString"));
} else if(json.optBoolean("hasSlaveAffinity", json.has("label"))) {
assignedNode = Util.fixEmptyAndTrim(json.optString("label"));
} else {
} else {
assignedNode = null;
}
canRoam = assignedNode==null;
......
......@@ -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));
......
......@@ -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);
}
}
......
......@@ -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>
......
......@@ -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) {
......
......@@ -69,6 +69,7 @@ public abstract class TopLevelItemDescriptor extends Descriptor<TopLevelItem> {
* {@link TopLevelItemDescriptor}s that act like a wizard and produces different
* object types than {@link #clazz} can override this method to augment
* instance-descriptor relationship.
* @since 1.410
*/
public boolean testInstance(TopLevelItem i) {
return clazz.isInstance(i);
......
......@@ -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);
}
......
......@@ -51,7 +51,7 @@ public abstract class DiskSpaceMonitorDescriptor extends AbstractAsyncNodeMonito
* Value object that represents the disk space.
*/
@ExportedBean
public static final class DiskSpace extends OfflineCause implements Serializable {
public static final class DiskSpace extends MonitorOfflineCause implements Serializable {
private final String path;
@Exported
public final long size;
......@@ -70,7 +70,7 @@ public abstract class DiskSpaceMonitorDescriptor extends AbstractAsyncNodeMonito
@Override
public String toString() {
return String.valueOf(size);
return Messages.DiskSpaceMonitorDescriptor_DiskSpace_FreeSpaceTooLow(getGbLeft(), path);
}
/**
......@@ -119,6 +119,7 @@ public abstract class DiskSpaceMonitorDescriptor extends AbstractAsyncNodeMonito
this.triggered = triggered;
}
@Override
public Class<? extends AbstractDiskSpaceMonitor> getTrigger() {
return trigger;
}
......
/*
* The MIT License
*
* Copyright (c) 2014 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.node_monitors;
import javax.annotation.Nonnull;
import hudson.slaves.OfflineCause;
/**
* Offline cause to denote it was node monitor what put computer offline.
*
* @author ogondza
* @since 1.595
*/
public abstract class MonitorOfflineCause extends OfflineCause {
/**
* Identify monitor implementation that put computer offline.
*/
public abstract @Nonnull Class<? extends NodeMonitor> getTrigger();
}
......@@ -142,7 +142,7 @@ public class ResponseTimeMonitor extends NodeMonitor {
* Immutable representation of the monitoring data.
*/
@ExportedBean
public static final class Data extends OfflineCause implements Serializable {
public static final class Data extends MonitorOfflineCause implements Serializable {
/**
* Record of the past 5 times. -1 if time out. Otherwise in milliseconds.
* Old ones first.
......@@ -204,6 +204,11 @@ public class ResponseTimeMonitor extends NodeMonitor {
return getAverage()+"ms";
}
@Override
public Class<? extends NodeMonitor> getTrigger() {
return ResponseTimeMonitor.class;
}
private static final long serialVersionUID = 1L;
}
......
......@@ -96,10 +96,12 @@ abstract class BaseParser extends LLkParser {
int u = UPPER_BOUNDS[field];
if (field==2) u = 28; // day of month can vary depending on month, so to make life simpler, just use [1,28] that's always safe
if (field==4) u = 6; // Both 0 and 7 of day of week are Sunday. For better distribution, limit upper bound to 6
return doHash(LOWER_BOUNDS[field], u, step);
return doHash(LOWER_BOUNDS[field], u, step, field);
}
protected long doHash(int s, int e, int step) throws ANTLRException {
protected long doHash(int s, int e, int step, int field) throws ANTLRException {
rangeCheck(s, field);
rangeCheck(e, field);
if (step > e - s + 1) {
error(Messages.BaseParser_OutOfRange(step, 1, e - s + 1));
throw new AssertionError();
......
......@@ -144,10 +144,11 @@ public final class Permission {
* See {@link #description}.
* @param impliedBy
* See {@link #impliedBy}.
* @throws IllegalStateException if this permission was already defined
*/
public Permission(@Nonnull PermissionGroup group, @Nonnull String name,
@CheckForNull Localizable description, @CheckForNull Permission impliedBy, boolean enable,
@Nonnull PermissionScope[] scopes) {
@Nonnull PermissionScope[] scopes) throws IllegalStateException {
if(!JSONUtils.isJavaIdentifier(name))
throw new IllegalArgumentException(name+" is not a Java identifier");
this.owner = group.owner;
......@@ -221,6 +222,14 @@ public final class Permission {
return owner.getName()+'.'+name;
}
@Override public boolean equals(Object o) {
return o instanceof Permission && getId().equals(((Permission) o).getId());
}
@Override public int hashCode() {
return getId().hashCode();
}
/**
* Convert the ID representation into {@link Permission} object.
*
......
......@@ -23,17 +23,13 @@
*/
package hudson.security;
import hudson.CopyOnWrite;
import hudson.model.Hudson;
import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.annotation.CheckForNull;
import org.jvnet.localizer.Localizable;
/**
......@@ -42,8 +38,7 @@ import org.jvnet.localizer.Localizable;
* Sortable by the owner class name.
*/
public final class PermissionGroup implements Iterable<Permission>, Comparable<PermissionGroup> {
private final List<Permission> permisisons = new CopyOnWriteArrayList<Permission>();
private final List<Permission> permisisonsView = Collections.unmodifiableList(permisisons);
private final SortedSet<Permission> permissions = new TreeSet<Permission>(Permission.ID_COMPARATOR);
public final Class owner;
/**
......@@ -52,37 +47,41 @@ public final class PermissionGroup implements Iterable<Permission>, Comparable<P
*/
public final Localizable title;
public PermissionGroup(Class owner, Localizable title) {
/**
* Both creates a registers a new permission group.
* @param owner sets {@link #owner}
* @param title sets {@link #title}
* @throws IllegalStateException if this group was already registered
*/
public PermissionGroup(Class owner, Localizable title) throws IllegalStateException {
this.owner = owner;
this.title = title;
register(this);
}
synchronized(PermissionGroup.class) {
List<PermissionGroup> allGroups = new ArrayList<PermissionGroup>(ALL);
allGroups.add(this);
Collections.sort(allGroups);
ALL = Collections.unmodifiableList(allGroups);
}
PERMISSIONS.put(owner,this);
private String id() {
return owner.getName();
}
public Iterator<Permission> iterator() {
return permisisons.iterator();
return getPermissions().iterator();
}
/*package*/ void add(Permission p) {
permisisons.add(p);
/*package*/ synchronized void add(Permission p) {
if (!permissions.add(p)) {
throw new IllegalStateException("attempt to register a second Permission for " + p.getId());
}
}
/**
* Lists up all the permissions in this group.
*/
public List<Permission> getPermissions() {
return permisisonsView;
public synchronized List<Permission> getPermissions() {
return new ArrayList<Permission>(permissions);
}
public boolean hasPermissionContainedBy(PermissionScope scope) {
for (Permission p : permisisons)
public synchronized boolean hasPermissionContainedBy(PermissionScope scope) {
for (Permission p : permissions)
if (p.isContainedBy(scope))
return true;
return false;
......@@ -91,8 +90,8 @@ public final class PermissionGroup implements Iterable<Permission>, Comparable<P
/**
* Finds a permission that has the given name.
*/
public Permission find(String name) {
for (Permission p : permisisons) {
public synchronized Permission find(String name) {
for (Permission p : permissions) {
if(p.name.equals(name))
return p;
}
......@@ -107,7 +106,7 @@ public final class PermissionGroup implements Iterable<Permission>, Comparable<P
// among the permissions of the same group, just sort by their names
// so that the sort order is consistent regardless of classloading order.
return this.owner.getName().compareTo(that.owner.getName());
return id().compareTo(that.id());
}
private int compareOrder() {
......@@ -115,23 +114,35 @@ public final class PermissionGroup implements Iterable<Permission>, Comparable<P
return 1;
}
public int size() {
return permisisons.size();
@Override public boolean equals(Object o) {
return o instanceof PermissionGroup && id().equals(((PermissionGroup) o).id());
}
/**
* All groups. Sorted.
*/
@CopyOnWrite
private static List<PermissionGroup> ALL = Collections.emptyList();
@Override public int hashCode() {
return id().hashCode();
}
public synchronized int size() {
return permissions.size();
}
@Override public String toString() {
return "PermissionGroup[" + id() + "]";
}
private static synchronized void register(PermissionGroup g) {
if (!PERMISSIONS.add(g)) {
throw new IllegalStateException("attempt to register a second PermissionGroup for " + g.id());
}
}
/**
* Returns all the {@link PermissionGroup}s available in the system.
* @return
* always non-null. Read-only.
*/
public static List<PermissionGroup> getAll() {
return ALL;
public static synchronized List<PermissionGroup> getAll() {
return new ArrayList<PermissionGroup>(PERMISSIONS);
}
/**
......@@ -139,12 +150,17 @@ public final class PermissionGroup implements Iterable<Permission>, Comparable<P
*
* @return null if not found.
*/
public static PermissionGroup get(Class owner) {
return PERMISSIONS.get(owner);
public static synchronized @CheckForNull PermissionGroup get(Class owner) {
for (PermissionGroup g : PERMISSIONS) {
if (g.owner == owner) {
return g;
}
}
return null;
}
/**
* All the permissions in the system, keyed by their owners.
*/
private static final Map<Class, PermissionGroup> PERMISSIONS = new ConcurrentHashMap<Class, PermissionGroup>();
private static final SortedSet<PermissionGroup> PERMISSIONS = new TreeSet<PermissionGroup>();
}
......@@ -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*/);
}
}
......
......@@ -294,8 +294,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
public FingerprintAction(Run build, Map<String, String> record) {
this.build = build;
this.record = PackedMap.of(record);
compact();
this.record = compact(record);
}
@Deprecated
......@@ -306,9 +305,8 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
public void add(Map<String,String> moreRecords) {
Map<String,String> r = new HashMap<String, String>(record);
r.putAll(moreRecords);
record = PackedMap.of(r);
record = compact(r);
ref = null;
compact();
}
public String getIconFileName() {
......@@ -341,48 +339,20 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD
@Override public void onLoad(Run<?,?> r) {
build = r;
compact();
record = compact(record);
}
@Override public void onAttached(Run<?,?> r) {
// for historical reasons this setup is done in the constructor instead
}
private void compact() {
// share data structure with nearby builds, but to keep lazy loading efficient,
// don't go back the history forever.
if (rand.nextInt(2)!=0) {
Run pb = build.getPreviousBuild();
if (pb!=null) {
FingerprintAction a = pb.getAction(FingerprintAction.class);
if (a!=null)
compact(a);
}
}
}
/**
* Reuse string instances from another {@link FingerprintAction} to reduce memory footprint.
*/
protected void compact(FingerprintAction a) {
Map<String,String> intern = new HashMap<String, String>(); // string intern map
for (Entry<String, String> e : a.record.entrySet()) {
intern.put(e.getKey(),e.getKey());
intern.put(e.getValue(),e.getValue());
}
Map<String,String> b = new HashMap<String, String>();
/** Share data structure with other builds, mainly those of the same job. */
private PackedMap<String,String> compact(Map<String,String> record) {
Map<String,String> b = new HashMap<String,String>();
for (Entry<String,String> e : record.entrySet()) {
String k = intern.get(e.getKey());
if (k==null) k = e.getKey();
String v = intern.get(e.getValue());
if (v==null) v = e.getValue();
b.put(k,v);
b.put(e.getKey().intern(), e.getValue().intern());
}
record = PackedMap.of(b);
return PackedMap.of(b);
}
/**
......
......@@ -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);
......
......@@ -234,13 +234,15 @@ public class JDKInstaller extends ToolInstaller {
- http://java.sun.com/j2se/1.5.0/sdksilent.html
- http://java.sun.com/j2se/1.4.2/docs/guide/plugin/developer_guide/silent.html
*/
String logFile = jdkBundle+".install.log";
expectedLocation = expectedLocation.trim();
if (expectedLocation.endsWith("\\")) {
// Prevent a trailing slash from escaping quotes
expectedLocation = expectedLocation.substring(0, expectedLocation.length() - 1);
}
String logFile = new FilePath(launcher.getChannel(), expectedLocation).getParent().createTempFile("install", "log").getRemote();
ArgumentListBuilder args = new ArgumentListBuilder();
assert (new File(expectedLocation).exists()) : expectedLocation
+ " must exist, otherwise /L will cause the installer to fail with error 1622";
......@@ -248,20 +250,19 @@ public class JDKInstaller extends ToolInstaller {
// Installer uses InstallShield.
args.add("CMD.EXE", "/C");
// see http://docs.oracle.com/javase/1.5.0/docs/guide/deployment/deployment-guide/silent.html
// CMD.EXE /C must be followed by a single parameter (do not split it!)
args.add(jdkBundle + " /s /v\"/qn REBOOT=ReallySuppress INSTALLDIR=\\\""
+ expectedLocation + "\\\" /L \\\"" + expectedLocation
+ "\\jdk.exe.install.log\\\"\"");
+ expectedLocation + "\\\" /L \\\"" + logFile + "\\\"\"");
} else {
// Installed uses Windows Installer (MSI)
args.add(jdkBundle, "/s");
// Create a private JRE by omitting "PublicjreFeature"
// @see http://docs.oracle.com/javase/7/docs/webnotes/install/windows/jdk-installation-windows.html#jdk-silent-installation
args.add("ADDLOCAL=\"ToolsFeature\"");
args.add("REBOOT=ReallySuppress", "INSTALLDIR=" + expectedLocation,
"/L \\\"" + expectedLocation + "\\jdk.exe.install.log\\\"");
args.add("ADDLOCAL=\"ToolsFeature\"",
"REBOOT=ReallySuppress", "INSTALLDIR=" + expectedLocation,
"/L", logFile);
}
int r = launcher.launch().cmds(args).stdout(out)
.pwd(new FilePath(launcher.getChannel(), expectedLocation)).join();
......@@ -716,7 +717,7 @@ public class JDKInstaller extends ToolInstaller {
if (value) {
return FormValidation.ok();
} else {
return FormValidation.error(Messages.JDKInstaller_DescriptorImpl_doCheckAcceptLicense());
return FormValidation.error(Messages.JDKInstaller_DescriptorImpl_doCheckAcceptLicense());
}
}
......
......@@ -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);
......
......@@ -2037,7 +2037,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
this.buildsDir = buildsDir;
}
public FilePath getRootPath() {
@Override public @Nonnull FilePath getRootPath() {
return new FilePath(getRootDir());
}
......@@ -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);
}
/**
......
/*
* 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);
......
......@@ -16,6 +16,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import org.apache.commons.io.IOUtils;
/**
......@@ -106,7 +107,13 @@ public class DefaultConfidentialStore extends ConfidentialStore {
byte[] bytes = IOUtils.toByteArray(cis);
return verifyMagic(bytes);
} catch (GeneralSecurityException e) {
throw new IOException("Failed to persist the key: "+key.getId(),e);
throw new IOException("Failed to load the key: "+key.getId(),e);
} catch (IOException x) {
if (x.getCause() instanceof BadPaddingException) {
return null; // broken somehow
} else {
throw x;
}
} finally {
IOUtils.closeQuietly(cis);
IOUtils.closeQuietly(fis);
......
......@@ -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;
}
......
......@@ -33,11 +33,11 @@ THE SOFTWARE.
</h1>
<j:set var="commandArgs" value="${command.name}${command.singleLineSummary}"/>
<st:include page="example.jelly"/>
<p>${command.longDescription}</p>
<j:set var="usage" value="${command.usage}"/>
<j:if test="${!empty usage}">
<pre>${command.usage}</pre>
</j:if>
<pre>
<j:out value="${h.escape(command.longDescription)}"/>
<j:set var="usage" value="${command.usage}"/>
<j:if test="${!empty usage}"><br/>${command.usage}</j:if>
</pre>
</l:main-panel>
</l:layout>
</j:jelly>
......@@ -25,7 +25,7 @@ Build\ Artifacts=\u00C9p\u00EDt\u0151k\u00F6vek
Changes\ in\ dependency=V\u00E1ltoz\u00E1sok a f\u00FCgg\u0151s\u00E9g(ek)ben
Not\ yet\ determined=M\u00E9g nincs feldolgozva
Took=tartott
beingExecuted=A build {} perce fut
beingExecuted=A build {0} perce fut
detail=b\u0151vebben
on=@
startedAgo=Elkezd\u0151d\u00F6tt {0}-kor
......@@ -94,6 +94,7 @@ THE SOFTWARE.
<!-- Skip the label for this node -->
<j:if test="${entry.item!=it.node.selfLabel}">
<a class="${entry.className} model-link inside" href="${rootURL}/label/${entry.item.name}">${entry.item.name}</a>
<wbr/>
</j:if>
</j:forEach>
</div>
......
......@@ -50,6 +50,7 @@ THE SOFTWARE.
<st:nbsp/>
<a href="${url}" class="model-link inside">${c.displayName}</a>
</nobr>
<wbr/>
</j:forEach>
</div>
......
......@@ -55,8 +55,9 @@ AbstractProject.BuildPermission.Description=\
This permission grants the ability to start a new build.
AbstractProject.WorkspacePermission.Description=\
This permission grants the ability to retrieve the contents of a workspace \
Jenkins checked out for performing builds. If you don\u2019t want an user to access \
the source code, you can do so by revoking this permission.
Jenkins checked out for performing builds. If you don\u2019t want a user to access \
files in the workspace (e.g. source code checked out from SCM or intermediate build \
results) through the workspace browser, you can revoke this permission.
AbstractProject.ExtendedReadPermission.Description=\
This permission grants read-only access to project configurations. Please be \
aware that sensitive information in your builds, such as passwords, will be \
......
......@@ -152,7 +152,7 @@ Item.READ.description=\u30b8\u30e7\u30d6\u3092\u53c2\u7167\u3057\u307e\u3059\u30
(\u8a31\u53ef\u3057\u306a\u3044\u3053\u3068\u3082\u3067\u304d\u307e\u3059\u304c\u3001\u533f\u540d\u30e6\u30fc\u30b6\u30fc\u304c\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u30b8\u30e7\u30d6\u3092\u53c2\u7167\u3067\u304d\u308b\u3088\u3046\u306b"Discover"\u3092\u8a31\u53ef\u3057\u3066\u304f\u3060\u3055\u3044\u3002)
Job.AllRecentBuildFailed=\u6700\u8fd1\u306e\u5168\u3066\u306e\u30d3\u30eb\u30c9\u306f\u5931\u6557\u3057\u307e\u3057\u305f\u3002
Job.BuildStability=\u5b89\u5b9a\u3057\u305f\u30d3\u30eb\u30c9: {0}
Job.BuildStability=\u30d3\u30eb\u30c9\u306e\u5b89\u5b9a\u6027: {0}
Job.NOfMFailed=\u6700\u8fd1\u306e{1}\u500b\u4e2d\u3001{0}\u500b\u30d3\u30eb\u30c9\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002
Job.NoRecentBuildFailed=\u6700\u8fd1\u306e\u30d3\u30eb\u30c9\u306f\u5931\u6557\u3057\u3066\u307e\u305b\u3093\u3002
Job.Pronoun=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8
......
......@@ -27,6 +27,6 @@ THE SOFTWARE.
xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form"
xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
<f:entry title="${it.name}" description="${it.description}">
<a href="${rootURL}/${it.run.url}" class="model-link inside">${it.run}</a>
<a href="${rootURL}/${it.run.url}" class="model-link inside">${it.run.fullDisplayName}</a>
</f:entry>
</j:jelly>
......@@ -24,5 +24,5 @@ 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">
<p class="error">${%blurb(it.gbLeft, it.path)}</p>
<p class="error">${it}</p>
</j:jelly>
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, 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.
blurb=Disk space is too low. Only {0}GB left on {1}.
\ No newline at end of file
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, Inc. Kohsuke Kawaguchi. Knud Poulsen.
#
# 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.
blurb=Disk plads for lav. Kun {0}GB tilbage.
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Simon Wiest
#
# 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.
blurb=Zu wenig Festplattenplatz: Nur noch {0}GB frei.
\ No newline at end of file
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, 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.
blurb=El espacio en disco es muy bajo, sólo quedan {0}GB.
# This file is under the MIT License by authors
blurb=Espace disque insuffisant. Seulement {0} GB restant sur {1}
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Seiji Sogabe
#
# 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.
blurb=\u30C7\u30A3\u30B9\u30AF\u5BB9\u91CF\u304C\u5C11\u306A\u3059\u304E\u307E\u3059\u3002\u6B8B\u308A{0}GB\u3067\u3059\u3002
\ No newline at end of file
# The MIT License
#
# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributers
#
# 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.
# Disk space is too low. Only {0}GB left on {1}.
blurb=Espa\u00e7o em disco est\u00e1 acabando, apenas {0}Gb livre em {1}.
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, Inc., Cleiber Silva
#
# 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.
# Disk space is too low. Only {0}GB left.
blurb=Pouco espa\u00e7o em disco. Somente {0}GB restante.
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, 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.
blurb=Diskutrymme l\u00E5gt. Enbart {0}GB kvar.
# The MIT License
#
# Copyright (c) 2013, Chunghwa Telecom Co., Ltd., Pei-Tang Huang
#
# 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.
blurb=\u78c1\u789f\u7a7a\u9593\u592a\u5c11\u3002{1} \u4e0a\u53ea\u5269 {0}GB\u3002
......@@ -31,3 +31,4 @@ ResponseTimeMonitor.TimeOut=Time out for last {0} try
SwapSpaceMonitor.DisplayName=Free Swap Space
TemporarySpaceMonitor.DisplayName=Free Temp Space
AbstractNodeMonitorDescriptor.NoDataYet=Not yet
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=Disk space is too low. Only {0}GB left on {1}.
......@@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
blurb=Er is te weinig vrije schijfruimte, nog maar {0} GB over.
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=M\u00E1lo m\u00EDsta na disku. Zb\u00FDv\u00E1 pouze {0}GB.
......@@ -29,3 +29,4 @@ SwapSpaceMonitor.DisplayName=Ledig Swap Plads
ArchitectureMonitor.DisplayName=Arkitektur
ResponseTimeMonitor.MarkedOffline=Tager {0} offline midlertidigt da den ikke svarer
ResponseTimeMonitor.DisplayName=Respons Tid
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=Disk plads for lav. Kun {0}GB tilbage.
......@@ -29,7 +29,4 @@ ResponseTimeMonitor.MarkedOffline=Nehme {0} tempor
ResponseTimeMonitor.TimeOut= {0} mal keine Antwort
SwapSpaceMonitor.DisplayName=Freier Swap Space
TemporarySpaceMonitor.DisplayName=Freier TEMP-Platz
\ No newline at end of file
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=Zu wenig Festplattenplatz: Nur noch {0}GB frei.
......@@ -29,3 +29,4 @@ ResponseTimeMonitor.MarkedOffline=Poniendo temporalmente {0} fuera de l
ResponseTimeMonitor.TimeOut=Se sobrepasó el tiempo de espera en el último intento de {0}
SwapSpaceMonitor.DisplayName=Espacio de intercambio libre
TemporarySpaceMonitor.DisplayName=Espacio temporal libre
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=El espacio en disco es muy bajo, sólo quedan {0}GB.
......@@ -28,3 +28,4 @@ ResponseTimeMonitor.DisplayName=Temps de r\u00e9ponse
ResponseTimeMonitor.MarkedOffline={0} est marqu\u00e9 comme d\u00e9connect\u00e9 temporairement, parce qu''il ne r\u00e9pond pas
ResponseTimeMonitor.TimeOut=Time out du dernier essai {0}
SwapSpaceMonitor.DisplayName=Espace de swap disponible
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=Espace disque insuffisant. Seulement {0} GB restant sur {1}
......@@ -30,4 +30,5 @@ ResponseTimeMonitor.MarkedOffline=\u5fdc\u7b54\u3057\u306a\u3044\u305f\u3081\u30
ResponseTimeMonitor.TimeOut=\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8(\u76f4\u8fd1 {0}\u56de)
SwapSpaceMonitor.DisplayName=\u7a7a\u304d\u30b9\u30ef\u30c3\u30d7\u5bb9\u91cf
TemporarySpaceMonitor.DisplayName=\u7a7a\u304d\u30c6\u30f3\u30dd\u30e9\u30ea\u5bb9\u91cf
AbstractNodeMonitorDescriptor.NoDataYet=\u30c7\u30fc\u30bf\u672a\u53d6\u5f97
\ No newline at end of file
AbstractNodeMonitorDescriptor.NoDataYet=\u30c7\u30fc\u30bf\u672a\u53d6\u5f97
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=\u30C7\u30A3\u30B9\u30AF\u5BB9\u91CF\u304C\u5C11\u306A\u3059\u304E\u307E\u3059\u3002\u6B8B\u308A{0}GB\u3067\u3059\u3002
......@@ -22,4 +22,5 @@
ArchitectureMonitor.DisplayName=Architectuur
ClockMonitor.DisplayName=Tijdsverschil
DiskSpaceMonitor.DisplayName=Beschikbare schijfruimte
\ No newline at end of file
DiskSpaceMonitor.DisplayName=Beschikbare schijfruimte
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=Er is te weinig vrije schijfruimte, nog maar {0} GB over.
......@@ -31,3 +31,4 @@ DiskSpaceMonitor.DisplayName=Espa\u00e7o em disco livre
SwapSpaceMonitor.DisplayName=Espa\u00e7o de swap livre
ArchitectureMonitor.DisplayName=Arquitetura
ResponseTimeMonitor.MarkedOffline=Deixar {0} offline porque n\u00e3o est\u00e1 respondendo
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=Espa\u00e7o em disco est\u00e1 acabando, apenas {0}Gb livre em {1}.
......@@ -39,3 +39,5 @@ ResponseTimeMonitor.MarkedOffline= Indispon\u00edvel temporariamente por que n\u
AbstractNodeMonitorDescriptor.NoDataYet=Nada ainda
# Putting {0} back online as there is enough disk space again
DiskSpaceMonitor.MarkedOnline=Retornando {0} para online pois h\u00e1 espa\u00e7o em disco suficiente novamente
# Disk space is too low. Only {0}GB left.
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=Pouco espa\u00e7o em disco. Somente {0}GB restante.
......@@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
blurb=M\u00E1lo m\u00EDsta na disku. Zb\u00FDv\u00E1 pouze {0}GB.
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=Diskutrymme l\u00E5gt. Enbart {0}GB kvar.
......@@ -30,3 +30,4 @@ ResponseTimeMonitor.MarkedOffline={0} \u6c92\u6709\u56de\u61c9\uff0c\u5c07\u5176
ResponseTimeMonitor.TimeOut=\u6700\u8fd1 {0} \u6b21\u9023\u7dda\u90fd\u903e\u6642
SwapSpaceMonitor.DisplayName=\u53ef\u7528 Swap \u7a7a\u9593
TemporarySpaceMonitor.DisplayName=\u53ef\u7528\u66ab\u5b58\u7a7a\u9593
DiskSpaceMonitorDescriptor.DiskSpace.FreeSpaceTooLow=\u78c1\u789f\u7a7a\u9593\u592a\u5c11\u3002{1} \u4e0a\u53ea\u5269 {0}GB\u3002
<!--
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>
......
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
......@@ -5,9 +5,7 @@ def f=namespace(lib.FormTagLib)
f.entry(title:_("# of executors"), field:"numExecutors") {
f.number(clazz:"number", min:0, step:1)
}
if (!app.slaves.isEmpty() || !app.clouds.isEmpty()) {
f.entry(title:_("Labels"),field:"labelString") {
f.textbox()
}
f.slave_mode(name:"master.mode",node:app)
f.entry(title:_("Labels"),field:"labelString") {
f.textbox()
}
f.slave_mode(name:"master.mode",node:app)
<!--
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>
......
......@@ -217,9 +217,9 @@ ${h.initPageVariables(context)}
</l:breadcrumbBar>
</div>
<div id="page-body" class="container-fluid">
<div id="page-body">
<div class="row">
<div id="side-panel" class="col-sm-9 col-md-7 col-lg-6 col-xlg-4">
<div id="side-panel">
<div id="side-panel-content">
<j:set var="mode" value="side-panel" />
<d:invokeBody />
......@@ -237,7 +237,7 @@ ${h.initPageVariables(context)}
</div>
</div>
<div id="main-panel" class="col-sm-15 col-md-17 col-lg-18 col-xlg-20">
<div id="main-panel">
<div id="main-panel-content">
<j:set var="mode" value="main-panel" />
<d:invokeBody/>
......
/*
* 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");
......
......@@ -68,7 +68,7 @@ public class ListJobsCommandTest {
/*
@Test
@Bug(18393)
@Issue("JENKINS-18393")
public void failForMatrixProject() throws Exception {
final MatrixProject matrix = mock(MatrixProject.class);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册