提交 2a8a0bf7 编写于 作者: K kohsuke

Merged revisions...

Merged revisions 17324-17325,17428,17430,17473-17475,17481-17482,17555-17563,17571,17593-17595,17619-17620,17637-17644,17656-17657,17663-17665,17667-17670,17730,17753,17755-17758,17763-17768,17770-17771,17901,17906,17911-17912,17958,17961-17962,17970-17977 via svnmerge from 
https://www.dev.java.net/svn/hudson/branches/tool-autoinstallation

........
  r17324 | jglick | 2009-04-20 18:24:32 -0700 (Mon, 20 Apr 2009) | 2 lines
  
  Defensive cloning and Javadoc corrections.
  (Though it seems no one is using the default impls of these methods yet anyway.)
........
  r17325 | jglick | 2009-04-20 18:29:06 -0700 (Mon, 20 Apr 2009) | 1 line
  
  Deprecation of delegate method is intentional.
........
  r17428 | jglick | 2009-04-23 08:07:14 -0700 (Thu, 23 Apr 2009) | 1 line
  
  Noting ToolLocationTranslator more prominently.
........
  r17430 | jglick | 2009-04-23 09:16:40 -0700 (Thu, 23 Apr 2009) | 1 line
  
  Unused imports.
........
  r17473 | jglick | 2009-04-23 19:21:11 -0700 (Thu, 23 Apr 2009) | 1 line
  
  Allow Plugin.configure to see the StaplerRequest, needed for some kinds of calls.
........
  r17474 | jglick | 2009-04-23 19:25:54 -0700 (Thu, 23 Apr 2009) | 3 lines
  
  Initial sketch of plugin to manage tool installations across slaves.
  (Should really be under ../plugins/ but that was not branched, so putting here for the moment.)
........
  r17475 | jglick | 2009-04-23 20:29:36 -0700 (Thu, 23 Apr 2009) | 1 line
  
  Typo in Javadoc.
........
  r17481 | jglick | 2009-04-23 22:46:26 -0700 (Thu, 23 Apr 2009) | 1 line
  
  Unzip of an unbuffered stream is painfully slow.
........
  r17482 | jglick | 2009-04-23 22:48:01 -0700 (Thu, 23 Apr 2009) | 1 line
  
  ZIP-based tool installer now functional.
........
  r17555 | jglick | 2009-04-27 17:50:38 -0700 (Mon, 27 Apr 2009) | 1 line
  
  Build against newest trunk.
........
  r17556 | jglick | 2009-04-27 18:38:28 -0700 (Mon, 27 Apr 2009) | 1 line
  
  Adding help.
........
  r17557 | jglick | 2009-04-27 18:57:46 -0700 (Mon, 27 Apr 2009) | 1 line
  
  Moving toolName & label config GUI into generic files.
........
  r17558 | jglick | 2009-04-27 19:37:30 -0700 (Mon, 27 Apr 2009) | 1 line
  
  Typo.
........
  r17559 | jglick | 2009-04-27 20:07:47 -0700 (Mon, 27 Apr 2009) | 1 line
  
  Second tool installer type based on a freeform command.
........
  r17560 | jglick | 2009-04-27 20:24:36 -0700 (Mon, 27 Apr 2009) | 1 line
  
  installIfNecessaryFrom will now just return false in case the target dir exists but the URL cannot be opened.
........
  r17561 | jglick | 2009-04-27 20:25:22 -0700 (Mon, 27 Apr 2009) | 2 lines
  
  chmod only if something was actually installed.
  Use a+x, not o+x.
........
  r17562 | jglick | 2009-04-27 20:26:46 -0700 (Mon, 27 Apr 2009) | 1 line
  
  Comment only.
........
  r17563 | jglick | 2009-04-27 20:41:12 -0700 (Mon, 27 Apr 2009) | 1 line
  
  I18N.
........
  r17571 | jglick | 2009-04-28 08:15:31 -0700 (Tue, 28 Apr 2009) | 2 lines
  
  Wiki link.
........
  r17593 | jglick | 2009-04-28 15:38:33 -0700 (Tue, 28 Apr 2009) | 1 line
  
  Turn off echo mode, it's just noise.
........
  r17594 | jglick | 2009-04-28 15:39:29 -0700 (Tue, 28 Apr 2009) | 2 lines
  
  Synchronizing access to installers for a given node/tool combo.
  Sending log messages to Hudson log in real time.
........
  r17595 | jglick | 2009-04-28 15:46:29 -0700 (Tue, 28 Apr 2009) | 1 line
  
  Copyrights.
........
  r17619 | jglick | 2009-04-29 06:25:23 -0700 (Wed, 29 Apr 2009) | 1 line
  
  Improved Javadoc.
........
  r17620 | jglick | 2009-04-29 06:51:11 -0700 (Wed, 29 Apr 2009) | 1 line
  
  Demonstraing how to use DLJ to mechanically install official JDK releases on supported operating systems.
........
  r17637 | kohsuke | 2009-04-30 16:15:24 -0700 (Thu, 30 Apr 2009) | 1 line
  
  fixed what appears to be a C&P mistake
........
  r17638 | kohsuke | 2009-04-30 16:16:21 -0700 (Thu, 30 Apr 2009) | 1 line
  
  show YUI logger if YUI debugging mode is set.
........
  r17639 | kohsuke | 2009-04-30 16:52:28 -0700 (Thu, 30 Apr 2009) | 7 lines
  
  Fixed a UI script problem when <hetero-list> nests inside <repeatable>.
  
  The cause of the problem was that the behavior for inner <hetero-list> kicks in first, altering HTML (by removing "prototypes"), before behavior for outer <repetable> kicks in and removes the master copy from DOM tree.
  
  So <repeatable> ends up computing a master copy after some elements are removed, and hence when we try to reinsert them later by clicking "Add", it'll get incorrect copy, and re-execution of the <hetero-list> behavior bombs out.
  
  I fixed this by skipping those HTML-altering behaviors if they show up in the part of DOM tree that's supposed to be snapshotted and removed.
........
  r17640 | kohsuke | 2009-04-30 16:57:58 -0700 (Thu, 30 Apr 2009) | 7 lines
  
  Added ToolProperty to ToolInstallation (which acts like NodeProperty to Node --- basically decorates the base object with additional properties.)
  
  The original motivation is to implement ToolInstaller this way, if Jesse is OK. But decorating tools seem to have some other interesting use, such as a plugin for configuring $M2_HOME/conf/settings.xml, or a plugin to insert unlimited cryptography extension to JDK, or a plugin to add a few more jars into $ANT_HOME/lib.
  
  For this to work, ToolInstallations that want properties needs to be modified to use a new constructor. For a starter, I modified the JDK class to do this.
  
  I also took the opportunity to modernize JDK/config.jelly.
........
  r17641 | kohsuke | 2009-04-30 17:02:04 -0700 (Thu, 30 Apr 2009) | 1 line
  
  this appears to be a left-over from early days when we didn't have a means to do form validation
........
  r17642 | kohsuke | 2009-04-30 17:02:35 -0700 (Thu, 30 Apr 2009) | 1 line
  
  we don't need no.such.JDK but we need name. rebranching
........
  r17643 | kohsuke | 2009-04-30 17:03:22 -0700 (Thu, 30 Apr 2009) | 1 line
  
  rebranched from Hudson's configure.properties
........
  r17644 | kohsuke | 2009-04-30 17:05:31 -0700 (Thu, 30 Apr 2009) | 1 line
  
  removed unnecessary translations
........
  r17656 | kohsuke | 2009-05-01 11:27:36 -0700 (Fri, 01 May 2009) | 1 line
  
  Less hack for improved type safety. Asking people to write more code is not necessarily bad as long as what they need to do is obvious to them.
........
  r17657 | kohsuke | 2009-05-01 12:07:20 -0700 (Fri, 01 May 2009) | 5 lines
  
  The toolautoinst part of the change that corresponds to rev.17640.
  
  ToolInstallers are now hooked under ToolInstallation through InstallSourceProperty as a ToolProperty.
  
  This allows the relevant UIs to be shown in one place. Still pending Jesse's feedback. Making it a single commit to simplify a revert if necessary
........
  r17663 | kohsuke | 2009-05-01 13:23:51 -0700 (Fri, 01 May 2009) | 1 line
  
  doc improvement
........
  r17664 | kohsuke | 2009-05-01 13:24:10 -0700 (Fri, 01 May 2009) | 1 line
  
  needs this to allow Stapler to databind hetero-list automatically
........
  r17665 | jglick | 2009-05-01 13:31:27 -0700 (Fri, 01 May 2009) | 1 line
  
  Warnings.
........
  r17667 | kohsuke | 2009-05-01 14:20:53 -0700 (Fri, 01 May 2009) | 1 line
  
  added a method to do chmod
........
  r17668 | kohsuke | 2009-05-01 14:30:35 -0700 (Fri, 01 May 2009) | 1 line
  
  prefer JDK6 version to avoid unnecessary JNA loading
........
  r17669 | kohsuke | 2009-05-01 14:33:09 -0700 (Fri, 01 May 2009) | 1 line
  
  no, I must have confused this.
........
  r17670 | kohsuke | 2009-05-01 15:57:11 -0700 (Fri, 01 May 2009) | 2 lines
  
  - Automated JDK installer and its test.
  - Minor update to the signature of ToolInstaller for supplying the expected default location.
........
  r17730 | kohsuke | 2009-05-04 11:27:34 -0700 (Mon, 04 May 2009) | 1 line
  
  avoid NPE as reported in HUDSON-3608
........
  r17753 | jglick | 2009-05-04 19:34:08 -0700 (Mon, 04 May 2009) | 2 lines
  
  Moved toolautoinst functionality into core.
........
  r17755 | jglick | 2009-05-04 20:28:34 -0700 (Mon, 04 May 2009) | 8 lines
  
  Added TaskListener parameter and IOException and InterruptedException to several methods:
  Run.getEnvironment
  NodeSpecific.forNode
  ToolLocationTranslator.getToolHome
  ToolLocationNodeProperty.getToolHome
  ToolInstallation.translateFor
  ProcessCache.Factory.getMavenInstallation
  ProcessCache.Factory.getJava
........
  r17756 | jglick | 2009-05-04 20:36:56 -0700 (Mon, 04 May 2009) | 1 line
  
  expectedLocation was never strictly necessary, and will soon no longer be used even by JDKInstaller.
........
  r17757 | jglick | 2009-05-04 20:40:41 -0700 (Mon, 04 May 2009) | 1 line
  
  Not sure yet what @since should be.
........
  r17758 | jglick | 2009-05-04 20:41:51 -0700 (Mon, 04 May 2009) | 1 line
  
  getDefaultInstallers method added. Not yet in use.
........
  r17763 | kohsuke | 2009-05-05 10:50:36 -0700 (Tue, 05 May 2009) | 1 line
  
  deprecated redundant getJavaHome method
........
  r17764 | kohsuke | 2009-05-05 12:10:45 -0700 (Tue, 05 May 2009) | 1 line
  
  added general-purpose property defaulting mechanism, and added default tool installer for JDK
........
  r17765 | jglick | 2009-05-05 12:35:56 -0700 (Tue, 05 May 2009) | 1 line
  
  capitalize("") -> ""
........
  r17766 | jglick | 2009-05-05 12:49:52 -0700 (Tue, 05 May 2009) | 1 line
  
  Missing @since.
........
  r17767 | jglick | 2009-05-05 13:01:41 -0700 (Tue, 05 May 2009) | 1 line
  
  Unchecked warning.
........
  r17768 | jglick | 2009-05-05 13:19:49 -0700 (Tue, 05 May 2009) | 2 lines
  
  Two unsuccessful attempts at form validation.
........
  r17770 | jglick | 2009-05-05 13:48:37 -0700 (Tue, 05 May 2009) | 1 line
  
  Better class names.
........
  r17771 | kohsuke | 2009-05-05 14:12:21 -0700 (Tue, 05 May 2009) | 5 lines
  
  brought the form validation check back. The trick is to use the 'value' parameter.
  
  In the end we'd expect this check to go away anyway, as we change input field to drop-down list, but in the mean time, this is why it didn't work.
  
  Maybe we probably need more static code checkers? Like FindBugs rules?
........
  r17901 | kohsuke | 2009-05-09 19:15:40 -0700 (Sat, 09 May 2009) | 1 line
  
  having two toolHome is odd.
........
  r17906 | kohsuke | 2009-05-10 11:09:13 -0700 (Sun, 10 May 2009) | 1 line
  
  adding the generalization of UpdateCenter, but for compatibility reasons, we are leaving UpdateCenter as-is.
........
  r17911 | kohsuke | 2009-05-10 17:02:53 -0700 (Sun, 10 May 2009) | 1 line
  
  integrated json-lib with a bit of bug fix
........
  r17912 | kohsuke | 2009-05-10 17:13:05 -0700 (Sun, 10 May 2009) | 1 line
  
  JDK list is now fetched from hudson.dev.java.net
........
  r17958 | kohsuke | 2009-05-11 13:44:25 -0700 (Mon, 11 May 2009) | 48 lines
  
  Handle AbortException better to avoid error like this:
  
  [INFO] ------------------------------------------------------------------------
  [ERROR] BUILD ERROR
  [INFO] ------------------------------------------------------------------------
  [INFO] Failed to delete directory: C:\files\hudson\workspace\hudson\hudson\main\remoting\target. Reason: Unable to delete file C:\files\hudson\workspace\hudson\hudson\main\remoting\target\remoting-1.304-SNAPSHOT.jar
  
  [INFO] ------------------------------------------------------------------------
  [INFO] For more information, run Maven with the -e switch
  [INFO] ------------------------------------------------------------------------
  [INFO] Total time: 31 seconds
  [INFO] Finished at: Sun May 10 18:15:33 PDT 2009
  [INFO] Final Memory: 25M/48M
  [INFO] ------------------------------------------------------------------------
  Recording fingerprints
  Archiving artifacts
  Recording test results
  ERROR: Failed to archive JUnit reports
  hudson.util.IOException2: remote file operation failed
  	at hudson.FilePath.act(FilePath.java:596)
  	at hudson.tasks.junit.JUnitResultArchiver.perform(JUnitResultArchiver.java:82)
  	at hudson.model.AbstractBuild$AbstractRunner.performAllBuildStep(AbstractBuild.java:372)
  	at hudson.model.AbstractBuild$AbstractRunner.performAllBuildStep(AbstractBuild.java:360)
  	at hudson.model.Build$RunnerImpl.post2(Build.java:183)
  	at hudson.model.AbstractBuild$AbstractRunner.post(AbstractBuild.java:345)
  	at hudson.model.Run.run(Run.java:943)
  	at hudson.model.Build.run(Build.java:112)
  	at hudson.model.ResourceController.execute(ResourceController.java:93)
  	at hudson.model.Executor.run(Executor.java:119)
  Caused by: hudson.AbortException: Test reports were found but none of them are new. Did tests run? 
  For example, \files\hudson\workspace\hudson\hudson\main\core\target\surefire-reports\TEST-hudson.BulkChangeTest.xml is 2 days 23 hr old
  
  	at hudson.tasks.junit.TestResult.parse(TestResult.java:134)
  	at hudson.tasks.junit.TestResult.<init>(TestResult.java:95)
  	at hudson.tasks.junit.JUnitResultArchiver$1.invoke(JUnitResultArchiver.java:95)
  	at hudson.tasks.junit.JUnitResultArchiver$1.invoke(JUnitResultArchiver.java:82)
  	at hudson.FilePath$FileCallableWrapper.call(FilePath.java:1583)
  	at hudson.remoting.UserRequest.perform(UserRequest.java:92)
  	at hudson.remoting.UserRequest.perform(UserRequest.java:46)
  	at hudson.remoting.Request$2.run(Request.java:236)
  	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
  	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
  	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
  	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
  	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
  	at java.lang.Thread.run(Thread.java:619)
........
  r17961 | kohsuke | 2009-05-11 15:16:39 -0700 (Mon, 11 May 2009) | 1 line
  
  adding Windows JDK silent installation
........
  r17962 | kohsuke | 2009-05-11 16:08:13 -0700 (Mon, 11 May 2009) | 1 line
  
  Fixing JDK offline installatino for Windows
........
  r17970 | kohsuke | 2009-05-11 19:37:59 -0700 (Mon, 11 May 2009) | 1 line
  
  Fixing a problem that prevents the usage of this tag in <repeatable> block, which reinserts the same fragment over and over. So statically assigning IDs won't work
........
  r17971 | kohsuke | 2009-05-11 22:17:09 -0700 (Mon, 11 May 2009) | 1 line
  
  continuing to remove static ID generation, which breaks the tag when used inside <repetable> tag.
........
  r17972 | kohsuke | 2009-05-11 22:19:11 -0700 (Mon, 11 May 2009) | 1 line
  
  I don't know when this started, but even in the hudson-dev:run mode, the versionis no longer '?', so we need a different way to force reload
........
  r17973 | kohsuke | 2009-05-11 22:26:34 -0700 (Mon, 11 May 2009) | 1 line
  
  adjusted the text a bit since multiple "add" and "delete"s were rather confusing
........
  r17974 | kohsuke | 2009-05-11 22:31:58 -0700 (Mon, 11 May 2009) | 1 line
  
  added help screen
........
  r17975 | kohsuke | 2009-05-11 22:41:39 -0700 (Mon, 11 May 2009) | 1 line
  
  form validation now works against the checkbox, too
........
  r17976 | kohsuke | 2009-05-11 22:43:00 -0700 (Mon, 11 May 2009) | 1 line
  
  with auto installation, we expect users to specify a directory that doesn't exist yet, so don't report it as an error
........
  r17977 | kohsuke | 2009-05-11 22:43:24 -0700 (Mon, 11 May 2009) | 1 line
  
  improving the text
........


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@17985 71c3de6d-444a-0410-be80-ed276b4c234a
上级 bdd10908
......@@ -345,8 +345,17 @@ THE SOFTWARE.
<groupId>commons-jexl</groupId>
<artifactId>commons-jexl</artifactId>
</exclusion>
<exclusion><!-- excluded until stapler revs up -->
<groupId>org.kohsuke.stapler</groupId>
<artifactId>json-lib</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency><!-- manually specified until stapler 1.104 -->
<groupId>org.kohsuke.stapler</groupId>
<artifactId>json-lib</artifactId>
<version>2.1-rev2</version>
</dependency>
<dependency>
<groupId>args4j</groupId>
<artifactId>args4j</artifactId>
......@@ -371,7 +380,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>xstream</artifactId>
<version>1.3.1-hudson-1</version>
<version>1.3.1-hudson-2</version>
</dependency>
<dependency>
<groupId>jfree</groupId>
......@@ -702,6 +711,11 @@ THE SOFTWARE.
<artifactId>jinterop-wmi</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.jvnet.hudson</groupId>
<artifactId>htmlunit</artifactId>
<version>2.2-hudson-9</version>
</dependency>
<!-- offline profiler API to put in the classpath if we need it -->
<!--dependency>
......
......@@ -40,7 +40,7 @@ import hudson.remoting.RemoteInputStream;
import hudson.util.IOException2;
import hudson.util.HeadBufferingStream;
import hudson.util.FormValidation;
import hudson.util.jna.GNUCLibrary;
import static hudson.util.jna.GNUCLibrary.LIBC;
import static hudson.Util.fixEmpty;
import static hudson.FilePath.TarCompression.GZIP;
import org.apache.tools.ant.BuildException;
......@@ -88,6 +88,8 @@ import java.util.zip.GZIPOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipInputStream;
import com.sun.jna.Native;
/**
* {@link File} like object with remoting support.
*
......@@ -192,6 +194,11 @@ public final class FilePath implements Serializable {
this.remote = localPath.getPath();
}
/**
* Construct a path starting with a base location.
* @param base starting point for resolution, and defines channel
* @param rel a path which if relative will be resolved against base
*/
public FilePath(FilePath base, String rel) {
this.channel = base.channel;
if(isAbsolute(rel)) {
......@@ -382,7 +389,7 @@ public final class FilePath implements Serializable {
private void unzip(File dir, InputStream in) throws IOException {
dir = dir.getAbsoluteFile(); // without absolutization, getParentFile below seems to fail
ZipInputStream zip = new ZipInputStream(in);
ZipInputStream zip = new ZipInputStream(new BufferedInputStream(in));
java.util.zip.ZipEntry e;
try {
......@@ -408,6 +415,17 @@ public final class FilePath implements Serializable {
}
}
/**
* Absolutizes this {@link FilePath} and returns the new one.
*/
public FilePath absolutize() throws IOException, InterruptedException {
return new FilePath(channel,act(new FileCallable<String>() {
public String invoke(File f, VirtualChannel channel) throws IOException {
return f.getAbsolutePath();
}
}));
}
/**
* Supported tar file compression methods.
*/
......@@ -472,10 +490,11 @@ public final class FilePath implements Serializable {
* that supports upgrade and downgrade. Specifically,
*
* <ul>
* <li>If the target directory doesn't exit {@linkplain #mkdirs() it'll be created}.
* <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,
* 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
......@@ -490,7 +509,21 @@ public final class FilePath implements Serializable {
* @since 1.299
*/
public boolean installIfNecessaryFrom(URL archive, TaskListener listener, String message) throws IOException, InterruptedException {
URLConnection con = archive.openConnection();
URLConnection con;
try {
con = archive.openConnection();
con.connect();
} catch (IOException x) {
if (this.exists()) {
// Cannot connect now, so assume whatever was last unpacked is still OK.
if (listener != null) {
listener.getLogger().println("Skipping installation of " + archive + " to " + remote + ": " + x);
}
return false;
} else {
throw x;
}
}
long sourceTimestamp = con.getLastModified();
FilePath timestamp = this.child(".timestamp");
......@@ -591,6 +624,8 @@ public final class FilePath implements Serializable {
// run this on a remote system
try {
return channel.call(new FileCallableWrapper<T>(callable));
} catch (AbortException e) {
throw e; // pass through so that the caller can catch it as AbortException
} catch (IOException e) {
// wrap it into a new IOException so that we get the caller's stack trace as well.
throw new IOException2("remote file operation failed",e);
......@@ -709,7 +744,9 @@ public final class FilePath implements Serializable {
}
/**
* The same as {@code new FilePath(this,rel)} but more OO.
* The same as {@link FilePath#FilePath(FilePath,String)} but more OO.
* @param rel a relative or absolute path
* @return a file on the same channel
*/
public FilePath child(String rel) {
return new FilePath(this,rel);
......@@ -864,6 +901,24 @@ public final class FilePath implements Serializable {
});
}
/**
* Sets the file permission.
*
* On Windows, no-op.
*
* @since 1.303
*/
public void chmod(final int mask) throws IOException, InterruptedException {
if(!isUnix()) return;
act(new FileCallable<Void>() {
public Void invoke(File f, VirtualChannel channel) throws IOException {
if(LIBC.chmod(f.getAbsolutePath(),mask)!=0)
throw new IOException("Failed to chmod "+f+" : "+LIBC.strerror(Native.getLastError()));
return null;
}
});
}
/**
* List up files and directories in this directory.
*
......@@ -1265,7 +1320,7 @@ public final class FilePath implements Serializable {
f.setLastModified(te.getModTime().getTime());
int mode = te.getMode()&0777;
if(mode!=0 && !Hudson.isWindows()) // be defensive
GNUCLibrary.LIBC.chmod(f.getPath(),mode);
LIBC.chmod(f.getPath(),mode);
}
}
} catch(IOException e) {
......
......@@ -41,6 +41,7 @@ import hudson.model.Project;
import hudson.model.Run;
import hudson.model.TopLevelItem;
import hudson.model.View;
import hudson.model.JDK;
import hudson.search.SearchableModelObject;
import hudson.security.AccessControlled;
import hudson.security.AuthorizationStrategy;
......@@ -70,6 +71,7 @@ import org.apache.commons.jelly.XMLOutput;
import org.apache.commons.jexl.parser.ASTSizeFunction;
import org.apache.commons.jexl.util.Introspector;
import org.jvnet.animal_sniffer.IgnoreJRERequirement;
import org.jvnet.tiger_types.Types;
import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
......@@ -90,6 +92,8 @@ import java.lang.management.ManagementFactory;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
......@@ -145,6 +149,31 @@ public class Functions {
return Util.RFC822_DATETIME_FORMATTER.format(cal.getTime());
}
/**
* Given {@code c=MyList (extends ArrayList<Foo>), base=List}, compute the parameterization of 'base'
* that's assignable from 'c' (in this case {@code List<Foo>}), and return its n-th type parameter
* (n=0 would return {@code Foo}).
*
* <p>
* This method is useful for doing type arithmetic.
*
* @throws AssertionError
* if c' is not parameterized.
*/
public static <B> Class getTypeParameter(Class<? extends B> c, Class<B> base, int n) {
Type parameterization = Types.getBaseClass(c,base);
if (parameterization instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) parameterization;
return Types.erasure(Types.getTypeArgument(pt,n));
} else {
throw new AssertionError(c+" doesn't properly parameterize "+base);
}
}
public JDK.DescriptorImpl getJDKDescriptor() {
return Hudson.getInstance().getDescriptorByType(JDK.DescriptorImpl.class);
}
/**
* Prints the integer as a string that represents difference,
* like "-5", "+/-0", "+3".
......@@ -366,7 +395,7 @@ public class Functions {
/**
* Set to true if you need to use the debug version of YUI.
*/
public static boolean DEBUG_YUI = System.getProperty("debug.YUI")!=null;
public static boolean DEBUG_YUI = Boolean.getBoolean("debug.YUI");
/**
* Creates a sub map by using the given range (both ends inclusive).
......@@ -819,6 +848,14 @@ public class Functions {
return buf.toString();
}
/**
* Converts "abc" to "Abc".
*/
public static String capitalize(String s) {
if(s==null || s.length()==0) return s;
return Character.toUpperCase(s.charAt(0))+s.substring(1);
}
public static String getVersion() {
return Hudson.VERSION;
}
......@@ -1057,6 +1094,8 @@ public class Functions {
* Gets all the {@link PageDecorator}s.
*/
public static List<PageDecorator> getPageDecorators() {
// this method may be called to render start up errors, at which point Hudson doesn't exist yet. see HUDSON-3608
if(Hudson.getInstance()==null) return Collections.emptyList();
return PageDecorator.all();
}
......
......@@ -145,6 +145,13 @@ public abstract class Plugin implements Saveable {
public void stop() throws Exception {
}
/**
* @since 1.233
* @deprecated override {@link #configure(StaplerRequest,JSONObject)} instead
*/
public void configure(JSONObject formData) throws IOException, ServletException, FormException {
}
/**
* Handles the submission for the system configuration.
*
......@@ -170,10 +177,10 @@ public abstract class Plugin implements Saveable {
* <p>
* If you are using this method, you'll likely be interested in
* using {@link #save()} and {@link #load()}.
*
* @since 1.233
* @since XXX
*/
public void configure(JSONObject formData) throws IOException, ServletException, FormException {
public void configure(StaplerRequest req, JSONObject formData) throws IOException, ServletException, FormException {
configure(formData);
}
/**
......
......@@ -436,8 +436,8 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
}
@Override
public EnvVars getEnvironment() throws IOException, InterruptedException {
EnvVars env = super.getEnvironment();
public EnvVars getEnvironment(TaskListener log) throws IOException, InterruptedException {
EnvVars env = super.getEnvironment(log);
env.put("WORKSPACE", getProject().getWorkspace().getRemote());
// servlet container may have set CLASSPATH in its launch script,
// so don't let that inherit to the new child process.
......@@ -448,7 +448,7 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
if(jdk != null) {
Computer computer = Computer.currentComputer();
if (computer != null) { // just in case were not in a build
jdk = jdk.forNode(computer.getNode());
jdk = jdk.forNode(computer.getNode(), log);
}
jdk.buildEnvVars(env);
}
......
......@@ -260,7 +260,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable {
StaplerRequest req = Stapler.getCurrentRequest();
Ancestor a = req.findAncestor(DescriptorByNameOwner.class);
// a is always non-null because we already have Hudson as the sentinel
return singleQuote(a.getUrl()+"/descriptorByName/"+clazz.getName()+"/check"+capitalizedFieldName+"?value=")+"+encode(this.value)";
return singleQuote(a.getUrl()+"/descriptorByName/"+clazz.getName()+"/check"+capitalizedFieldName+"?value=")+"+toValue(this)";
}
/**
......
package hudson.model;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.util.QuotedStringTokenizer;
import hudson.util.TextFile;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.Stapler;
import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
import net.sf.json.JSONObject;
/**
* Service for plugins to periodically retrieve update data files
* (like the one in the update center) through browsers.
*
* <p>
* Because the retrieval of the file goes through XmlHttpRequest,
* we cannot reliably pass around binary.
*
* @author Kohsuke Kawaguchi
*/
@Extension
public class DownloadService extends PageDecorator {
public DownloadService() {
super(DownloadService.class);
}
/**
* Builds up an HTML fragment that starts all the download jobs.
*/
public String generateFragment() {
StringBuilder buf = new StringBuilder();
long now = System.currentTimeMillis();
for (Downloadable d : Downloadable.all()) {
if(d.getDue()<now) {
buf.append("<script>downloadService.download(")
.append(QuotedStringTokenizer.quote(d.getId()))
.append(',')
.append(QuotedStringTokenizer.quote(d.getUrl()))
.append(',')
.append("{version:"+QuotedStringTokenizer.quote(Hudson.VERSION)+'}')
.append(',')
.append(QuotedStringTokenizer.quote(Stapler.getCurrentRequest().getContextPath()+'/'+getUrl()+"/byId/"+d.getId()+"/postBack"))
.append(',')
.append("null);</script>");
}
}
return buf.toString();
}
/**
* Gets {@link Downloadable} by its ID.
* Used to bind them to URL.
*/
public Downloadable getById(String id) {
for (Downloadable d : Downloadable.all())
if(d.getId().equals(id))
return d;
return null;
}
public static abstract class Downloadable implements ExtensionPoint {
private final String id;
private final String url;
private final long interval;
private volatile long due=0;
/**
*
* @param url
* URL relative to {@link UpdateCenter#getUrl()}.
* So if this string is "foo.json", the ultimate URL will be
* something like "https://hudson.dev.java.net/foo.json"
*
* For security and privacy reasons, we don't allow the retrieval
* from random locations.
*/
protected Downloadable(String id, String url, long interval) {
this.id = id;
this.url = url;
this.interval = interval;
}
public String getId() {
return id;
}
/**
* URL to download.
*/
public String getUrl() {
return Hudson.getInstance().getUpdateCenter().getUrl()+url;
}
/**
* How often do we retrieve the new image?
*
* @return
* number of milliseconds between retrieval.
*/
public long getInterval() {
return interval;
}
/**
* This is where the retrieved file will be stored.
*/
public TextFile getDataFile() {
return new TextFile(new File(Hudson.getInstance().getRootDir(),"updates/"+id));
}
/**
* When shall we retrieve this file next time?
*/
public long getDue() {
if(due==0)
// if the file doesn't exist, this code should result
// in a very small (but >0) due value, which should trigger
// the retrieval immediately.
due = getDataFile().file.lastModified()+interval;
return due;
}
/**
* Loads the current file into JSON and returns it, or null
* if no data exists.
*/
public JSONObject getData() throws IOException {
TextFile df = getDataFile();
if(df.exists())
return JSONObject.fromObject(df.read());
return null;
}
/**
* This is where the browser sends us the data.
*/
public void doPostBack(@QueryParameter String json) throws IOException {
long dataTimestamp = System.currentTimeMillis();
TextFile df = getDataFile();
df.write(json);
df.file.setLastModified(dataTimestamp);
due = dataTimestamp+getInterval();
LOGGER.info("Obtained the updated data file for "+id);
}
/**
* Returns all the registered {@link Downloadable}s.
*/
public static ExtensionList<Downloadable> all() {
return Hudson.getInstance().getExtensionList(Downloadable.class);
}
private static final Logger LOGGER = Logger.getLogger(Downloadable.class.getName());
}
}
......@@ -61,7 +61,7 @@ public class ExternalRun extends Run<ExternalJob,ExternalRun> {
public void run(final String[] cmd) {
run(new Runner() {
public Result run(BuildListener listener) throws Exception {
Proc proc = new Proc.LocalProc(cmd,getEnvironment(),System.in,new DualOutputStream(System.out,listener.getLogger()));
Proc proc = new Proc.LocalProc(cmd,getEnvironment(listener),System.in,new DualOutputStream(System.out,listener.getLogger()));
return proc.join()==0?Result.SUCCESS:Result.FAILURE;
}
......
......@@ -2182,17 +2182,8 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
systemMessage = Util.nullify(req.getParameter("system_message"));
{// update JDK installations
jdks.clear();
String[] names = req.getParameterValues("jdk_name");
String[] homes = req.getParameterValues("jdk_home");
if(names!=null && homes!=null) {
int len = Math.min(names.length,homes.length);
for(int i=0;i<len;i++) {
jdks.add(new JDK(names[i],homes[i]));
}
}
}
jdks.clear();
jdks.addAll(req.bindJSONToList(JDK.class,json.get("jdks")));
boolean result = true;
......@@ -2218,7 +2209,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
result &= configureDescriptor(req,json,d);
for( JSONObject o : StructuredForm.toList(json,"plugin"))
pluginManager.getPlugin(o.getString("name")).getPlugin().configure(o);
pluginManager.getPlugin(o.getString("name")).getPlugin().configure(req, o);
clouds.rebuildHetero(req,json, Cloud.all(), "cloud");
......@@ -2902,24 +2893,6 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
rsp.getWriter().println("Invoked");
}
/**
* Checks if the JAVA_HOME is a valid JAVA_HOME path.
*/
public FormValidation doJavaHomeCheck(@QueryParameter File value) {
// this can be used to check the existence of a file on the server, so needs to be protected
checkPermission(ADMINISTER);
if(!value.isDirectory())
return FormValidation.error(Messages.Hudson_NotADirectory(value));
File toolsJar = new File(value,"lib/tools.jar");
File mac = new File(value,"lib/dt.jar");
if(!toolsJar.exists() && !mac.exists())
return FormValidation.error(Messages.Hudson_NotJDKDir(value));
return FormValidation.ok();
}
/**
* If the user chose the default JDK, make sure we got 'java' in PATH.
*/
......@@ -3338,7 +3311,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
context.setAttribute("version",ver);
VERSION_HASH = Util.getDigestOf(ver).substring(0, 8);
if(ver.equals("?"))
if(ver.equals("?") || Boolean.getBoolean("hudson.script.noCache"))
RESOURCE_PATH = "";
else
RESOURCE_PATH = "/static/"+VERSION_HASH;
......
......@@ -25,18 +25,25 @@ package hudson.model;
import hudson.util.StreamTaskListener;
import hudson.util.NullStream;
import hudson.util.FormValidation;
import hudson.Launcher;
import hudson.Extension;
import hudson.EnvVars;
import hudson.slaves.NodeSpecific;
import hudson.tools.ToolInstallation;
import hudson.tools.ToolDescriptor;
import hudson.tools.ToolLocationNodeProperty;
import hudson.tools.ToolProperty;
import hudson.tools.JDKInstaller;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.List;
import java.util.Arrays;
import java.util.Collections;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
/**
* Information about JDK installation.
......@@ -48,17 +55,26 @@ public final class JDK extends ToolInstallation implements NodeSpecific<JDK>, En
private String javaHome;
public JDK(String name, String javaHome) {
super(name, javaHome);
super(name, javaHome, Collections.<ToolProperty<?>>emptyList());
}
@DataBoundConstructor
public JDK(String name, String home, List<? extends ToolProperty<?>> properties) {
super(name, home, properties);
}
/**
* install directory.
*
* @deprecated as of 1.304
* Use {@link #getHome()}
*/
public String getJavaHome() {
return getHome();
}
public String getHome() {
@SuppressWarnings({"deprecation"})
public @Override String getHome() {
if (javaHome != null) return javaHome;
return super.getHome();
}
......@@ -67,7 +83,7 @@ public final class JDK extends ToolInstallation implements NodeSpecific<JDK>, En
* Gets the path to the bin directory.
*/
public File getBinDir() {
return new File(getJavaHome(),"bin");
return new File(getHome(),"bin");
}
/**
* Gets the path to 'java'.
......@@ -79,7 +95,7 @@ public final class JDK extends ToolInstallation implements NodeSpecific<JDK>, En
else
execName = "java";
return new File(getJavaHome(),"bin/"+execName);
return new File(getHome(),"bin/"+execName);
}
/**
......@@ -95,11 +111,11 @@ public final class JDK extends ToolInstallation implements NodeSpecific<JDK>, En
public void buildEnvVars(Map<String,String> env) {
// see EnvVars javadoc for why this adss PATH.
env.put("PATH+JDK",getBinDir().getPath());
env.put("JAVA_HOME",getJavaHome());
env.put("JAVA_HOME",getHome());
}
public JDK forNode(Node node) {
return new JDK(getName(),translateFor(node));
public JDK forNode(Node node, TaskListener log) throws IOException, InterruptedException {
return new JDK(getName(), translateFor(node, log));
}
public JDK forEnvironment(EnvVars environment) {
......@@ -132,16 +148,38 @@ public final class JDK extends ToolInstallation implements NodeSpecific<JDK>, En
return "Java Development Kit";
}
public JDK[] getInstallations() {
public @Override JDK[] getInstallations() {
return Hudson.getInstance().getJDKs().toArray(new JDK[0]);
}
// this isn't really synchronized well since the list is Hudson.jdks :(
public synchronized void setInstallations(JDK... jdks) {
public @Override synchronized void setInstallations(JDK... jdks) {
List<JDK> list = Hudson.getInstance().getJDKs();
list.clear();
for (JDK jdk: jdks) list.add(jdk);
list.addAll(Arrays.asList(jdks));
}
@Override
public List<JDKInstaller> getDefaultInstallers() {
return Collections.singletonList(new JDKInstaller(null,false));
}
/**
* Checks if the JAVA_HOME is a valid JAVA_HOME path.
*/
public FormValidation doCheckHome(@QueryParameter File value) {
// this can be used to check the existence of a file on the server, so needs to be protected
Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
if(value.exists() && !value.isDirectory())
return FormValidation.error(Messages.Hudson_NotADirectory(value));
File toolsJar = new File(value,"lib/tools.jar");
File mac = new File(value,"lib/dt.jar");
if(!toolsJar.exists() && !mac.exists())
return FormValidation.error(Messages.Hudson_NotJDKDir(value));
return FormValidation.ok();
}
}
}
......@@ -27,7 +27,6 @@ import hudson.ExtensionPoint;
import hudson.FilePath;
import hudson.FileSystemProvisioner;
import hudson.Launcher;
import hudson.tools.ToolLocationNodeProperty;
import hudson.node_monitors.NodeMonitor;
import hudson.remoting.VirtualChannel;
import hudson.security.ACL;
......
......@@ -92,6 +92,17 @@ public abstract class PageDecorator extends Descriptor<PageDecorator> implements
return "";
}
/**
* Obtains the URL of this object, excluding the context path.
*
* <p>
* Every {@link PageDecorator} is bound to URL via {@link Hudson#getDescriptor()}.
* This method returns such an URL.
*/
public final String getUrl() {
return "descriptor/"+clazz.getName();
}
/**
* All the registered instances.
* @deprecated as of 1.286
......
......@@ -41,13 +41,11 @@ import hudson.security.ACL;
import hudson.security.AccessControlled;
import hudson.security.Permission;
import hudson.security.PermissionGroup;
import hudson.tasks.BuildStep;
import hudson.tasks.BuildWrapper;
import hudson.tasks.LogRotator;
import hudson.tasks.Mailer;
import hudson.tasks.test.AbstractTestResultAction;
import hudson.util.IOException2;
import hudson.util.ProcessTreeKiller;
import hudson.util.LogTaskListener;
import hudson.util.XStream2;
import java.io.BufferedReader;
......@@ -1274,6 +1272,13 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
}
}
/**
* @deprecated as of XXX use {@link #getEnvironment(TaskListener)}
*/
public EnvVars getEnvironment() throws IOException, InterruptedException {
return getEnvironment(new LogTaskListener(LOGGER, Level.INFO));
}
/**
* Returns the map that contains environmental variables to be used for launching
* processes for this build.
......@@ -1287,7 +1292,7 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
* Unlike earlier {@link #getEnvVars()}, this map contains the whole environment,
* not just the overrides, so one can introspect values to change its behavior.
*/
public EnvVars getEnvironment() throws IOException, InterruptedException {
public EnvVars getEnvironment(TaskListener log) throws IOException, InterruptedException {
EnvVars env = Computer.currentComputer().getEnvironment().overrideAll(getCharacteristicEnvVars());
String rootUrl = Hudson.getInstance().getRootUrl();
if(rootUrl!=null)
......
......@@ -36,7 +36,6 @@ import hudson.tasks.Builder;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
/**
* Extensible property of {@link Node}.
......@@ -106,12 +105,6 @@ public abstract class NodeProperty<N extends Node> implements Describable<NodePr
* given project.
*/
public static List<NodePropertyDescriptor> for_(Node node) {
List<NodePropertyDescriptor> result = new ArrayList<NodePropertyDescriptor>();
for (NodePropertyDescriptor npd : all()) {
if (npd.isApplicable(node.getClass())) {
result.add(npd);
}
}
return result;
return NodePropertyDescriptor.for_(all(),node);
}
}
......@@ -23,14 +23,9 @@
*/
package hudson.slaves;
import hudson.model.Descriptor;
import hudson.model.Node;
import hudson.Extension;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.jvnet.tiger_types.Types;
import hudson.model.Node;
import hudson.tools.PropertyDescriptor;
/**
* Descriptor for {@link NodeProperty}.
......@@ -41,28 +36,11 @@ import org.jvnet.tiger_types.Types;
* @since 1.286
* @see NodeProperty
*/
public abstract class NodePropertyDescriptor extends Descriptor<NodeProperty<?>> {
/**
* Returns true if this {@link NodeProperty} type is applicable to the
* given node type.
*
* <p>
* The default implementation of this method checks if the given node type is assignable to 'N' of
* {@link NodeProperty}<tt>&lt;N></tt>, but subtypes can extend this to change this behavior.
*
* @return
* true to indicate applicable, in which case the property will be
* displayed in the configuration screen of this node.
*/
public boolean isApplicable(Class<? extends Node> nodeType) {
Type parameterization = Types.getBaseClass(clazz, NodeProperty.class);
if (parameterization instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) parameterization;
Class<?> applicable = Types.erasure(Types.getTypeArgument(pt, 0));
return applicable.isAssignableFrom(nodeType);
} else {
throw new AssertionError(clazz+" doesn't properly parameterize NodeProperty. The isApplicable() method must be overriden.");
}
public abstract class NodePropertyDescriptor extends PropertyDescriptor<NodeProperty<?>,Node> {
protected NodePropertyDescriptor(Class<? extends NodeProperty<?>> clazz) {
super(clazz);
}
protected NodePropertyDescriptor() {
}
}
......@@ -26,6 +26,8 @@ package hudson.slaves;
import hudson.model.Node;
import hudson.model.EnvironmentSpecific;
import hudson.model.TaskListener;
import java.io.IOException;
/**
* Represents any concept that can be adapted for a node.
......@@ -42,5 +44,5 @@ public interface NodeSpecific<T extends NodeSpecific<T>> {
/**
* Returns a specialized copy of T for functioning in the given node.
*/
T forNode(Node node);
T forNode(Node node, TaskListener log) throws IOException, InterruptedException;
}
......@@ -133,13 +133,13 @@ public class Ant extends Builder {
ArgumentListBuilder args = new ArgumentListBuilder();
EnvVars env = build.getEnvironment();
EnvVars env = build.getEnvironment(listener);
AntInstallation ai = getAnt();
if(ai==null) {
args.add(launcher.isUnix() ? "ant" : "ant.bat");
} else {
ai = ai.forNode(Computer.currentComputer().getNode());
ai = ai.forNode(Computer.currentComputer().getNode(), listener);
ai = ai.forEnvironment(env);
String exe = ai.getExecutable(launcher);
if (exe==null) {
......@@ -391,8 +391,8 @@ public class Ant extends Builder {
return new AntInstallation(getName(), environment.expand(antHome));
}
public AntInstallation forNode(Node node) {
return new AntInstallation(getName(),translateFor(node));
public AntInstallation forNode(Node node, TaskListener log) throws IOException, InterruptedException {
return new AntInstallation(getName(), translateFor(node, log));
}
@Extension
......
......@@ -75,7 +75,7 @@ public abstract class CommandInterpreter extends Builder {
int r;
try {
EnvVars envVars = build.getEnvironment();
EnvVars envVars = build.getEnvironment(listener);
// on Windows environment variables are converted to all upper case,
// but no such conversions are done on Unix, so to make this cross-platform,
// convert variables to all upper cases.
......
......@@ -38,12 +38,12 @@ import hudson.model.Computer;
import hudson.model.EnvironmentSpecific;
import hudson.model.Node;
import hudson.model.Hudson;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.VirtualChannel;
import hudson.slaves.NodeSpecific;
import hudson.tools.ToolDescriptor;
import hudson.tools.ToolInstallation;
import hudson.tools.ToolLocationNodeProperty;
import hudson.util.ArgumentListBuilder;
import hudson.util.NullStream;
import hudson.util.StreamTaskListener;
......@@ -178,7 +178,7 @@ public class Maven extends Builder {
VariableResolver<String> vr = build.getBuildVariableResolver();
EnvVars env = build.getEnvironment();
EnvVars env = build.getEnvironment(listener);
String targets = Util.replaceMacro(this.targets,vr);
targets = env.expand(targets);
......@@ -205,7 +205,7 @@ public class Maven extends Builder {
String execName = proj.getWorkspace().act(new DecideDefaultMavenCommand(normalizedTarget));
args.add(execName);
} else {
mi = mi.forNode(Computer.currentComputer().getNode());
mi = mi.forNode(Computer.currentComputer().getNode(), listener);
mi = mi.forEnvironment(env);
String exec = mi.getExecutable(launcher);
if(exec==null) {
......@@ -409,8 +409,8 @@ public class Maven extends Builder {
return new MavenInstallation(getName(), environment.expand(getHome()));
}
public MavenInstallation forNode(Node node) {
return new MavenInstallation(getName(),translateFor(node));
public MavenInstallation forNode(Node node, TaskListener log) throws IOException, InterruptedException {
return new MavenInstallation(getName(), translateFor(node, log));
}
@Extension
......
/*
* The MIT License
*
* Copyright (c) 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.
*/
package hudson.tools;
import hudson.Extension;
import hudson.FilePath;
import hudson.model.Node;
import hudson.model.TaskListener;
import hudson.tasks.CommandInterpreter;
import hudson.util.FormValidation;
import java.io.IOException;
import java.util.Collections;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
/**
* Installs a tool by running an arbitrary shell command.
*/
public class CommandInstaller extends ToolInstaller {
/**
* Command to execute, similar to {@link CommandInterpreter#command}.
*/
private final String command;
@DataBoundConstructor
public CommandInstaller(String label, String command) {
super(label);
this.command = command;
}
public String getCommand() {
return command;
}
public FilePath performInstallation(ToolInstallation tool, Node node, TaskListener log) throws IOException, InterruptedException {
FilePath tools = node.getRootPath().child("tools");
// XXX support Windows batch scripts, Unix scripts with interpreter line, etc. (see CommandInterpreter subclasses)
FilePath script = tools.createTextTempFile("hudson", ".sh", command);
try {
String[] cmd = {"sh", "-e", script.getRemote()};
// XXX it always logs at least: "INFO: [tools] $ sh -e /hudson/tools/hudson8889216416382058262.sh"
int r = node.createLauncher(log).launch(cmd, Collections.<String,String>emptyMap(), log.getLogger(), tools).join();
if (r != 0) {
throw new IOException("Command returned status " + r);
}
} finally {
script.delete();
}
return node.createPath(tool.getHome());
}
@Extension
public static class DescriptorImpl extends ToolInstallerDescriptor<CommandInstaller> {
public String getDisplayName() {
return Messages.CommandInstaller_DescriptorImpl_displayName();
}
public FormValidation doCheckCommand(@QueryParameter String value) {
if (value.length() > 0) {
return FormValidation.ok();
} else {
return FormValidation.error(Messages.CommandInstaller_no_command());
}
}
}
}
/*
* The MIT License
*
* Copyright (c) 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.
*/
package hudson.tools;
import hudson.Extension;
import hudson.util.DescribableList;
import hudson.model.Descriptor;
import hudson.model.Saveable;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.List;
import java.io.IOException;
/**
* {@link ToolProperty} that shows auto installation options.
*
* @author Kohsuke Kawaguchi
*/
public class InstallSourceProperty extends ToolProperty<ToolInstallation> {
// TODO: get the proper Saveable
public final DescribableList<ToolInstaller, Descriptor<ToolInstaller>> installers =
new DescribableList<ToolInstaller, Descriptor<ToolInstaller>>(Saveable.NOOP);
@DataBoundConstructor
public InstallSourceProperty(List<? extends ToolInstaller> installers) throws IOException {
this.installers.replaceBy(installers);
}
@Override
public void setTool(ToolInstallation t) {
super.setTool(t);
for (ToolInstaller installer : installers)
installer.setTool(t);
}
public Class<ToolInstallation> type() {
return ToolInstallation.class;
}
@Extension
public static class DescriptorImpl extends ToolPropertyDescriptor {
public String getDisplayName() {
return "Install automatically";
}
}
}
/*
* The MIT License
*
* Copyright (c) 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.
*/
package hudson.tools;
import hudson.Extension;
import hudson.model.Node;
import hudson.model.TaskListener;
import java.io.IOException;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Semaphore;
/**
* Actually runs installations.
*/
@Extension
public class InstallerTranslator extends ToolLocationTranslator {
private static final Map<Node,Map<ToolInstallation,Semaphore>> mutexByNode = new WeakHashMap<Node,Map<ToolInstallation,Semaphore>>();
public String getToolHome(Node node, ToolInstallation tool, TaskListener log) throws IOException, InterruptedException {
InstallSourceProperty isp = tool.getProperties().get(InstallSourceProperty.class);
if (isp == null) {
return null;
}
for (ToolInstaller installer : isp.installers) {
if (installer.appliesTo(node)) {
Map<ToolInstallation, Semaphore> mutexByTool = mutexByNode.get(node);
if (mutexByTool == null) {
mutexByNode.put(node, mutexByTool = new WeakHashMap<ToolInstallation, Semaphore>());
}
Semaphore semaphore = mutexByTool.get(tool);
if (semaphore == null) {
mutexByTool.put(tool, semaphore = new Semaphore(1));
}
semaphore.acquire();
try {
return installer.performInstallation(tool, node, log).getRemote();
} finally {
semaphore.release();
}
}
}
return null;
}
}
/*
* The MIT License
*
* Copyright (c) 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.
*/
package hudson.tools;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlOption;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlSelect;
import hudson.AbortException;
import hudson.Extension;
import hudson.FilePath;
import hudson.Util;
import hudson.util.FormValidation;
import hudson.util.TimeUnit2;
import hudson.util.ArgumentListBuilder;
import hudson.FilePath.FileCallable;
import hudson.model.Node;
import hudson.model.TaskListener;
import hudson.model.DownloadService.Downloadable;
import static hudson.tools.JDKInstaller.Preference.*;
import hudson.remoting.Callable;
import hudson.remoting.VirtualChannel;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Arrays;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sf.json.JSONObject;
/**
* Install JDKs from java.sun.com.
*
* @author Kohsuke Kawaguchi
*/
public class JDKInstaller extends ToolInstaller {
/**
* The release ID that Sun assigns to each JDK, such as "jdk-6u13-oth-JPR@CDS-CDS_Developer"
*
* <p>
* This ID can be seen in the "ProductRef" query parameter of the download page, like
* https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=jdk-6u13-oth-JPR@CDS-CDS_Developer
*/
public final String id;
/**
* We require that the user accepts the license by clicking a checkbox, to make up for the part
* that we auto-accept cds.sun.com license click through.
*/
public final boolean acceptLicense;
@DataBoundConstructor
public JDKInstaller(String id, boolean acceptLicense) {
super(null);
this.id = id;
this.acceptLicense = acceptLicense;
}
public FilePath performInstallation(ToolInstallation tool, Node node, TaskListener log) throws IOException, InterruptedException {
FilePath expectedLocation = node.createPath(tool.getHome());
PrintStream out = log.getLogger();
try {
if(!acceptLicense) {
out.println("Unable to perform installation until the license is accepted.");
return expectedLocation;
}
// already installed?
FilePath marker = expectedLocation.child(".installedByHudson");
if(marker.exists())
return expectedLocation;
expectedLocation.mkdirs();
Platform p = Platform.of(node);
URL url = locate(log, p, CPU.of(node));
out.println("Downloading "+url);
FilePath file = expectedLocation.child(fileName(p));
file.copyFrom(url);
out.println("Installing "+file);
switch (p) {
case LINUX:
case SOLARIS:
file.chmod(0755);
if(node.createLauncher(log).launch(new String[]{file.getRemote(),"-noregister"},new String[0],new ByteArrayInputStream("yes".getBytes()),out,expectedLocation).join()!=0)
throw new AbortException("Failed to install JDK");
// JDK creates its own sub-directory, so pull them up
List<FilePath> paths = expectedLocation.list(JDK_FINDER);
if(paths.size()!=1)
throw new AbortException("Failed to find the extracted JDKs: "+paths);
paths.get(0).act(PULLUP_DIRECTORY);
// clean up
paths.get(0).delete();
break;
case WINDOWS:
/*
Windows silent installation is full of bad know-how.
On Windows, command line argument to a process at the OS level is a single string,
not a string array like POSIX. When we pass arguments as string array, JRE eventually
turn it into a single string with adding quotes to "the right place". Unfortunately,
with the strange argument layout of InstallShield (like /v/qn" INSTALLDIR=foobar"),
it appears that the escaping done by JRE gets in the way, and prevents the installation.
Presumably because of this, my attempt to use /q/vn" INSTALLDIR=foo" didn't work with JDK5.
I tried to locate exactly how InstallShield parses the arguments (and why it uses
awkward option like /qn, but couldn't find any. Instead, experiments revealed that
"/q/vn ARG ARG ARG" works just as well. This is presumably due to the Visual C++ runtime library
(which does single string -> string array conversion to invoke the main method in most Win32 process),
and this consistently worked on JDK5 and JDK4.
Some of the official documentations are available at
- 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
*/
// see
//
FilePath logFile = node.getRootPath().createTempFile("jdk-install",".log");
// JDK6u13 doesn't like path representation like "/tmp/foo", so make it a strict Windows format
String normalizedPath = expectedLocation.absolutize().getRemote();
ArgumentListBuilder args = new ArgumentListBuilder();
args.add(file.getRemote());
args.add("/s");
args.add("/v/qn REBOOT=Suppress INSTALLDIR="+normalizedPath+" /L "+logFile.getRemote());
if(node.createLauncher(log).launch(args.toCommandArray(),new String[0],out,expectedLocation).join()!=0) {
out.println("Failed to install JDK");
// log file is in UTF-16
InputStreamReader in = new InputStreamReader(logFile.read(), "UTF-16");
try {
IOUtils.copy(in,new OutputStreamWriter(out));
} finally {
in.close();
}
throw new AbortException();
}
logFile.delete();
break;
}
// successfully installed
file.delete();
marker.touch(System.currentTimeMillis());
} catch (DetectionFailedException e) {
out.println("JDK installation skipped: "+e.getMessage());
}
return expectedLocation;
}
/**
* Choose the file name suitable for the downloaded JDK bundle.
*/
private String fileName(Platform p) {
switch (p) {
case LINUX:
case SOLARIS:
return "jdk.sh";
case WINDOWS:
return "jdk.exe";
}
throw new AssertionError();
}
/**
* Finds the directory that JDK has created.
*/
private static final FileFilter JDK_FINDER = new FileFilter() {
public boolean accept(File f) {
return f.isDirectory() && f.getName().startsWith("jdk");
}
};
/**
* Moves all the contents of this directory into ".."
*/
private static final FileCallable<Void> PULLUP_DIRECTORY = new FileCallable<Void>() {
public Void invoke(File f, VirtualChannel channel) throws IOException {
File p = f.getParentFile();
for(File child : f.listFiles()) {
File target = new File(p, child.getName());
if(!child.renameTo(target))
throw new IOException("Failed to rename "+child+" to "+target);
}
return null;
}
};
/**
* Performs a license click through and obtains the one-time URL for downloading bits.
*
*/
private URL locate(TaskListener log, Platform platform, CPU cpu) throws IOException {
final PrintStream out = log.getLogger();
final WebClient wc = new WebClient();
wc.setJavaScriptEnabled(false);
wc.setCssEnabled(false);
out.println("Visiting http://cds.sun.com/ for download");
HtmlPage p = (HtmlPage)wc.getPage("https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef="+id);
HtmlForm form = p.getFormByName("aForm");
((HtmlInput)p.getElementById("dnld_license")).click();
// pick the right download. to make the comparison more robust, we do it in the upper case
HtmlOption primary=null,secondary=null;
HtmlSelect platformChoice = (HtmlSelect) p.getElementById("dnld_platform");
for(HtmlOption opt : platformChoice.getOptions()) {
String value = opt.getValueAttribute().toUpperCase(Locale.ENGLISH);
if(!platform.is(value)) continue;
switch (cpu.accept(value)) {
case PRIMARY: primary = opt;break;
case SECONDARY: secondary=opt;break;
case UNACCEPTABLE: break;
}
}
if(primary==null) primary=secondary;
if(primary==null)
throw new AbortException("Couldn't find the right download for "+platform+" and "+ cpu +" combination");
((HtmlSelect)p.getElementById("dnld_platform")).setSelectedAttribute(primary,true);
p = (HtmlPage)form.submit();
out.println("Choosing the download bundle");
List<String> urls = new ArrayList<String>();
// for some reason, the <style> tag in the middle of the page confuses HtmlUnit,
// so reverting to text parsing.
Matcher m = Pattern.compile("<a href=\"(http://cds.sun.com/[^\"]+/VerifyItem-Start[^\"]+)\"").matcher(p.getDocumentElement().getTextContent());
while(m.find()) {
String url = m.group(1);
// still more options to choose from.
// avoid rpm bundles, and avoid tar.Z bundle
if(url.contains("rpm")) continue;
if(url.contains("tar.Z")) continue;
// sparcv9 bundle is add-on to the sparc bundle, so just download 32bit sparc bundle, even on 64bit system
if(url.contains("sparcv9")) continue;
urls.add(url);
LOGGER.fine("Found a download candidate: "+url);
}
// prefer the first match because sometimes "optional downloads" follow the main bundle
return new URL(urls.get(0));
}
public enum Preference {
PRIMARY, SECONDARY, UNACCEPTABLE
}
/**
* Supported platform.
*/
public enum Platform {
LINUX, SOLARIS, WINDOWS;
public boolean is(String line) {
return line.contains(name());
}
/**
* Determines the platform of the given node.
*/
public static Platform of(Node n) throws IOException,InterruptedException,DetectionFailedException {
return n.toComputer().getChannel().call(new Callable<Platform,DetectionFailedException>() {
public Platform call() throws DetectionFailedException {
return current();
}
});
}
public static Platform current() throws DetectionFailedException {
String arch = System.getProperty("os.name").toLowerCase();
if(arch.contains("linux")) return LINUX;
if(arch.contains("windows")) return WINDOWS;
if(arch.contains("sun") || arch.contains("solaris")) return SOLARIS;
throw new DetectionFailedException("Unknown CPU name: "+arch);
}
}
/**
* CPU type.
*/
public enum CPU {
i386, amd64, Sparc, Itanium;
/**
* In JDK5u3, I see platform like "Linux AMD64", while JDK6u3 refers to "Linux x64", so
* just use "64" for locating bits.
*/
public Preference accept(String line) {
switch (this) {
// these two guys are totally incompatible with everything else, so no fallback
case Sparc: return must(line.contains("SPARC"));
case Itanium: return must(line.contains("ITANIUM"));
// 64bit Solaris, Linux, and Windows can all run 32bit executable, so fall back to 32bit if 64bit bundle is not found
case amd64:
if(line.contains("64")) return PRIMARY;
if(line.contains("SPARC") || line.contains("ITANIUM")) return UNACCEPTABLE;
return SECONDARY;
case i386:
if(line.contains("64") || line.contains("SPARC") || line.contains("ITANIUM")) return UNACCEPTABLE;
return PRIMARY;
}
return UNACCEPTABLE;
}
private static Preference must(boolean b) {
return b ? PRIMARY : UNACCEPTABLE;
}
/**
* Determines the CPU of the given node.
*/
public static CPU of(Node n) throws IOException,InterruptedException, DetectionFailedException {
return n.toComputer().getChannel().call(new Callable<CPU,DetectionFailedException>() {
public CPU call() throws DetectionFailedException {
return current();
}
});
}
/**
* Determines the CPU of the current JVM.
*
* http://lopica.sourceforge.net/os.html was useful in writing this code.
*/
public static CPU current() throws DetectionFailedException {
String arch = System.getProperty("os.arch").toLowerCase();
if(arch.contains("sparc")) return Sparc;
if(arch.contains("ia64")) return Itanium;
if(arch.contains("amd64") || arch.contains("86_64")) return amd64;
if(arch.contains("86")) return i386;
throw new DetectionFailedException("Unknown CPU architecture: "+arch);
}
}
/**
* Indicates the failure to detect the OS or CPU.
*/
private static final class DetectionFailedException extends Exception {
private DetectionFailedException(String message) {
super(message);
}
}
public static final class JDKFamilyList {
public JDKFamily[] jdks = new JDKFamily[0];
}
public static final class JDKFamily {
public String name;
public InstallableJDK[] list;
}
public static final class InstallableJDK {
public String name;
/**
* Product code.
*/
public String id;
}
@Extension
public static final class DescriptorImpl extends ToolInstallerDescriptor<JDKInstaller> {
public String getDisplayName() {
return "Install from java.sun.com"; // XXX I18N
}
public FormValidation doCheckId(@QueryParameter String value) {
if (Util.fixEmpty(value) == null) {
return FormValidation.error("Define JDK ID"); // XXX I18N and improve message
} else {
// XXX further checks?
return FormValidation.ok();
}
}
/**
* List of installable JDKs.
* @return never null.
*/
public List<JDKFamily> getInstallableJDKs() throws IOException {
return Arrays.asList(JDKList.all().get(JDKList.class).toList().jdks);
}
public FormValidation doCheckAcceptLicense(@QueryParameter boolean value) {
if (value) {
return FormValidation.ok();
} else {
return FormValidation.error("You must agree to the license to download the JDK."); // XXX I18N
}
}
}
/**
* JDK list.
*/
@Extension
public static final class JDKList extends Downloadable {
public JDKList() {
super(JDKInstaller.class.getName(), "jdk.json", TimeUnit2.DAYS.toMillis(1));
}
public JDKFamilyList toList() throws IOException {
JSONObject d = getData();
if(d==null) return new JDKFamilyList();
return (JDKFamilyList)JSONObject.toBean(d,JDKFamilyList.class);
}
}
private static final Logger LOGGER = Logger.getLogger(JDKInstaller.class.getName());
}
package hudson.tools;
import hudson.Functions;
import hudson.model.Describable;
import hudson.model.Descriptor;
import java.util.ArrayList;
import java.util.List;
/**
* Base {@link Descriptor} type used for {@code XyzProperty} classes.
*
* @param <P>
* Type of the {@code XyzProperty}. Called 'property type'
* @param <T>
* Type of the {@code Xyz}, that the property attaches to. Called 'target type'
* @author Kohsuke Kawaguchi
*/
public abstract class PropertyDescriptor<P extends Describable<P>,T> extends Descriptor<P> {
protected PropertyDescriptor(Class<? extends P> clazz) {
super(clazz);
}
protected PropertyDescriptor() {
}
/**
* Infer the type parameterization 'P'
*/
private Class<P> getP() {
return Functions.getTypeParameter(getClass(),Descriptor.class,0);
}
/**
* Returns true if this property type is applicable to the
* given target type.
*
* <p>
* The default implementation of this method checks if the given node type is assignable
* according to the parameterization, but subtypes can extend this to change this behavior.
*
* @return
* true to indicate applicable, in which case the property will be
* displayed in the configuration screen of the target, for example.
*/
public boolean isApplicable(Class<? extends T> targetType) {
Class<? extends T> applicable = Functions.getTypeParameter(clazz,getP(),0);
return applicable.isAssignableFrom(targetType);
}
public static <D extends PropertyDescriptor<?,T>,T> List<D> for_(List<D> all, Class<? extends T> target) {
List<D> result = new ArrayList<D>();
for (D d : all)
if (d.isApplicable(target))
result.add(d);
return result;
}
public static <D extends PropertyDescriptor<?,T>,T> List<D> for_(List<D> all, T target) {
return for_(all,(Class)target.getClass());
}
}
......@@ -25,6 +25,11 @@
package hudson.tools;
import hudson.model.Descriptor;
import hudson.util.DescribableList;
import java.util.Collections;
import java.util.List;
import java.io.IOException;
/**
* {@link Descriptor} for {@link ToolInstallation}.
......@@ -39,20 +44,53 @@ public abstract class ToolDescriptor<T extends ToolInstallation> extends Descrip
/**
* Configured instances of {@link ToolInstallation}s.
*
* @return
* can be empty but never null. Treat this as a read-only copy, do not mutate.
* @return read-only list of installations;
* can be empty but never null.
*/
public T[] getInstallations() {
return installations;
return installations.clone();
}
/**
* Overwrites {@link ToolInstallation}s.
*
* @return
* @param installations list of installations;
* can be empty but never null.
*/
public void setInstallations(T[] installations) {
this.installations = installations;
public void setInstallations(T... installations) {
this.installations = installations.clone();
}
/**
* Lists up {@link ToolPropertyDescriptor}s that are applicable to this {@link ToolInstallation}.
*/
public List<ToolPropertyDescriptor> getPropertyDescriptors() {
return PropertyDescriptor.for_(ToolProperty.all(),clazz);
}
/**
* Optional list of installers to be configured by default for new tools of this type.
* If there are popular versions of the tool available using generic installation techniques,
* they can be returned here for the user's convenience.
* @since XXX
*/
public List<? extends ToolInstaller> getDefaultInstallers() {
return Collections.emptyList();
}
/**
* Default value for {@link ToolInstallation#getProperties()} used in the form binding.
* @since XXX
*/
public DescribableList<ToolProperty<?>,ToolPropertyDescriptor> getDefaultProperties() throws IOException {
DescribableList<ToolProperty<?>,ToolPropertyDescriptor> r
= new DescribableList<ToolProperty<?>, ToolPropertyDescriptor>(NOOP);
List<? extends ToolInstaller> installers = getDefaultInstallers();
if(!installers.isEmpty())
r.add(new InstallSourceProperty(installers));
return r;
}
}
......@@ -25,16 +25,23 @@
package hudson.tools;
import hudson.DescriptorExtensionList;
import hudson.EnvVars;
import hudson.Extension;
import hudson.ExtensionPoint;
import hudson.EnvVars;
import hudson.model.Describable;
import hudson.model.EnvironmentSpecific;
import hudson.model.Hudson;
import hudson.model.Node;
import hudson.model.Saveable;
import hudson.model.TaskListener;
import hudson.slaves.NodeSpecific;
import hudson.util.DescribableList;
import java.io.Serializable;
import java.io.IOException;
import java.util.List;
import com.thoughtworks.xstream.annotations.XStreamSerializable;
/**
* Formalization of a tool installed in nodes used for builds
......@@ -55,7 +62,7 @@ import java.io.Serializable;
*
* <p>
* Implementations of this class are strongly encouraged to also implement {@link NodeSpecific}
* (by using {@link #translateFor(Node)}) and
* (by using {@link #translateFor(Node, TaskListener)}) and
* {@link EnvironmentSpecific} (by using {@link EnvVars#expand(String)}.)
*
* <p>
......@@ -65,15 +72,44 @@ import java.io.Serializable;
* @since 1.286
*/
public abstract class ToolInstallation implements Serializable, Describable<ToolInstallation>, ExtensionPoint {
private final String name;
private final String home;
/**
* {@link ToolProperty}s that are associated with this tool.
*/
@XStreamSerializable
private transient /*almost final*/ DescribableList<ToolProperty<?>,ToolPropertyDescriptor> properties
= new DescribableList<ToolProperty<?>,ToolPropertyDescriptor>(Saveable.NOOP);
/**
* @deprecated
* as of 1.302. Use {@link #ToolInstallation(String, String, List)}
*/
public ToolInstallation(String name, String home) {
this.name = name;
this.home = home;
}
public ToolInstallation(String name, String home, List<? extends ToolProperty<?>> properties) {
this.name = name;
this.home = home;
if(properties!=null) {
try {
this.properties.replaceBy(properties);
for (ToolProperty<?> p : properties)
_setTool(p,this);
} catch (IOException e) {
throw new AssertionError(e); // no Saveable, so can't happen
}
}
}
// helper function necessary to avoid a warning
private <T extends ToolInstallation> void _setTool(ToolProperty<T> prop, ToolInstallation t) {
prop.setTool(prop.type().cast(t));
}
/**
* Gets the human readable name that identifies this tool among other {@link ToolInstallation}s of the same kind.
*/
......@@ -91,23 +127,43 @@ public abstract class ToolInstallation implements Serializable, Describable<Tool
return home;
}
public DescribableList<ToolProperty<?>,ToolPropertyDescriptor> getProperties() {
assert properties!=null;
return properties;
}
public ToolDescriptor<?> getDescriptor() {
return (ToolDescriptor) Hudson.getInstance().getDescriptor(getClass());
}
/**
* Finds a tool on a node.
* Checks if the location of the tool is overridden for the given node, and if so,
* return the node-specific home directory. Otherwise return {@code installation.getHome()}
* return the node-specific home directory.
* Also checks available {@link ToolLocationTranslator}s.
* Otherwise returns {@code installation.getHome()}.
*
* <p>
* This is the core logic behind {@link NodeSpecific#forNode(Node)} for {@link ToolInstallation},
* This is the core logic behind {@link NodeSpecific#forNode(Node, TaskListener)} for {@link ToolInstallation},
* and meant to be used by the {@code forNode} implementations.
*
* @return
* never null.
*/
protected String translateFor(Node node) {
return ToolLocationNodeProperty.getToolHome(node,this);
@SuppressWarnings("deprecation")
protected String translateFor(Node node, TaskListener log) throws IOException, InterruptedException {
return ToolLocationNodeProperty.getToolHome(node, this, log);
}
/**
* Invoked by XStream when this object is read into memory.
*/
private Object readResolve() {
if(properties==null)
properties = new DescribableList<ToolProperty<?>,ToolPropertyDescriptor>(Saveable.NOOP);
for (ToolProperty<?> p : properties)
_setTool(p, this);
return this;
}
/**
......
/*
* The MIT License
*
* Copyright (c) 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.
*/
package hudson.tools;
import hudson.ExtensionPoint;
import hudson.FilePath;
import hudson.Util;
import hudson.model.Describable;
import hudson.model.Hudson;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.TaskListener;
import java.io.IOException;
import org.kohsuke.stapler.DataBoundConstructor;
/**
* An object which can ensure that a generic {@link ToolInstallation} in fact exists on a node.
* The subclass should have a {@link ToolInstallerDescriptor}.
* A {@code config.jelly} should be provided to customize specific fields;
* {@code <st:include page="config-base.jelly"/>} to customize {@code toolName} and {@code label}.
* @see <a href="http://wiki.hudson-ci.org/display/HUDSON/Tool+Auto-Installation">Tool Auto-Installation</a>
*/
public abstract class ToolInstaller implements Describable<ToolInstaller>, ExtensionPoint {
private final String label;
protected transient ToolInstallation tool;
/**
* Subclasses should pass these parameters in using {@link DataBoundConstructor}.
*/
protected ToolInstaller(String label) {
this.label = Util.fixEmptyAndTrim(label);
}
/**
* Called during the initialization to tell {@link ToolInstaller} what {@link ToolInstallation}
* it is configured against.
*/
protected void setTool(ToolInstallation t) {
this.tool = t;
}
/**
* Label to limit which nodes this installation can be performed on.
* Can be null to not impose a limit.
*/
public final String getLabel() {
return label;
}
/**
* Checks whether this installer can be applied to a given node.
* (By default, just checks the label.)
*/
public boolean appliesTo(Node node) {
Label l = Hudson.getInstance().getLabel(label);
return l == null || l.contains(node);
}
/**
* Ensure that the configured tool is really installed.
* If it is already installed, do nothing.
* Called only if {@link #appliesTo(Node)} are true.
* @param tool the tool being installed
* @param node the computer on which to install the tool
* @param log any status messages produced by the installation go here
* @return
* The (directory) path at which the tool can be found (like {@link ToolInstallation#getHome})
* As a tip, you can return {@code node.createPath(tool.getHome())}
* if your implementation wants to skip the installation and behaves as if it was a no-op.
* @throws IOException if installation fails
* @throws InterruptedException if communication with a slave is interrupted
*/
public abstract FilePath performInstallation(ToolInstallation tool, Node node, TaskListener log) throws IOException, InterruptedException;
public ToolInstallerDescriptor<?> getDescriptor() {
return (ToolInstallerDescriptor) Hudson.getInstance().getDescriptor(getClass());
}
}
/*
* The MIT License
*
* Copyright (c) 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.
*/
package hudson.tools;
import hudson.DescriptorExtensionList;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import java.util.List;
import java.util.ArrayList;
/**
* Descriptor for a {@link ToolInstaller}.
*/
public abstract class ToolInstallerDescriptor<T extends ToolInstaller> extends Descriptor<ToolInstaller> {
/**
* Controls what kind of {@link ToolInstallation} this installer can be applied to.
*
* <p>
* By default, this method just returns true to everything, claiming it's applicable to any tool installations.
*/
public boolean isApplicable(Class<? extends ToolInstallation> toolType) {
return true;
}
public static DescriptorExtensionList<ToolInstaller,ToolInstallerDescriptor<?>> all() {
return Hudson.getInstance().getDescriptorList(ToolInstaller.class);
}
/**
* Filters {@link #all()} by eliminating things that are not applicable to the given type.
*/
public static List<ToolInstallerDescriptor<?>> for_(Class<? extends ToolInstallation> type) {
List<ToolInstallerDescriptor<?>> r = new ArrayList<ToolInstallerDescriptor<?>>();
for (ToolInstallerDescriptor<?> d : all())
if(d.isApplicable(type))
r.add(d);
return r;
}
}
......@@ -29,9 +29,11 @@ import hudson.Extension;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.model.Node;
import hudson.model.TaskListener;
import hudson.slaves.NodeProperty;
import hudson.slaves.NodePropertyDescriptor;
import hudson.slaves.NodeSpecific;
import java.io.IOException;
import org.kohsuke.stapler.DataBoundConstructor;
import java.util.Arrays;
......@@ -84,7 +86,7 @@ public class ToolLocationNodeProperty extends NodeProperty<Node> {
* @deprecated
* Use {@link ToolInstallation#translateFor(Node)}
*/
public static String getToolHome(Node node, ToolInstallation installation) {
public static String getToolHome(Node node, ToolInstallation installation, TaskListener log) throws IOException, InterruptedException {
String result = null;
// node-specific configuration takes precedence
......@@ -94,7 +96,7 @@ public class ToolLocationNodeProperty extends NodeProperty<Node> {
// consult translators
for( ToolLocationTranslator t : ToolLocationTranslator.all() ) {
result = t.getToolHome(node,installation);
result = t.getToolHome(node, installation, log);
if(result!=null) return result;
}
......
......@@ -29,6 +29,8 @@ import hudson.scm.CVSChangeLogSet.File;
import hudson.slaves.NodeSpecific;
import hudson.model.Hudson;
import hudson.model.Node;
import hudson.model.TaskListener;
import java.io.IOException;
/**
* This Hudson-wide extension points can participate in determining the actual node-specific path
......@@ -61,7 +63,7 @@ public abstract class ToolLocationTranslator implements ExtensionPoint {
* Otherwise return null to let other {@link ToolLocationTranslator}s a chance to do translations
* on their own.
*/
public abstract String getToolHome(Node node, ToolInstallation installation);
public abstract String getToolHome(Node node, ToolInstallation installation, TaskListener log) throws IOException, InterruptedException;
/**
* Returns all the registered {@link ToolLocationTranslator}s.
......
package hudson.tools;
import hudson.DescriptorExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Describable;
import hudson.model.Hudson;
import java.util.List;
/**
* Extensible property of {@link ToolInstallation}.
*
* <p>
* Plugins can contribute this extension point to add additional data or UI actions to {@link ToolInstallation}.
* {@link ToolProperty}s show up in the configuration screen of a tool, and they are persisted with the {@link ToolInstallation} object.
*
*
* <h2>Views</h2>
* <dl>
* <dt>config.jelly</dt>
* <dd>Added to the configuration page of the tool.
* <dl>
*
* @param <T>
* {@link ToolProperty} can choose to only work with a certain subtype of {@link ToolInstallation}, and this 'T'
* represents that type. Also see {@link ToolPropertyDescriptor#isApplicable(Class)}.
*
* @since 1.303
*/
public abstract class ToolProperty<T extends ToolInstallation> implements Describable<ToolProperty<?>>, ExtensionPoint {
protected transient T tool;
protected void setTool(T tool) {
this.tool = tool;
}
public ToolPropertyDescriptor getDescriptor() {
return (ToolPropertyDescriptor) Hudson.getInstance().getDescriptor(getClass());
}
/**
* What is your 'T'?
*/
public abstract Class<T> type();
/**
* Lists up all the registered {@link ToolPropertyDescriptor}s in the system.
*
* @see ToolDescriptor#getPropertyDescriptors()
*/
public static DescriptorExtensionList<ToolProperty<?>,ToolPropertyDescriptor> all() {
return (DescriptorExtensionList)Hudson.getInstance().getDescriptorList(ToolProperty.class);
}
}
package hudson.tools;
import hudson.Extension;
/**
* Descriptor for {@link ToolProperty}.
*
* <p>
* Put {@link Extension} on your descriptor implementation to have it auto-registered.
*
* @since 1.286
* @see ToolProperty
* @author Kohsuke Kawaguchi
*/
public abstract class ToolPropertyDescriptor extends PropertyDescriptor<ToolProperty<?>,ToolInstallation> {
protected ToolPropertyDescriptor(Class<? extends ToolProperty<?>> clazz) {
super(clazz);
}
protected ToolPropertyDescriptor() {
}
}
/*
* The MIT License
*
* Copyright (c) 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.
*/
package hudson.tools;
import hudson.Extension;
import hudson.FilePath;
import hudson.FilePath.FileCallable;
import hudson.Util;
import hudson.model.Node;
import hudson.model.TaskListener;
import hudson.remoting.VirtualChannel;
import hudson.util.FormValidation;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
/**
* Installs a tool into the Hudson working area by downloading and unpacking a ZIP file.
*/
public class ZipExtractionInstaller extends ToolInstaller {
/**
* URL of a ZIP file which should be downloaded in case the tool is missing.
*/
private final String url;
/**
* Optional subdir to extract.
*/
private final String subdir;
@DataBoundConstructor
public ZipExtractionInstaller(String label, String url, String subdir) {
super(label);
this.url = url;
this.subdir = Util.fixEmptyAndTrim(subdir);
}
public String getUrl() {
return url;
}
public String getSubdir() {
return subdir;
}
public FilePath performInstallation(ToolInstallation tool, Node node, TaskListener log) throws IOException, InterruptedException {
String dirname = tool.getName().replaceAll("[^A-Za-z0-9_.-]+", "_");
FilePath dir = node.getRootPath().child("tools").child(dirname);
if (dir.installIfNecessaryFrom(new URL(url), log, "Unpacking " + url + " to " + dir + " on " + node.getDisplayName())) {
dir.act(new ChmodRecAPlusX());
}
if (subdir == null) {
return dir;
} else {
return dir.child(subdir);
}
}
@Extension
public static class DescriptorImpl extends ToolInstallerDescriptor<ZipExtractionInstaller> {
public String getDisplayName() {
return Messages.ZipExtractionInstaller_DescriptorImpl_displayName();
}
public FormValidation doCheckUrl(@QueryParameter String value) {
try {
URLConnection conn = new URL(value).openConnection();
conn.connect();
if (conn instanceof HttpURLConnection) {
if (((HttpURLConnection) conn).getResponseCode() != HttpURLConnection.HTTP_OK) {
return FormValidation.error(Messages.ZipExtractionInstaller_bad_connection());
}
}
return FormValidation.ok();
} catch (MalformedURLException x) {
return FormValidation.error(Messages.ZipExtractionInstaller_malformed_url());
} catch (IOException x) {
return FormValidation.error(Messages.ZipExtractionInstaller_could_not_connect());
}
}
}
/**
* Sets execute permission on all files, since unzip etc. might not do this.
* Hackish, is there a better way?
*/
private static class ChmodRecAPlusX implements FileCallable<Void> {
private static final long serialVersionUID = 1L;
public Void invoke(File d, VirtualChannel channel) throws IOException {
process(d);
return null;
}
private void process(File f) {
if (f.isFile()) {
f.setExecutable(true, false); // XXX JDK 6-specific
} else {
File[] kids = f.listFiles();
if (kids != null) {
for (File kid : kids) {
process(kid);
}
}
}
}
}
}
......@@ -88,7 +88,7 @@ import java.net.URLConnection;
* ...
*
* public void {@linkplain Builder#perform(AbstractBuild, Launcher, BuildListener) perform}(...) {
* String version = getAntVersin(antHome);
* String version = getAntVersion(antHome);
* ...
* }
* </pre>
......
/*
* The MIT License
*
* Copyright (c) 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.
*/
package hudson.util;
import hudson.model.TaskListener;
import hudson.util.StreamTaskListener;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* {@link TaskListener} which sends messages to a {@link Logger}.
*/
public class LogTaskListener implements TaskListener {
private final TaskListener delegate;
public LogTaskListener(Logger logger, Level level) {
delegate = new StreamTaskListener(new LogOutputStream(logger, level, new Throwable().getStackTrace()[1]));
}
public PrintStream getLogger() {
return delegate.getLogger();
}
public PrintWriter error(String msg) {
return delegate.error(msg);
}
public PrintWriter error(String format, Object... args) {
return delegate.error(format, args);
}
public PrintWriter fatalError(String msg) {
return delegate.fatalError(msg);
}
public PrintWriter fatalError(String format, Object... args) {
return delegate.fatalError(format, args);
}
public void close() {
delegate.getLogger().close();
}
private static class LogOutputStream extends OutputStream {
private final Logger logger;
private final Level level;
private final StackTraceElement caller;
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
public LogOutputStream(Logger logger, Level level, StackTraceElement caller) {
this.logger = logger;
this.level = level;
this.caller = caller;
}
public void write(int b) throws IOException {
if (b == '\r' || b == '\n') {
flush();
} else {
baos.write(b);
}
}
public @Override void flush() throws IOException {
if (baos.size() > 0) {
LogRecord lr = new LogRecord(level, baos.toString());
lr.setSourceClassName(caller.getClassName());
lr.setSourceMethodName(caller.getMethodName());
logger.log(lr);
}
baos.reset();
}
public @Override void close() throws IOException {
flush();
}
}
}
<!--
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.
-->
<!--
If necessary, ask the client to fetch update-center data file for us.
This allows us to avoid proxy related configuration on the server,
and instead piggy-back on user connection.
This file is pulled into the layout.jelly
-->
<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">
${it.generateFragment()}
</j:jelly>
......@@ -100,28 +100,9 @@ THE SOFTWARE.
<f:section title="${%JDKs}">
<f:entry title="${%JDK installations}"
description="${%List of JDK installations on this system}">
<f:repeatable var="inst" items="${it.JDKs}" name="jdks">
<table width="100%">
<f:entry title="${%name}">
<input class="setting-input" name="jdk_name"
type="text" value="${inst.name}" />
</f:entry>
<j:set var="status" value="${null}" />
<j:if test="${inst!=null and !inst.exists and inst.name!=''}">
<j:set var="status" value="${%no.such.JDK}" />
</j:if>
<f:entry title="JAVA_HOME" description="${status}">
<input class="setting-input validated" name="jdk_home"
type="text" value="${inst.javaHome}"
checkUrl="'javaHomeCheck?value='+escape(this.value)"/>
</f:entry>
<f:entry title="">
<div align="right">
<f:repeatableDeleteButton />
</div>
</f:entry>
</table>
<f:repeatable var="instance" items="${it.JDKs}" name="jdks" add="${%Add JDK}">
<j:set var="descriptor" value="${h.getJDKDescriptor()}" />
<st:include page="${descriptor.configPage}" from="${descriptor}" />
</f:repeatable>
</f:entry>
</f:section>
......
<!--
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.
-->
<!--
Config page
-->
<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">
<table width="100%">
<f:entry title="${%name}" field="name">
<f:textbox />
</f:entry>
<f:entry title="JAVA_HOME" field="home">
<f:textbox />
</f:entry>
<f:descriptorList descriptors="${descriptor.propertyDescriptors}" field="properties" />
<f:entry title="">
<div align="right">
<f:repeatableDeleteButton value="${%Delete JDK}" />
</div>
</f:entry>
</table>
</j:jelly>
\ No newline at end of file
# 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.
name=Name
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Eric Lefevre-Ardant
#
# 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.
name=Nom
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, 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.
name=\u540D\u524D
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, id:sorokh
#
# 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.
name=Naam
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Reginaldo L. Russinholi
#
# 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.
name=nome
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Oguz Dag
#
# 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.
name=isim
<!--
The MIT License
Copyright (c) 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.
-->
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form" xmlns:st="jelly:stapler" xmlns:t="/hudson/tools">
<t:label />
<f:entry title="${%Command}" field="command">
<f:textarea/>
</f:entry>
</j:jelly>
<div>
Command to run on the slave node to install the tool.
The command will always be run, so it should be a quick no-op if the tool is already installed.
</div>
<p>
Runs a shell command of your choice to install the tool. Ubuntu example,
assuming the Hudson user is in <code>/etc/sudoers</code>:
</p>
<pre>sudo apt-get --yes install openjdk-6-jdk</pre>
<p>
(In this case specify <code>/usr/lib/jvm/java-6-openjdk</code> as the home directory.)
</p>
<p>
As another example, to install Sun JDK 6 for (x86) Linux,
you can use <a href="https://jdk-distros.dev.java.net/developer.html">DLJ</a>:
</p>
<pre>bin=jdk-6u13-dlj-linux-i586.bin
if [ \! -f $bin ]
then
wget --no-verbose http://download.java.net/dlj/binaries/$bin
sh $bin --unpack --accept-license
fi</pre>
<p>
(In this case specify <code>jdk1.6.0_13</code> as the home directory.)
</p>
<!--
The MIT License
Copyright (c) 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.
-->
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:block>
<j:invokeStatic var="descriptors" className="hudson.tools.ToolInstallerDescriptor" method="for_">
<j:arg value="${descriptor.clazz}" />
</j:invokeStatic>
<f:hetero-list name="installers" items="${instance.installers}" descriptors="${descriptors}" hasHeader="true"/>
</f:block>
</j:jelly>
<div>
Choose this option to let Hudson install this tool for you on demand,
to the location you configured above.
<p>
If you check this option, you'll then be asked to configure a series
of "installer"s for this tool, where each installer defines how
Hudson will try to install this tool.
<p>
For a platform-independent tool (such as Ant), configuring multiple installers
for a single tool makes not much sense, but for a platform dependent tool,
multiple installer configurations allow you to run a different set up script
depending on the slave environment.
</div>
\ No newline at end of file
<!--
The MIT License
Copyright (c) 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.
-->
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<!-- XXX t:label? -->
<f:entry title="${%Version}" field="id">
<j:choose>
<j:set var="jdks" value="${descriptor.installableJDKs}"/>
<j:when test="${empty(jdks)}">
<!-- if JDK list is not available, fall back to text box -->
<f:textbox />
</j:when>
<j:otherwise>
<select name="_.id">
<j:forEach var="family" items="${jdks}">
<optgroup label="${family.name}">
<j:forEach var="jdk" items="${family.list}">
<f:option value="${jdk.id}" selected="${jdk.id==instance.id}">${jdk.name}</f:option>
</j:forEach>
</optgroup>
</j:forEach>
</select>
</j:otherwise>
</j:choose>
</f:entry>
<f:entry title="" field="acceptLicense">
<f:checkbox/>
<label class="attach-previous">I agree to the Java SE Development Kit License Agreement</label>
</f:entry>
</j:jelly>
......@@ -21,3 +21,10 @@
# THE SOFTWARE.
ToolLocationNodeProperty.DisplayName=Tool Locations
CommandInstaller.DescriptorImpl.displayName=Run Command
CommandInstaller.no_command=Must provide a command to run.
CommandInstaller.no_toolHome=Must provide a tool home directory.
ZipExtractionInstaller.DescriptorImpl.displayName=Extract *.zip/*.tar.gz
ZipExtractionInstaller.bad_connection=Server rejected connection.
ZipExtractionInstaller.malformed_url=Malformed URL.
ZipExtractionInstaller.could_not_connect=Could not connect to URL.
<!--
The MIT License
Copyright (c) 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.
-->
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form" xmlns:st="jelly:stapler" xmlns:t="/hudson/tools">
<t:label />
<f:entry title="${%Download URL for binary archive}" field="url">
<f:textbox/>
</f:entry>
<f:entry title="${%Subdirectory of extracted archive}" field="subdir">
<f:textbox/>
</f:entry>
</j:jelly>
<div>
Optional subdirectory of the downloaded and unpacked archive to use as the tool's home directory.
</div>
<div>
URL from which to download the tool in binary form.
Should be either a ZIP or a GZip-compressed TAR file.
The timestamp on the server will be compared to the local version (if any)
so you can publish updates easily.
The URL must be accessible from the Hudson master but need not be accessible from slaves.
</div>
<div>
Downloads a tool archive and installs it within Hudson's working directory.
Example: <code>http://apache.promopeddler.com/ant/binaries/apache-ant-1.7.1-bin.zip</code>
(or whatever mirror is closest to your server)
and specify a subdir of <code>apache-ant-1.7.1</code>.
</div>
<!--
The MIT License
Copyright (c) 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.
-->
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form" xmlns:st="jelly:stapler">
<st:documentation>
Puts the input field for allowing an user to limit this installer to a certain label.
Meant to be used from config.jelly of ToolInstaller subypes.
</st:documentation>
<f:entry title="${%Label}" field="label" help="/help/tools/help-label.html">
<!-- XXX could use Hudson.getLabels and make a combo box -->
<f:textbox/>
</f:entry>
</j:jelly>
......@@ -44,6 +44,7 @@ THE SOFTWARE.
<input type="checkbox"
name="${attrs.name?:'_.'+attrs.field}"
value="${attrs.value}"
onclick="${attrs.onclick}" id="${attrs.id}" class="${attrs.negative!=null ? 'negative' : null}"
onclick="${attrs.onclick}" id="${attrs.id}" class="${attrs.negative!=null ? 'negative' : null} ${attrs.checkUrl!=null?'validated':''}"
checkUrl="${attrs.checkUrl}"
checked="${(attrs.checked ?: instance[attrs.field] ?: attrs.default) ? 'true' : null}"/>
</j:jelly>
\ No newline at end of file
......@@ -53,7 +53,7 @@ THE SOFTWARE.
</st:documentation>
<j:set var="targetType" value="${attrs.targetType ?: it.class}"/>
<j:set var="instances" value="${attrs.instances ?: instance[field]}"/>
<j:set var="instances" value="${attrs.instances ?: instance[field] ?: descriptor['default'+h.capitalize(field)]}"/>
<j:if test="${!empty(descriptors) or context['org.apache.commons.jelly.body']!=null}">
<f:section title="${attrs.title}" name="${attrs.field?:attrs.name}">
......
......@@ -66,6 +66,7 @@ THE SOFTWARE.
<st:include from="${descriptor}" page="${descriptor.configPage}" />
<f:block>
<div align="right">
<input type="hidden" name="stapler-class" value="${descriptor.clazz.name}" />
<f:repeatableDeleteButton value="${attrs.deleteCaption}" />
</div>
</f:block>
......@@ -86,7 +87,7 @@ THE SOFTWARE.
</j:forEach>
<div class="repeatable-insertion-point" />
<div class="prototypes" style="display:none">
<div class="prototypes to-be-removed">
<!-- render one prototype for each type -->
<j:set var="instance" value="${null}" />
<j:forEach var="descriptor" items="${attrs.descriptors}" varStatus="loop">
......
......@@ -29,6 +29,11 @@ THE SOFTWARE.
<st:documentation>
&lt;option> tag for the &lt;select> element.
<st:attribute name="value" use="required">
The value to be sent when the form is submitted.
Due to the browser incompatibility between IE and Firefox,
this parameter is mandatory.
</st:attribute>
<st:attribute name="selected" type="boolean">
If true, the option value appears as selected.
</st:attribute>
......
......@@ -48,12 +48,10 @@ THE SOFTWARE.
</st:attribute>
</st:documentation>
<j:set var="id" value="${h.generateId()}" />
<tr id="oe_s${id}" class="optional-block-start" hasHelp="${attrs.help!=null}"><!-- this ID marks the beginning -->
<tr class="optional-block-start row-set-start" hasHelp="${attrs.help!=null}"><!-- this ID marks the beginning -->
<td colspan="3">
<f:checkbox name="${name}" onclick="javascript:updateOptionalBlock(this,true)"
negative="${attrs.negative}"
id="oe_c${id}" checked="${checked}" />
negative="${attrs.negative}" checked="${checked}" />
<st:nbsp/>
<label class="attach-previous">${title}</label>
</td>
......@@ -68,9 +66,5 @@ THE SOFTWARE.
</j:if>
<d:invokeBody />
<!-- end marker -->
<tr class="optional-block-end" style="display:none" id="oe_e${id}">
<script>
initOptionalBlock('oe_s${id}','oe_e${id}','oe_c${id}');
</script>
</tr>
<tr class="row-set-end optional-block-end" />
</j:jelly>
\ No newline at end of file
......@@ -81,6 +81,9 @@ THE SOFTWARE.
true if the default 'add' button (that adds a new copy) shouldn't be displayed.
When you use this attribute,
</st:attribute>
<st:attribute name="add">
If specified, this text will replace the standard "Add" text.
</st:attribute>
<st:attribute name="minimum">
At least provide this number of copies initially.
minimum="1" is useful to make sure there's always at least one entry for the user to fill in.
......@@ -107,7 +110,7 @@ THE SOFTWARE.
<div class="repeated-container">
<!-- The first DIV is the master copy. -->
<div class="repeated-chunk" style="display:none" name="${name}">
<div class="repeated-chunk to-be-removed" name="${name}">
<d:invokeBody />
</div>
<!-- then populate them for each item -->
......@@ -130,7 +133,7 @@ THE SOFTWARE.
<div class="repeatable-insertion-point" />
<j:if test="${!attrs.noAddButton}">
<input type="button" value="${%Add}" class="repeatable-add" />
<input type="button" value="${attrs.add?:'%Add'}" class="repeatable-add" />
</j:if>
</div>
</j:jelly>
\ No newline at end of file
......@@ -42,16 +42,9 @@ THE SOFTWARE.
<d:invokeBody />
</j:when>
<j:otherwise>
<j:set var="id" value="${h.generateId()}" />
<j:set var="head" value="rowGroupStart${id}"/>
<j:set var="ref" value="${attrs.ref?:head}"/>
<tr id="${head}" style="display:none" name="${attrs.name}" />
<tr ref="${attrs.ref}" class="row-set-start" style="display:none" name="${attrs.name}" />
<d:invokeBody />
<tr id="rowGroupEnd${id}" style="display:none" />
<script>
applyNameRef($$('${head}') , $$('rowGroupEnd${id}') , '${ref}')
</script>
<tr class="row-set-end" />
</j:otherwise>
</j:choose>
</j:jelly>
\ No newline at end of file
......@@ -209,6 +209,17 @@ THE SOFTWARE.
<div id="navigation">
<j:set var="mode" value="side-panel" />
<d:invokeBody />
<!-- add YUI logger if debugging YUI -->
<j:if test="${h.yuiSuffix=='debug'}">
<div id="yui-logreader" style="margin-top:1em"/>
<script>
Behaviour.addLoadEvent(function(){
var logReader = new YAHOO.widget.LogReader("yui-logreader");
logReader.collapse();
});
</script>
</j:if>
</div>
</td>
<td id="main-panel" width="80%" height="100%">
......
......@@ -473,7 +473,7 @@ public class MavenBuild extends AbstractBuild<MavenModule,MavenBuild> {
buildEnvironments.add(e);
}
EnvVars envVars = getEnvironment(); // buildEnvironments should be set up first
EnvVars envVars = getEnvironment(listener); // buildEnvironments should be set up first
ProcessCache.MavenProcess process = mavenProcessCache.get(launcher.getChannel(), listener,
new MavenProcessFactory(getParent().getParent(),launcher,envVars,null));
......
......@@ -311,7 +311,7 @@ public final class MavenModuleSetBuild extends AbstractBuild<MavenModuleSet,Mave
protected Result doRun(final BuildListener listener) throws Exception {
PrintStream logger = listener.getLogger();
try {
EnvVars envVars = getEnvironment();
EnvVars envVars = getEnvironment(listener);
parsePoms(listener, logger, envVars);
if(!project.isAggregatorStyleBuild()) {
......@@ -419,7 +419,7 @@ public final class MavenModuleSetBuild extends AbstractBuild<MavenModuleSet,Mave
throw new AbortException("A Maven installation needs to be available for this project to be built.\n"+
"Either your server has no Maven installations defined, or the requested Maven version does not exist.");
mvn = mvn.forEnvironment(envVars).forNode(Computer.currentComputer().getNode());
mvn = mvn.forEnvironment(envVars).forNode(Computer.currentComputer().getNode(), listener);
List<PomInfo> poms;
try {
......
......@@ -62,6 +62,7 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.jar.JarFile;
import java.util.logging.Logger;
......@@ -228,7 +229,7 @@ final class MavenProcessFactory implements ProcessCache.Factory {
* UGLY.
*/
private ArgumentListBuilder buildMavenCmdLine(BuildListener listener,int tcpPort) throws IOException, InterruptedException {
MavenInstallation mvn = getMavenInstallation();
MavenInstallation mvn = getMavenInstallation(listener);
if(mvn==null) {
listener.error("Maven version is not configured for this project. Can't determine which Maven to run");
throw new RunnerAbortedException();
......@@ -247,7 +248,7 @@ final class MavenProcessFactory implements ProcessCache.Factory {
slaveRoot = getCurrentNode().getRootPath();
ArgumentListBuilder args = new ArgumentListBuilder();
JDK jdk = getJava();
JDK jdk = getJava(listener);
if(jdk==null) {
args.add("java");
} else {
......@@ -290,16 +291,16 @@ final class MavenProcessFactory implements ProcessCache.Factory {
return envVars.expand(mms.getMavenOpts());
}
public MavenInstallation getMavenInstallation() {
public MavenInstallation getMavenInstallation(TaskListener log) throws IOException, InterruptedException {
MavenInstallation mi = mms.getMaven();
if (mi != null) mi = mi.forNode(getCurrentNode()).forEnvironment(envVars);
if (mi != null) mi = mi.forNode(getCurrentNode(), log).forEnvironment(envVars);
return mi;
}
public JDK getJava() {
public JDK getJava(TaskListener log) throws IOException, InterruptedException {
JDK jdk = mms.getJDK();
if (jdk != null) jdk = jdk.forNode(getCurrentNode()).forEnvironment(envVars);
if (jdk != null) jdk = jdk.forNode(getCurrentNode(), log).forEnvironment(envVars);
return jdk;
}
......
......@@ -26,6 +26,7 @@ package hudson.maven;
import hudson.Util;
import hudson.model.BuildListener;
import hudson.model.JDK;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.VirtualChannel;
......@@ -61,8 +62,8 @@ public final class ProcessCache {
*/
Channel newProcess(BuildListener listener,OutputStream out) throws IOException, InterruptedException;
String getMavenOpts();
MavenInstallation getMavenInstallation();
JDK getJava();
MavenInstallation getMavenInstallation(TaskListener listener) throws IOException, InterruptedException;
JDK getJava(TaskListener listener) throws IOException, InterruptedException;
}
class MavenProcess {
......@@ -161,8 +162,8 @@ public final class ProcessCache {
*/
public MavenProcess get(VirtualChannel owner, BuildListener listener, Factory factory) throws InterruptedException, IOException {
String mavenOpts = factory.getMavenOpts();
MavenInstallation installation = factory.getMavenInstallation();
JDK jdk = factory.getJava();
MavenInstallation installation = factory.getMavenInstallation(listener);
JDK jdk = factory.getJava(listener);
PerChannel list = get(owner);
synchronized(list.processes) {
......
......@@ -50,7 +50,7 @@ public class CaptureEnvironmentBuilder extends Builder {
}
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
envVars = build.getEnvironment();
envVars = build.getEnvironment(listener);
return true;
}
......
......@@ -88,7 +88,7 @@ public class HudsonTest extends HudsonTestCase {
private void assertJDK(JDK jdk, String name, String home) {
assertEquals(jdk.getName(),name);
assertEquals(jdk.getJavaHome(),home);
assertEquals(jdk.getHome(),home);
}
/**
......
......@@ -28,7 +28,7 @@ public class EnvVarsInConfigTasksTest extends HudsonTestCase {
super.setUp();
JDK defaultJDK = hudson.getJDK(null);
JDK varJDK = new JDK("varJDK", withVariable(defaultJDK.getJavaHome()));
JDK varJDK = new JDK("varJDK", withVariable(defaultJDK.getHome()));
hudson.getJDKs().add(varJDK);
// Maven with a variable in its path
......
......@@ -95,7 +95,7 @@ public class MavenTest extends HudsonTestCase {
hudson.getDescriptorByType(Maven.DescriptorImpl.class).setInstallations(maven, varMaven);
JDK jdk = hudson.getJDK("default");
String javaHome = jdk.getJavaHome();
String javaHome = jdk.getHome();
String javaHomeVar = "${VAR_JAVA}" + javaHome.substring(3);
String javaVar = javaHome.substring(0, 3);
JDK varJDK = new JDK("varJDK", javaHomeVar);
......
package hudson.tools;
import org.jvnet.hudson.test.HudsonTestCase;
import hudson.model.JDK;
import hudson.model.FreeStyleProject;
import hudson.model.FreeStyleBuild;
import hudson.tasks.Shell;
import java.io.File;
import java.util.Arrays;
/**
* @author Kohsuke Kawaguchi
*/
public class JDKInstallerTest extends HudsonTestCase {
/**
* Tests the configuration round trip.
*/
public void testConfigRoundtrip() throws Exception {
File tmp = env.temporaryDirectoryAllocator.allocate();
JDKInstaller installer = new JDKInstaller("jdk-6u13-oth-JPR@CDS-CDS_Developer", true);
hudson.getJDKs().add(new JDK("test",tmp.getAbsolutePath(), Arrays.asList(
new InstallSourceProperty(Arrays.<ToolInstaller>asList(installer)))));
submit(new WebClient().goTo("configure").getFormByName("config"));
JDK jdk = hudson.getJDK("test");
InstallSourceProperty isp = jdk.getProperties().get(InstallSourceProperty.class);
assertEquals(1,isp.installers.size());
assertEqualBeans(installer,isp.installers.get(JDKInstaller.class),"id,acceptLicense");
}
/**
* Tests the auto installation.
*/
public void testAutoInstallation() throws Exception {
// this is a really time consuming test, so only run it when we really want
if(!Boolean.getBoolean("hudson.sunTests"))
return;
File tmp = env.temporaryDirectoryAllocator.allocate();
JDKInstaller installer = new JDKInstaller("jdk-6u13-oth-JPR@CDS-CDS_Developer", true);
JDK jdk = new JDK("test", tmp.getAbsolutePath(), Arrays.asList(
new InstallSourceProperty(Arrays.<ToolInstaller>asList(installer))));
hudson.getJDKs().add(jdk);
FreeStyleProject p = createFreeStyleProject();
p.setJDK(jdk);
p.getBuildersList().add(new Shell("java -fullversion\necho $JAVA_HOME"));
FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0).get());
System.out.println(b.getLog());
// make sure it runs with the JDK that just got installed
assertTrue(b.getLog().contains("1.6.0_13-b03"));
assertTrue(b.getLog().contains(tmp.getAbsolutePath()));
}
}
......@@ -111,7 +111,7 @@ THE SOFTWARE.
<!-- bundled plugins -->
<resolveArtifact groupId="${project.groupId}" artifactId="maven-plugin" version="${project.version}" type="hpi" tofile="${basedir}/target/generated-resources/WEB-INF/plugins/maven-plugin.hpi" />
<resolveArtifact groupId="org.jvnet.hudson.plugins" artifactId="ssh-slaves" version="0.5" type="hpi" tofile="${basedir}/target/generated-resources/WEB-INF/plugins/ssh-slaves.hpi" />
<resolveArtifact groupId="org.jvnet.hudson.plugins" artifactId="ssh-slaves" version="0.5.2" type="hpi" tofile="${basedir}/target/generated-resources/WEB-INF/plugins/ssh-slaves.hpi" />
</tasks>
</configuration>
</execution>
......@@ -158,6 +158,11 @@ THE SOFTWARE.
<name>stapler.trace</name>
<value>true</value>
</systemProperty>
<systemProperty>
<!-- always reload scripts during debugging -->
<name>hudson.script.noCache</name>
<value>true</value>
</systemProperty>
<systemProperty>
<!-- load view resources from the source directly, again for real time change -->
<name>stapler.resourcePath</name>
......
......@@ -687,6 +687,17 @@ TABLE.fingerprint-in-build TD {
.repeated-chunk .show-if-not-only { visibility: visible; }
.repeated-chunk.only .show-if-not-only { visibility: hidden; }
/*
<DIV>s marked with to-be-removed is used in conjunction with repetable.jelly and hetero-list.jelly
and represents a master copy that gets pulled out from HTML, then inserted later upon demand multiple times
when the user does "Add".
*/
DIV.to-be-removed { display: none; }
/* ========================= Other form related CSS ========================= */
.row-set-end { display: none; }
/* ========================= Yahoo UI style adjustments ========================= */
.ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover {
color: inherit;
......
<div>
Optional label to restrict the use of this installation method.
Only nodes which have this label will be considered.
</div>
......@@ -22,7 +22,7 @@
* THE SOFTWARE.
*/
//
initOptionalBlock//
//
// JavaScript for Hudson
// See http://www.ibm.com/developerworks/web/library/wa-memleak/?ca=dgr-lnxw97JavascriptLeaks
// for memory leak patterns and how to prevent them.
......@@ -94,6 +94,13 @@ var FormChecker = {
}
}
function toValue(e) {
// compute the form validation value to be sent to the server
if(e.getAttribute("type").toLowerCase()=="checkbox")
return e.checked;
return encode(e.value);
}
// find the nearest ancestor node that has the given tag name
function findAncestor(e, tagName) {
do {
......@@ -207,6 +214,14 @@ function makeButton(e,onclick) {
return btn;
}
/*
If we are inside 'to-be-removed' class, some HTML altering behaviors interact badly, because
the behavior re-executes when the removed master copy gets reinserted later.
*/
function isInsideRemovable(e) {
return Element.ancestors(e).find(function(f){return f.hasClassName("to-be-removed");});
}
var hudsonRules = {
"BODY" : function() {
tooltip = new YAHOO.widget.Tooltip("tt", {context:[], zindex:999});
......@@ -216,6 +231,8 @@ var hudsonRules = {
// other behavior rules change them (like YUI buttons.)
"DIV.hetero-list-container" : function(e) {
if(isInsideRemovable(e)) return;
// components for the add button
var menu = document.createElement("SELECT");
var btn = findElementsBySelector(e,"INPUT.hetero-list-add")[0];
......@@ -279,6 +296,8 @@ var hudsonRules = {
},
"DIV.repeated-container" : function(e) {
if(isInsideRemovable(e)) return;
// compute the insertion point
var ip = e.lastChild;
while (!Element.hasClassName(ip, "repeatable-insertion-point"))
......@@ -516,6 +535,38 @@ var hudsonRules = {
makeButton(e);
},
"TR.optional-block-start": function(e) { // see optionalBlock.jelly
// set start.ref to checkbox in preparation of row-set-end processing
var checkbox = e.firstChild.firstChild;
e.setAttribute("ref", checkbox.id = "cb"+(iota++));
},
"TR.row-set-end": function(e) { // see rowSet.jelly and optionalBlock.jelly
// figure out the corresponding start block
var end = e;
for( var depth=0; ; e=e.previousSibling) {
if(Element.hasClassName(e,"row-set-end")) depth++;
if(Element.hasClassName(e,"row-set-start")) depth--;
if(depth==0) break;
}
var start = e;
var ref = start.getAttribute("ref");
if(ref==null)
start.id = ref = "rowSetStart"+(iota++);
applyNameRef(start,end,ref);
},
"BODY TR.optional-block-start": function(e) { // see optionalBlock.jelly
// this is prefixed by a pointless BODY so that two processing for optional-block-start
// can sandwitch row-set-end
// this requires "TR.row-set-end" to mark rows
var checkbox = e.firstChild.firstChild;
updateOptionalBlock(checkbox,false);
},
// image that shows [+] or [-], with hover effect.
// oncollapsed and onexpanded will be called when the button is triggered.
"IMG.fold-control" : function(e) {
......@@ -598,11 +649,6 @@ function applyNameRef(s,e,id) {
}
}
function initOptionalBlock(sid, eid, cid) {
applyNameRef($(sid),$(eid),cid);
updateOptionalBlock($(cid),false);
}
// used by optionalBlock.jelly to update the form status
// @param c checkbox element
function updateOptionalBlock(c,scroll) {
......@@ -1432,6 +1478,29 @@ function loadScript(href) {
document.getElementsByTagName("HEAD")[0].appendChild(s);
}
var downloadService = {
continuations: {},
download : function(id,url,info, postBack,completionHandler) {
this.continuations[id] = {postBack:postBack,completionHandler:completionHandler};
loadScript(url+"?"+Hash.toQueryString(info));
},
post : function(id,data) {
var o = this.continuations[id];
new Ajax.Request(o.postBack, {
method:"post",
parameters:{json:Object.toJSON(data)},
onSuccess: function() {
if(o.completionHandler!=null)
o.completionHandler();
}
});
}
};
// update center service. for historical reasons,
// this is separate from downloadSerivce
var updateCenter = {
postBackURL : null,
info: {},
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册