+ CLI clients should be able to see plugin classes
+ report
+
+ Fixed NPE in running assembly:assembly with Maven3
+ (issue 8837)
+
+ Fixed a bug in one of the file copy operations that caused the copy-artifact plugin to fail to preserve the timestamp.
+ (issue 10805)
+
+ Jenkins didn't start on IBM JDK.
+ (issue 10810)
+
+ Fixed a possible NPE during the boot sequence
+ (issue 10799)
+
+ stdin/stdout based remote slaves, such as ones launched via SSH or script, now does a better redirect to avoid interference with JVM output to stdout.
+ (issue 8856)
+
+ Project names in fingerprint records weren't updated when a project is renamed.
+ (issue 10330)
+
+ External job submision now supports &ly;displayName> and <description> elements
+ (pull 215)
+
+ CLI jar download was making the browser prefer a wrong file name.
+
+ Link "Started by user XXX" broken on build status page if user name modified.
+ (issue 10698)
+
+ 404 error when clicking "Build History" link on MyView.
+
+ Add a DefaultViewsTabBar config.jelly to avoid jelly exception
+ (issue 10640)
+
+ go back to view page when job is deleted.
+ (issue 10510)
+
+ A Global Environment variable with an empty key would fail maven builds since 1.424. Ignore these variables instead of failing the build.
+ (report)
+
+
+ Regression in jenkins .401 maven plugin - deploy to repository post-task
+ (issue 9084)
+
+ Fixed a bug in persisting user configuration that causes NPE in some plugins
+ (issue 9062)
+
+ Replacement of some maven properties is not working
+ (issue 8573)
+
+ Fixed JDK1.6 dependency that has crept into the core in 1.400
+ (issue 8914)
+
+ When both "block build when upstream/downstream is building" are checked, the upstream block check wasn't taking effect.
+ (issue 8968)
+
+ A project aggregating tests without any tests itself should now link properly
+ to latest aggregated results, rather than broken link to non-existent test
+ results.
+
+ Initial position of the "build time" timeline was off by one day
+ (issue 8865)
+
+ Build list tables had "Date" as column label, but actual content of the column was "Time Since".
+ (issue 9102)
+
+ PAM authentication fails to restore group membership information on "remember me" tokens.
+ (issue 9094)
+
+ Upstream culprits did include culprits of an old build.
+ (issue 8567)
+
+ Shell Task on Windows Slave Uses Incorrect /bin/sh.
+ issue 8449)
+
+ NPE during run - fingerprint cleanup thread.
+ issue 6128)
+
+ Failed to instantiate class hudson.slaves.DumbSlave.
+ issue 7174)
+
+ "Last Duration" column was showing all N/A. Regression in 1.403
+ issue 9134)
+
+ Added the --mimeTypes command line option to define additional MIME type mappings.
+
+ Added a new axis type to the matrix project that lets you use boolean expressions
+ (pull request #66)
+
+ Improved the error diagnostics when a remote method call fails to deserialize.
+ (issue 9050)
+
+ Added "Manage Jenkins" link to the left side panel.
+ (issue 7743)
+
+ LDAP group names are now available as-is for the use in authorization. No upper casing / no 'ROLE_' prefix.
+
+ Added a new extension point to contribute build variables.
+
+ On IBM JDKs, Jenkins incorrectly ended up closing stdout to read from forked processes.
+ (issue 8420)
+
+ Fixed a race condition in obtaining the tail of the output from remote process.
+ (issue 7809)
+
+ Jenkins was unable to kill/list up native processses on 64bit Mac JVMs.
+
+ Many messages about RecordReaper IllegalArgumentException
+ (issue 8647)
+
+ Multiple polling events triggering a single build show up as multiple identical BuildActions in the sidebar, since there
+ is only one polling log file, regardless of how many times polling happened. Should only be the latest polling instance now.
+ (issue 7649)
+
+ Fix javascript errors on config pages when view name or user name contains an apostrophe.
+ (issue 8789)
+
+ Fix expansion of builtin environment variables in Ant properties on Windows.
+ (issue 7442)
+
+ Fixed a log rotation configuration problem on openSUSE.
+ (issue 5784)
+
+ Fixed a bug in the OpenSUSE startup script (again)
+ (issue 5020)
+
+ Change prefix of BUILD_TAG variable to "jenkins-"
+
+ Lock down maven plugin versions to shut up m3
+ (issue 7275)
+
+ BuildWrappers can now act on the build in progress before the checkout occurs.
+
+ Improved the process forking abstractions so that plugins can more easily read from child processes.
+ (issue 7809)
+
Fixed a possible NPE in computing dependency changes.
Fixed the malformed HTTP request error recovery behavior in Winstone.
- (issue 7201)
+ (issue 7201)
When checking module descendant relationships, SCM changelog paths were using system file separators while module paths were always using /s.
- (issue 7611)
+ (issue 7611)
Hudson was creating multiple instances of PageDecorators, resulting in data consistency problem.
(report)
Improving the master/slave communication to avoid pipe clogging problem.
- (issue 5977)
+ (issue 5977)
Rolling back to Ant 1.8.0 due to bug in Ant 1.8.1 file copy with large files.
- (issue 7013)
+ (issue 7013)
Multiple fingerprints and "redeploy artifacts" links are added to M2 builds when multiple forked lifecycles are invovled.
Computation of the module build time in the m2 job was incorrect when multiple forked lifecycles are involved.
Standardized logic for determining alternate settings file location in Maven projects for POM parsing and actual Maven execution.
- (issue 4963)
+ (issue 4963)
- Side effect from earlier fix of issue 7300 - some help files were linking to a now-moved file in SVN directly. Those are all changed to relative paths now.
+ Side effect from earlier fix of issue 7300 - some help files were linking to a now-moved file in SVN directly. Those are all changed to relative paths now.
BuildWrapper teardowns could not get result of build for Maven2 projects.
- (issue 6033)
+ (issue 6033)
Properly handle incremental builds of Maven projects using relative paths to modules.
- (issue 5357)
+ (issue 5357)
Setting of MAXOPENFILES was not reflected in the debian init script.
- (issue 5721)
+ (issue 5721)
Do not expose static resources under WEB-INF to clients
- (issue 7457)
+ (issue 7457)
Console annotations are added to highlight warnings/errors in Maven
@@ -280,29 +1452,29 @@ Upcoming changes
Added a new extension point to prolong the quiet down period programmatically.
Added a new extension point to make the ping behaviour customizable.
- (issue 5249)
+ (issue 5249)
Added a new classloader ("a la" child first for plugin)
- (issue 5360)
+ (issue 5360)
Make /buildWithParameters support remote cause and user supplied cause text
for build via authentication token, just as /build does.
- (issue 7004)
+ (issue 7004)
Auto install of JDK when master/slave are different platforms would fail.
- (issue 6880)
+ (issue 6880)
Safe restart made Hudson unresponsive until all running jobs complete, since 1.361.
- (issue 6649)
+ (issue 6649)
Plugins with dependencies show wrong description on installed plugins page.
- (issue 6966)
+ (issue 6966)
Fix redirect after login when return URL has characters that need encoding.
- (issue 6960)
+ (issue 6960)
- <input type='hidden'> field shouldn't be getting the plain text password value.
+ <input type='hidden'> field shouldn't be getting the plain text password value.
Added a mechanism to register CLI option handler as an extension point.
@@ -485,7 +1657,7 @@ Upcoming changes
Show outline structure for Ant execution in the console output view.
Remote API now supports the 'tree' filter query parameter which is more efficient and easier to use.
- (issue 5940)
+ (issue 5940)
Delete contained module builds when a maven project build is deleted, to avoid
orphaned builds which can then affect the displayed result of a prior build.
- (issue 6779)
+ (issue 6779)
Hide some sidepanel links that should not be shown in user-private views.
- (issue 6832)
+ (issue 6832)
Fix for file parameters that are copied to a subdirectory of the workspace.
- (issue 6889)
+ (issue 6889)
File parameters uploaded via the CLI are now displayed correctly on the build Parameters page.
- (issue 6896)
+ (issue 6896)
Allowed file parameters to be downloaded even when the name contains URL-unfriendly characters.
- (issue 6897)
+ (issue 6897)
Fixed a garbage in the raw console plain text output.
- (issue 6034)
+ (issue 6034)
"Hudson is loading" page didn't take the user back to the same page.
@@ -531,13 +1703,13 @@ Upcoming changes
Fixed a race condition where a queued build may get executed multiple times.
- (issue 6819)
+ (issue 6819)
Some UI labels related to JUnit results were shown in the wrong locale.
- (issue 6824)
+ (issue 6824)
Allow multiple dependencies between same two projects, as they may trigger under
different conditions and with different parameters.
- (issue 5708)
+ (issue 5708)
Timeline on build trend page should use server timezone instead of always GMT.
- (issue 6692)
+ (issue 6692)
Don't mask the cause of the checkout related exception.
"who am I?" page should be visible to everyone.
Avoid pointless and harmful redirection when downloading slave.jar.
- (issue 5752)
+ (issue 5752)
Cache downloaded JDKs.
Reinstall a JDK when a different version is selected.
- (issue 5551)
+ (issue 5551)
Integrated community-contributed translations (Germany, Greek, Spanish, Finnish, Hungarian, Italian, Japanese, French,
Russian, Slovenian, Dutch, Traditional Chinese, Swedish, Ukrainian, and Portuguese.)
Build page may not list all of the artifacts since 1.348.
- (issue 6371)
+ (issue 6371)
Add workaround for Opera 10.52/53 bug causing error in saving job configuration.
- (issue 6424)
+ (issue 6424)
Fix createSymlink problem on *nix systems that do not use GNUCLibrary since 1.356.
- (issue 6437)
+ (issue 6437)
Hide add/edit description link on test result pages when user does not have
permission to submit a description.
@@ -723,13 +1895,13 @@ Upcoming changes
Add "LOADING" overlay on global and job config pages until form is ready for use.
Email recipient lists now support build parameters.
- (issue 6394)
+ (issue 6394)
Make it easier to see the latest update jobs on the Update Center page.
- (issue 4255)
+ (issue 4255)
Allow plugins to use forms with an onsubmit handler, and fix "no-json" handling.
- (issue 5927)
+ (issue 5927)
Updated bundled subversion plugin to version 1.17.
@@ -737,13 +1909,13 @@ Upcoming changes
Fix StringIndexOutOfBoundsException in console log from UrlAnnotator.
- (issue 6252)
+ (issue 6252)
Fixed potential deadlock between saving project config and getting project page.
- (issue 6269)
+ (issue 6269)
Fixed garbled response of XML API if xpath is specified.
(ja@hudson.dev.javanet)
@@ -751,7 +1923,7 @@ Upcoming changes
Fix broken links for stopping jobs in executor list on pages for slave nodes or filtered views.
Fixed NoSuchMethodError with Maven and Ivy plugins.
- (issue 6311)
+ (issue 6311)
Extension points can be now sorted.
@@ -760,10 +1932,10 @@ Upcoming changes
Colored ball image at top of build pages was broken for Hudson in some web
containers (fixed by removing workaround for a Firefox bug fixed since 3.0.5/Dec2008).
- (issue 2341)
+ (issue 2341)
Console page while build is running did not wrap lines when viewed in IE.
- (issue 5869)
+ (issue 5869)
Fixed build history to indicate test failure for MavenBuild and MavenModuleSetBuild.
@@ -783,18 +1955,18 @@ Upcoming changes
POM parsing was still using the module root as the base for relative paths for alternate settings files.
- (issue 6080)
+ (issue 6080)
Fix dynamic updates of build history table when CSRF protection is turned on.
- (issue 6072)
+ (issue 6072)
Improved the error reporting mechanism in LDAP setting.
CLI commands on protected Hudson now asks a password interactively, if run on Java6.
@@ -872,11 +2044,11 @@ Upcoming changes
Fix handling of relative paths in alternate settings.xml path for Maven projects.
- (issue 4693)
+ (issue 4693)
Alternate settings, private repository, profiles, etc were not used in embedded Maven for
deploy publisher.
- (issue 4939)
+ (issue 4939)
Make editableComboBox work in repeatable content, such as a build step.
@@ -884,16 +2056,16 @@ Upcoming changes
fixed this to use proper HTML rendering when appropriate.
'<' and '&' in the console output was not escaped since 1.349
- (issue 5852)
+ (issue 5852)
Fixed an AbstractMethodError in SCM polling under some circumstances.
- (issue 5756)
+ (issue 5756)
Fixed a ClassCastException in the Subversion plugin - now using Subversion plugin 1.13.
- (issue 5827)
+ (issue 5827)
The Maven Integration plugin link in the Update Center was going to a dead location.
- (issue 4811)
+ (issue 4811)
On RPM/DEB/etc installation, don't offer the self upgrade. It should be done by the native package manager.
(report)
@@ -902,10 +2074,10 @@ Upcoming changes
Added advanced option to LogRotator to allow for removing artifacts from old builds
without removing the logs, history, etc.
- (issue 834)
+ (issue 834)
Maven modules should not be buildable when the parent project is disabled.
- (issue 1375)
+ (issue 1375)
Fixed the broken quiet period implementation when polling interval is shorter than
the quiet period. (Changes in SCM impls are needed for this to take effect.)
- (issue 2180)
+ (issue 2180)
Escape username in URLs in case it contains special characters such as "#".
- (issue 2610)
+ (issue 2610)
Fix sidepanel link for People to be visible and show view-specific info when appropriate.
- (issue 5443)
+ (issue 5443)
Improved HTML rendering, not using closing tags that do not exist in HTML.
- (issue 5458)
+ (issue 5458)
Show better error message for missing view type selection when creating a view.
- (issue 5469)
+ (issue 5469)
Hudson wasn't properly streaming a large external build submission,
which can result in OOME and unresponsiveness.
Use fixed-width font in text area for shell/batch build steps.
- (issue 5471)
+ (issue 5471)
Speed/footprint improvement in the HTML rendering.
@@ -1003,34 +2175,34 @@ Upcoming changes
Update center retrieval, "build now" link, and real-time console update was broken in 1.344.
- (issue 5536)
+ (issue 5536)
- Fixed the backward incompatibility introduced in HUDSON-5391 fix in 1.344.
- (issue 5391)
+ Fixed the backward incompatibility introduced in JENKINS-5391 fix in 1.344.
+ (issue 5391)
Fixed the bug that prevents update center metadata retrieval in Jetty.
- (issue 5210)
+ (issue 5210)
Show which plugins have already been upgraded in Plugin Manager.
- (issue 2313)
+ (issue 2313)
Show Hudson upgrade status on manage page instead of offering same upgrade again.
- (issue 3055)
+ (issue 3055)
Make badges in build history line up.
(report)
@@ -1039,23 +2211,23 @@ Upcoming changes
Don't report a computer as idle if it running the parent job for a matrix project.
- (issue 5049)
+ (issue 5049)
Improve error message for a name conflict when renaming a job.
- (issue 1916)
+ (issue 1916)
Job description set via the remote API was not saved.
- (issue 5351)
+ (issue 5351)
Work around a JVM bug on Windows that causes the "Access denied" error
while creating a temp file.
- (issue 5313)
+ (issue 5313)
Fixed a NPE in the update center with the container authentication mode.
- (issue 5382)
+ (issue 5382)
Global MAVEN_OPTS for Maven projects wasn't getting loaded properly for configuration.
- (issue 5405)
+ (issue 5405)
Fix for parameterized project with choice parameter value that has < or > character.
(report)
@@ -1064,69 +2236,69 @@ Upcoming changes
old ones later.
Move form to adjust logging levels to its own page and include table of configured levels.
- (issue 2210)
+ (issue 2210)
Allow the administrator to control the host names via a system property "host.name" per slave,
in case auto-detection fails.
- (issue 5373)
+ (issue 5373)
Introduced a new extension point for test result parsers.
(discussion)
Data loading is made more robust in the face of linkage failures.
- (issue 5383)
+ (issue 5383)
Auto-detect if Hudson is run in Solaris SMF
and provide restart capability.
(report)
Formalized an extension point to control priority among builds in the queue.
- (issue 833)
+ (issue 833)
Commands run on slaves (such as SCM operations) were not printed to the log
the way they would be when run on master.
- (issue 5296)
+ (issue 5296)
Downstream jobs could fail to trigger when using per-project read permissions.
- (issue 5265)
+ (issue 5265)
Update lastStable/lastSuccessful symlinks on filesystem later in build process to avoid
incorrectly updating links when build fails in post-build actions, and links briefly
pointing to a build that is not yet complete.
- (issue 2543)
+ (issue 2543)
Debian package no longer changes the permissions and owner of the jobs and .ssh directory.
This is to improve upgrade speed and so that ssh works properly after upgrading.
- (issue 4047 and
- issue 5112)
+ (issue 4047 and
+ issue 5112)
Link to project changes summary instead of this build's changes for "still unstable" email.
- (issue 3283)
+ (issue 3283)
SCM retry count and "Block build when upstream project is building" is now available on matrix projects.
(report)
@@ -1161,10 +2333,10 @@ Upcoming changes
Non ASCII chars get mangled when a new user is created.
- (issue 2026)
+ (issue 2026)
Fixed garbled mail text when default encoding is not UTF-8.
- (issue 1811)
+ (issue 1811)
Fixed a bug in the log rotation setting of RPM packages.
(report)
@@ -1175,57 +2347,57 @@ Upcoming changes
(report)
User IDs in Hudson are now case preserving but case insensitive.
- (issue 4354)
+ (issue 4354)
CVS support is separated into a plugin, although it's still bundled by default for compatibility.
- (issue 3101)
+ (issue 3101)
Maven projects will now use per-project MAVEN_OPTS if defined first, then global MAVEN_OPTS if defined, and finally
as fallback, MAVEN_OPTS environment variable on executor.
- (issue 2932)
+ (issue 2932)
Fix handling of non-ASCII characters in external job submission.
- (issue 4877)
+ (issue 4877)
Job assigned to label that no longer has any nodes generates exception since 1.330.
- (issue 4878)
+ (issue 4878)
Custom workspace on Windows with just a drive letter or using forward slashes in path
failed to build in 1.334.
- (issue 4894)
+ (issue 4894)
Core version number in plugin manager warning message was missing in 1.334.
@@ -1334,23 +2506,23 @@ Upcoming changes
Fixed a possible exception in submitting forms and obtaining update center metadata with Winstone in 1.333.
- (issue 4804)
+ (issue 4804)
Remoting layer was unable to kill remote processes. Prevented Mercurial plugin from implementing poll timeout on slaves.
- (issue 4611)
+ (issue 4611)
Display of console output as plain text did not work in browsers since 1.323.
- (issue 4557)
+ (issue 4557)
Show "Latest Test Results" link even when a new build is running.
- (issue 4580)
+ (issue 4580)
Fix broken links for failed tests on build page for a matrix type project.
- (issue 4765)
+ (issue 4765)
"Enable project-based security" always comes up unchecked on configure pages in 1.333,
so project permissions are lost if not rechecked before clicking Save.
- (issue 4826)
+ (issue 4826)
Project read permission was not enforced via /jobCaseInsensitive/jobname path.
@@ -1360,25 +2532,25 @@ Upcoming changes
Fixed the over zealous escaping in the inlined unit test failure report.
Allow non-absolute URLs in sidebar links that do not end with slash character.
- (issue 4720)
+ (issue 4720)
Build other projects "even when unstable" option was not working with Maven projects.
- (issue 4739)
+ (issue 4739)
When renaming a log recorder, check new name uses valid characters, remove config file for
old name and redirect to new name after save.
@@ -1412,7 +2584,7 @@ Upcoming changes
Hudson is internally capable of supporting multiple update sites.
Added a new "safe-restart" CLI command, also accessible at "/safeRestart", and used for post-upgrade/plugin install restart.
- (issue 4553)
+ (issue 4553)
Added "delete-builds" CLI command for bulk build record deletion.
@@ -1420,16 +2592,16 @@ Upcoming changes
Fixed another NotExportableException when making a remote API call on a project.
Broke NetBeans integration and possibly others.
- (issue 4760)
+ (issue 4760)
Fixed a regression in 1.331 where previously disabled plugins and their artifacts in build.xml can cause build records to fail to load.
- (issue 4752)
+ (issue 4752)
Fixed NotExportableException when making a remote API call on a project.
- (report)
+ (report)
Fixed IllegalArgumentException: name
(report)
@@ -1438,16 +2610,16 @@ Upcoming changes
Fixed a memory leak problem with the groovysh Hudson CLI command.
- (issue 4618)
+ (issue 4618)
CVS changelog reports were incorrect if built from tags.
- (issue 1816)
+ (issue 1816)
Upstream projects list was lost when saving matrix type project.
- (issue 3607)
+ (issue 3607)
Fixed a problem where taglibs defined in plugins cannot be seen from other plugins.
(report)
@@ -1455,7 +2627,7 @@ Upcoming changes
Improved the UI of taking a node offline.
Added improved logging for exceptions encountered when attempting to invoke Maven in Maven projects.
- (issue 3273)
+ (issue 3273)
Automated tool downloads are made more robust by using HTTP download retries.
@@ -1468,30 +2640,30 @@ Upcoming changes
Fixed NoSuchMethodError error during error recovery with Maven 2.1.
- (issue 2373)
+ (issue 2373)
RemoteClassLoader does not persist retrieved classes with package structure
- (issue 4657)
+ (issue 4657)
Update center switched over from https://hudson.dev.java.net/ to http://hudson-ci.org/
Use tree view to show 17-40 build artifacts on build/project pages.
- (issue 2280)
+ (issue 2280)
When showing why a build is pending, Hudson now puts hyperlinks to node/label/project names.
Custom workspace is now subject to the variable expansion.
- (issue 3997)
+ (issue 3997)
Overview of all SCM polling activity was never showing any entries.
- (issue 4609)
+ (issue 4609)
Fixed a bogus IOException in the termination of CLI.
@@ -1524,10 +2696,10 @@ Upcoming changes
Worked around a possible Windows slave hang on start up.
- (details)
+ (details)
Inability to access hudson.dev.java.net shouldn't prevent Hudson from working.
- (issue 4590)
+ (issue 4590)
Added a CLI command install-tool to invoke a tool auto-installation from Hudson CLI.
(report)
@@ -1537,7 +2709,7 @@ Upcoming changes
Build page now shows where the build was done.
Fixed an NPE in MavenBuild$RunnerImpl.decideWorkspace
- (issue 4226)
+ (issue 4226)
"Test e-mail" feature in the system configuration page wasn't taking most of the parameters from the current values of the form.
- (issue 3983)
+ (issue 3983)
If a Maven project is built with "-N" or "--non-recursive" in the goals, it will not attempt to
load and parse the POMs for any modules defined in the root POM.
- (issue 4491)
+ (issue 4491)
Update center will create *.bak files to make it easier for manual roll back of botched upgrades.
Vastly improved the default MIME type table of the built-in servlet container.
Javadoc location is now subject to the variable expansions.
- (issue 3942)
+ (issue 3942)
JNLP clients now report the reason when the connection is rejected by the master.
- (issue 3889)
+ (issue 3889)
Added call to MailSender in RunnerImpl.cleanUp so that mail gets sent for top-level Maven project as well as individual modules. This means mail will be sent if there are POM parsing errors, etc.
- (issue 1066)
+ (issue 1066)
Default value for password parameter in a parameterized project was not saved.
- (issue 4333)
+ (issue 4333)
Run sequentially option for Matrix project was not visible unless Axes was checked.
- (issue 4366)
+ (issue 4366)
Fix launching Windows slave for slave name with space or other characters needed encoding.
- (issue 4392)
+ (issue 4392)
Support authentication when running java -jar hudson-core-*.jar using username/password
included in HUDSON_HOME URL; also removed dependency on winstone.jar.
- (issue 4400)
+ (issue 4400)
Added client-side validator for required fields and used this to replace some AJAX calls.
@@ -1643,7 +2815,7 @@ Upcoming changes
Creation of symlinks failed (or created in wrong location) since 1.320.
- (issue 4301)
+ (issue 4301)
Fixed a NoClassDefFoundError problem that happens in remoting+maven+3rd plugin combo.
report
@@ -1653,34 +2825,34 @@ Upcoming changes
Manually wiping out a workspace from GUI can cause NPE with some SCM plugins.
Fixed ClassCastException in JavaMail with some application servers.
- (issue 1261)
+ (issue 1261)
Fixed a bug in the tabular display of matrix projects.
- (issue 4245)
+ (issue 4245)
Avoid division by zero error in swap space monitor.
- (issue 4284)
+ (issue 4284)
Avoid duplicate My Views links after Reload configuration from disk.
- (issue 4272)
+ (issue 4272)
Removed need for hack that lowered build health scores by one, so now 4/5 is reported as 80 instead of 79.
- (issue 4286)
+ (issue 4286)
Fixed renaming a job to a name that includes a + character.
Matrix project did not properly handle axis values with some special characters such as slash.
- (issue 2670)
+ (issue 2670)
Added validation for axis names in Matrix project.
Ajax validator for name of a new job now warns about invalid characters.
Maven builder in freestyle projects now supports "Use private repository" option.
- (issue 4205)
+ (issue 4205)
Maven incremental builds now rebuild failed/unstable modules from previous builds, even if they are unchanged.
- (issue 4152)
+ (issue 4152)
Labels are listed in lexicographic order.
(report)
@@ -1694,12 +2866,12 @@ Upcoming changes
Make dynamic labelling of nodes clearer and easier to work with.
Plugins can mark themselves as incompatible with earlier versions to notify users during upgrade.
- (issue 4056)
+ (issue 4056)
Footer now includes a timestamp.
Advanced option now available for all project types to keep builds in queue while upstream projects are building. Off by default.
- (issue 1938)
+ (issue 1938)
Fixed a bug in Winstone that hides the root cause of exceptions.
Failed Junit tests will display error message and stacktrace even when no
additional TestDataPublishers are attached to the project.
- (issue 4257)
+ (issue 4257)
Maven test failures will again properly mark a build as unstable,
even if later task segments are successful.
- (issue 4177)
+ (issue 4177)
Matrix permissions with LDAP now properly validates group names using configured
prefix and case settings; added help text about these settings.
- (issue 3459)
+ (issue 3459)
Improved the debian package to set USER and HOME.
(report)
@@ -1747,7 +2919,7 @@ Upcoming changes
Debian start-up script should inherit LANG and other key environment variables.
Dynamic label computation wasn't re-done properly for the master node.
- (issue 4235)
+ (issue 4235)
Form validation for the remote FS root of slaves was not functioning.
Plugin installation / Hudson upgrade are made more robust in the face of possible connection abortion.
(report)
Global and per-node environment vars are made available to SCM checkout.
- (issue 4124)
+ (issue 4124)
You can designate certain combinations in a matrix project as "touchstone builds". These will
be run first, and the rest of the combinations will run if the touchstone builds are successful.
- (issue 1613)
+ (issue 1613)
Added BUILD_URL and JOB_URL to the exposed environment variables.
(request)
@@ -1788,47 +2960,47 @@ Upcoming changes
Fixed an encoding problem in CVS changelog calculation.
- (issue 3979)
+ (issue 3979)
Environment variables are considered in test paths.
- (issue 3451)
+ (issue 3451)
A failing test is recorded when JUnit XML is invalid
- (issue 3149)
+ (issue 3149)
Fixed possible Unable to call getCredential. Invalid object ID race problem.
- (issue 4176)
+ (issue 4176)
If the timing coincides between polling and build, Hudson ended up creating multiple workspaces for the same job,
even when concurrent build is off.
- (issue 4202)
+ (issue 4202)
Hudson shouldn't immediately launch a slave newly created via copy.
report
@@ -1852,7 +3024,7 @@ Upcoming changes
Added support for optional alternate Maven settings file for use
in embedded Maven for POM parsing as well as actual Maven
execution.
- (issue 2575)
+ (issue 2575)
Added a test button to the PAM configuration to make sure Hudson has read access to
/etc/shadow
@@ -1866,7 +3038,7 @@ Upcoming changes
(report)
Implemented more intelligent polling assisted by commit-hook from SVN repository.
- (details)
+ (details)
Subversion support is moved into a plugin to isolate SVNKit that has GPL-like license.
Fixed a performance problem in master/slave file copy.
- (issue 3799)
+ (issue 3799)
Set time out to avoid infinite hang when SMTP servers don't respond in time.
(report)
@@ -2122,15 +3294,15 @@ Upcoming changes
Ant/Maven installers weren't setting the file permissions on Unix.
- (issue 3850)
+ (issue 3850)
Fixed cross-site scripting vulnerabilities, thanks to Steve Milner.
Changing number of executors for master node required Hudson restart.
- (issue 3092)
+ (issue 3092)
Improved validation and help text regarding when number of executors setting may be zero.
- (issue 2110)
+ (issue 2110)
NPE fix in the remote API if @xpath is used without @exclude.
(patch)
@@ -2159,7 +3331,7 @@ Upcoming changes
init script ($HUDSON_HOME/init.groovy) is now run with uber-classloader.
Maven2 projects may fail with "Cannot lookup required component".
- (issue 3706)
+ (issue 3706)
Toned down the form validation of JDK, Maven, Ant installations given that we can now auto-install them.
Link to the jar file in the CLI usage page is made more robust.
- (issue 3621)
+ (issue 3621)
"Build after other projects are built" wasn't loading the proper setting.
- (issue 3284)
+ (issue 3284)
Hudson started as "java -jar hudson.war" can now restart itself on all Unix flavors.
@@ -2239,8 +3411,8 @@ Upcoming changes
Added a new extension point to contribute fragment to UDP-based auto discovery.
- Rolled back changes for HUDSON-3580 - workspace is once again deleted on svn checkout.
- (issue 3580)
+ Rolled back changes for JENKINS-3580 - workspace is once again deleted on svn checkout.
+ (issue 3580)
Hudson now handles unexpected failures in trigger plugins more gracefully.
Use 8K buffer to improve remote file copy performance.
- (issue 3524)
+ (issue 3524)
Hudson now has a CLI
@@ -2285,10 +3457,10 @@ Upcoming changes
When polling SCMs, boolean parameter sets default value collectly.
Sidebar build descriptions will not have "..." appended unless they have been truncated.
- (issue 3541)
+ (issue 3541)
Workspace with symlink causes svn exception when updating externals.
- (issue 3532)
+ (issue 3532)
Hudson now started bundling ssh-slaves plugin out of the box.
@@ -2297,14 +3469,14 @@ Upcoming changes
You can now configure which columns are displayed in a view.
"Last Stable" was also added as an optional column (not displayed by default).
- (issue 3465)
+ (issue 3465)
Preventive node monitoring like /tmp size check, swap space size check can be now disabled selectively.
- (issue 2596,
- issue 2552)
+ (issue 2596,
+ issue 2552)
diff --git a/cli/pom.xml b/cli/pom.xml
index 763c0276c175b35727cc331914c0d62b734b41ba..7b0fd67ca0e2d261a5b0e738152c1629a9d80cd8 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -1,18 +1,51 @@
4.0.0
+
pom
- org.jvnet.hudson.main
- 1.390-SNAPSHOT
+ org.jenkins-ci.main
+ 1.433-SNAPSHOT
+
cli
- Hudson CLI
+
+ Jenkins CLI
+
+
+
+ commons-codec
+ commons-codec
+ 1.4
+
+
+ ${project.groupId}
+ remoting
+
+
+
+ junit
+ junit
+ 3.8.1
+ test
+
+
+ org.jvnet.localizer
+ localizer
+ 1.10
+
+
+ org.jvnet.hudson
+ trilead-ssh2
+ build212-hudson-5
+
+ maven-assembly-plugin
+
@@ -33,7 +66,7 @@
org.jvnet.localizermaven-localizer-plugin
- 1.10
+
@@ -49,22 +82,4 @@
-
-
- ${project.groupId}
- remoting
- ${project.version}
-
-
- junit
- junit
- 3.8.1
- test
-
-
- org.jvnet.localizer
- localizer
- 1.10
-
-
diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java
index fa62fa552067df9c6cca85bed98ae29ba5de250b..f3bb461848cce07dbd4fd008ac25c62f0cf88976 100644
--- a/cli/src/main/java/hudson/cli/CLI.java
+++ b/cli/src/main/java/hudson/cli/CLI.java
@@ -23,32 +23,53 @@
*/
package hudson.cli;
+import com.trilead.ssh2.crypto.Base64;
+import com.trilead.ssh2.crypto.PEMDecoder;
+import com.trilead.ssh2.signature.DSASHA1Verify;
+import com.trilead.ssh2.signature.RSASHA1Verify;
+import hudson.cli.client.Messages;
import hudson.remoting.Channel;
+import hudson.remoting.PingThread;
+import hudson.remoting.Pipe;
import hudson.remoting.RemoteInputStream;
import hudson.remoting.RemoteOutputStream;
-import hudson.remoting.PingThread;
import hudson.remoting.SocketInputStream;
import hudson.remoting.SocketOutputStream;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
-import java.net.Socket;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
-import java.util.ArrayList;
-import java.util.logging.Logger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.DataOutputStream;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static java.util.logging.Level.*;
/**
- * CLI entry point to Hudson.
+ * CLI entry point to Jenkins.
*
* @author Kohsuke Kawaguchi
*/
@@ -58,45 +79,32 @@ public class CLI {
private final CliEntryPoint entryPoint;
private final boolean ownsPool;
- public CLI(URL hudson) throws IOException, InterruptedException {
- this(hudson,null);
+ public CLI(URL jenkins) throws IOException, InterruptedException {
+ this(jenkins,null);
}
- public CLI(URL hudson, ExecutorService exec) throws IOException, InterruptedException {
- String url = hudson.toExternalForm();
+ public CLI(URL jenkins, ExecutorService exec) throws IOException, InterruptedException {
+ String url = jenkins.toExternalForm();
if(!url.endsWith("/")) url+='/';
ownsPool = exec==null;
pool = exec!=null ? exec : Executors.newCachedThreadPool();
+ Channel channel = null;
int clip = getCliTcpPort(url);
if(clip>=0) {
// connect via CLI port
- String host = new URL(url).getHost();
- LOGGER.fine("Trying to connect directly via TCP/IP to port "+clip+" of "+host);
- Socket s = new Socket(host,clip);
- DataOutputStream dos = new DataOutputStream(s.getOutputStream());
- dos.writeUTF("Protocol:CLI-connect");
-
- channel = new Channel("CLI connection to "+hudson, pool,
- new BufferedInputStream(new SocketInputStream(s)),
- new BufferedOutputStream(new SocketOutputStream(s)));
- } else {
+ try {
+ channel = connectViaCliPort(jenkins, url, clip);
+ } catch (IOException e) {
+ LOGGER.log(Level.FINE,"Failed to connect via CLI port. Falling back to HTTP",e);
+ }
+ }
+ if (channel==null) {
// connect via HTTP
- LOGGER.fine("Trying to connect to "+url+" via HTTP");
- url+="cli";
- hudson = new URL(url);
-
- FullDuplexHttpStream con = new FullDuplexHttpStream(hudson);
- channel = new Channel("Chunked connection to "+hudson,
- pool,con.getInputStream(),con.getOutputStream());
- new PingThread(channel,30*1000) {
- protected void onDead() {
- // noop. the point of ping is to keep the connection alive
- // as most HTTP servers have a rather short read time out
- }
- }.start();
+ channel = connectViaHttp(url);
}
+ this.channel = channel;
// execute the command
entryPoint = (CliEntryPoint)channel.waitForRemoteProperty(CliEntryPoint.class.getName());
@@ -105,6 +113,35 @@ public class CLI {
throw new IOException(Messages.CLI_VersionMismatch());
}
+ private Channel connectViaHttp(String url) throws IOException {
+ LOGGER.fine("Trying to connect to "+url+" via HTTP");
+ url+="cli";
+ URL jenkins = new URL(url);
+
+ FullDuplexHttpStream con = new FullDuplexHttpStream(jenkins);
+ Channel ch = new Channel("Chunked connection to "+jenkins,
+ pool,con.getInputStream(),con.getOutputStream());
+ new PingThread(ch,30*1000) {
+ protected void onDead() {
+ // noop. the point of ping is to keep the connection alive
+ // as most HTTP servers have a rather short read time out
+ }
+ }.start();
+ return ch;
+ }
+
+ private Channel connectViaCliPort(URL jenkins, String url, int clip) throws IOException {
+ String host = new URL(url).getHost();
+ LOGGER.fine("Trying to connect directly via TCP/IP to port "+clip+" of "+host);
+ Socket s = new Socket(host,clip);
+ DataOutputStream dos = new DataOutputStream(s.getOutputStream());
+ dos.writeUTF("Protocol:CLI-connect");
+
+ return new Channel("CLI connection to "+jenkins, pool,
+ new BufferedInputStream(new SocketInputStream(s)),
+ new BufferedOutputStream(new SocketOutputStream(s)));
+ }
+
/**
* If the server advertises CLI port, returns it.
*/
@@ -120,6 +157,9 @@ public class CLI {
return Integer.parseInt(p);
}
+ /**
+ * Shuts down the channel and closes the underlying connection.
+ */
public void close() throws IOException, InterruptedException {
channel.close();
channel.join();
@@ -128,14 +168,14 @@ public class CLI {
}
public int execute(List args, InputStream stdin, OutputStream stdout, OutputStream stderr) {
- return entryPoint.main(args,Locale.getDefault(),
+ return entryPoint.main(args, Locale.getDefault(),
new RemoteInputStream(stdin),
new RemoteOutputStream(stdout),
new RemoteOutputStream(stderr));
}
public int execute(List args) {
- return execute(args,System.in,System.out,System.err);
+ return execute(args, System.in, System.out, System.err);
}
public int execute(String... args) {
@@ -149,10 +189,42 @@ public class CLI {
return entryPoint.hasCommand(name);
}
+ /**
+ * Accesses the underlying communication channel.
+ * @since 1.419
+ */
+ public Channel getChannel() {
+ return channel;
+ }
+
+ /**
+ * Attempts to lift the security restriction on the underlying channel.
+ * This requires the administer privilege on the server.
+ *
+ * @throws SecurityException
+ * If we fail to upgrade the connection.
+ */
+ public void upgrade() {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ if (execute(Arrays.asList("groovy", "="),
+ new ByteArrayInputStream("hudson.remoting.Channel.current().setRestricted(false)".getBytes()),
+ out,out)!=0)
+ throw new SecurityException(out.toString()); // failed to upgrade
+ }
+
public static void main(final String[] _args) throws Exception {
+ System.exit(_main(_args));
+ }
+
+ public static int _main(String[] _args) throws Exception {
List args = Arrays.asList(_args);
+ List candidateKeys = new ArrayList();
+ boolean sshAuthRequestedExplicitly = false;
- String url = System.getenv("HUDSON_URL");
+ String url = System.getenv("JENKINS_URL");
+
+ if (url==null)
+ url = System.getenv("HUDSON_URL");
while(!args.isEmpty()) {
String head = args.get(0);
@@ -161,32 +233,166 @@ public class CLI {
args = args.subList(2,args.size());
continue;
}
+ if(head.equals("-i") && args.size()>=2) {
+ File f = new File(args.get(1));
+ if (!f.exists()) {
+ printUsage(Messages.CLI_NoSuchFileExists(f));
+ return -1;
+ }
+ try {
+ candidateKeys.add(loadKey(f));
+ } catch (IOException e) {
+ throw new Exception("Failed to load key: "+f,e);
+ } catch (GeneralSecurityException e) {
+ throw new Exception("Failed to load key: "+f,e);
+ }
+ args = args.subList(2,args.size());
+ sshAuthRequestedExplicitly = true;
+ continue;
+ }
break;
}
-
+
if(url==null) {
- printUsageAndExit(Messages.CLI_NoURL());
- return;
+ printUsage(Messages.CLI_NoURL());
+ return -1;
}
if(args.isEmpty())
args = Arrays.asList("help"); // default to help
+ if (candidateKeys.isEmpty())
+ addDefaultPrivateKeyLocations(candidateKeys);
+
CLI cli = new CLI(new URL(url));
try {
+ if (!candidateKeys.isEmpty()) {
+ try {
+ // TODO: server verification
+ cli.authenticate(candidateKeys);
+ } catch (IllegalStateException e) {
+ if (sshAuthRequestedExplicitly) {
+ System.err.println("The server doesn't support public key authentication");
+ return -1;
+ }
+ } catch (UnsupportedOperationException e) {
+ if (sshAuthRequestedExplicitly) {
+ System.err.println("The server doesn't support public key authentication");
+ return -1;
+ }
+ } catch (GeneralSecurityException e) {
+ if (sshAuthRequestedExplicitly) {
+ System.err.println(e.getMessage());
+ LOGGER.log(FINE,e.getMessage(),e);
+ return -1;
+ }
+ System.err.println("Failed to authenticate with your SSH keys. Proceeding with anonymous access");
+ LOGGER.log(FINE,"Failed to authenticate with your SSH keys. Proceeding with anonymous access",e);
+ }
+ }
+
// execute the command
// Arrays.asList is not serializable --- see 6835580
args = new ArrayList(args);
- System.exit(cli.execute(args, System.in, System.out, System.err));
+ return cli.execute(args, System.in, System.out, System.err);
} finally {
cli.close();
}
}
- private static void printUsageAndExit(String msg) {
+ /**
+ * Loads RSA/DSA private key in a PEM format into {@link KeyPair}.
+ */
+ public static KeyPair loadKey(File f) throws IOException, GeneralSecurityException {
+ DataInputStream dis = new DataInputStream(new FileInputStream(f));
+ byte[] bytes = new byte[(int) f.length()];
+ dis.readFully(bytes);
+ dis.close();
+ return loadKey(new String(bytes));
+ }
+
+ /**
+ * Loads RSA/DSA private key in a PEM format into {@link KeyPair}.
+ */
+ public static KeyPair loadKey(String pemString) throws IOException, GeneralSecurityException {
+ Object key = PEMDecoder.decode(pemString.toCharArray(), null);
+ if (key instanceof com.trilead.ssh2.signature.RSAPrivateKey) {
+ com.trilead.ssh2.signature.RSAPrivateKey x = (com.trilead.ssh2.signature.RSAPrivateKey)key;
+// System.out.println("ssh-rsa " + new String(Base64.encode(RSASHA1Verify.encodeSSHRSAPublicKey(x.getPublicKey()))));
+
+ return x.toJCEKeyPair();
+ }
+ if (key instanceof com.trilead.ssh2.signature.DSAPrivateKey) {
+ com.trilead.ssh2.signature.DSAPrivateKey x = (com.trilead.ssh2.signature.DSAPrivateKey)key;
+ KeyFactory kf = KeyFactory.getInstance("DSA");
+// System.out.println("ssh-dsa " + new String(Base64.encode(DSASHA1Verify.encodeSSHDSAPublicKey(x.getPublicKey()))));
+
+ return new KeyPair(
+ kf.generatePublic(new DSAPublicKeySpec(x.getY(), x.getP(), x.getQ(), x.getG())),
+ kf.generatePrivate(new DSAPrivateKeySpec(x.getX(), x.getP(), x.getQ(), x.getG())));
+ }
+
+ throw new UnsupportedOperationException("Unrecognizable key format: "+key);
+ }
+
+ /**
+ * try all the default key locations
+ */
+ private static void addDefaultPrivateKeyLocations(List keyFileCandidates) {
+ File home = new File(System.getProperty("user.home"));
+ for (String path : new String[]{".ssh/id_rsa",".ssh/id_dsa",".ssh/identity"}) {
+ File key = new File(home,path);
+ if (key.exists()) {
+ try {
+ keyFileCandidates.add(loadKey(key));
+ } catch (IOException e) {
+ // don't report an error. the user can still see it by using the -i option
+ LOGGER.log(FINE, "Failed to load "+key,e);
+ } catch (GeneralSecurityException e) {
+ LOGGER.log(FINE, "Failed to load " + key, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Authenticate ourselves against the server.
+ *
+ * @return
+ * identity of the server represented as a public key.
+ */
+ public PublicKey authenticate(Iterable privateKeys) throws IOException, GeneralSecurityException {
+ Pipe c2s = Pipe.createLocalToRemote();
+ Pipe s2c = Pipe.createRemoteToLocal();
+ entryPoint.authenticate("ssh",c2s, s2c);
+ Connection c = new Connection(s2c.getIn(), c2s.getOut());
+
+ try {
+ byte[] sharedSecret = c.diffieHellman(false).generateSecret();
+ PublicKey serverIdentity = c.verifyIdentity(sharedSecret);
+
+ // try all the public keys
+ for (KeyPair key : privateKeys) {
+ c.proveIdentity(sharedSecret,key);
+ if (c.readBoolean())
+ return serverIdentity; // succeeded
+ }
+ if (privateKeys.iterator().hasNext())
+ throw new GeneralSecurityException("Authentication failed. No private key accepted.");
+ else
+ throw new GeneralSecurityException("No private key is available for use in authentication");
+ } finally {
+ c.close();
+ }
+ }
+
+ public PublicKey authenticate(KeyPair key) throws IOException, GeneralSecurityException {
+ return authenticate(Collections.singleton(key));
+ }
+
+ private static void printUsage(String msg) {
if(msg!=null) System.out.println(msg);
System.err.println(Messages.CLI_Usage());
- System.exit(-1);
}
private static final Logger LOGGER = Logger.getLogger(CLI.class.getName());
diff --git a/cli/src/main/java/hudson/cli/CliEntryPoint.java b/cli/src/main/java/hudson/cli/CliEntryPoint.java
index 1f5478d191495bf86e20a3bc2d32ce78fab02912..57bd42e553b77336c2a0db0085ae4ce0474c76b2 100644
--- a/cli/src/main/java/hudson/cli/CliEntryPoint.java
+++ b/cli/src/main/java/hudson/cli/CliEntryPoint.java
@@ -23,6 +23,8 @@
*/
package hudson.cli;
+import hudson.remoting.Pipe;
+
import java.io.OutputStream;
import java.io.InputStream;
import java.util.List;
@@ -53,5 +55,19 @@ public interface CliEntryPoint {
*/
int protocolVersion();
+ /**
+ * Initiates authentication out of band.
+ *
+ * This method starts two-way byte channel that allows the client and the server to perform authentication.
+ * The current supported implementation is based on SSH public key authentication that mutually authenticates
+ * clients and servers.
+ *
+ * @param protocol
+ * Currently only "ssh" is supported.
+ * @throws UnsupportedOperationException
+ * If the specified protocol is not supported by the server.
+ */
+ void authenticate(String protocol, Pipe c2s, Pipe s2c);
+
int VERSION = 1;
}
diff --git a/cli/src/main/java/hudson/cli/Connection.java b/cli/src/main/java/hudson/cli/Connection.java
new file mode 100644
index 0000000000000000000000000000000000000000..543e6acb3e1d504bbf353156ebee6e9bb9e0b196
--- /dev/null
+++ b/cli/src/main/java/hudson/cli/Connection.java
@@ -0,0 +1,211 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2011, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson.cli;
+
+import hudson.remoting.SocketInputStream;
+import hudson.remoting.SocketOutputStream;
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.KeyAgreement;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.security.AlgorithmParameterGenerator;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.X509EncodedKeySpec;
+
+public class Connection {
+ public final InputStream in;
+ public final OutputStream out;
+
+ public final DataInputStream din;
+ public final DataOutputStream dout;
+
+ public Connection(Socket socket) throws IOException {
+ this(new SocketInputStream(socket),new SocketOutputStream(socket));
+ }
+
+ public Connection(InputStream in, OutputStream out) {
+ this.in = in;
+ this.out = out;
+ this.din = new DataInputStream(in);
+ this.dout = new DataOutputStream(out);
+ }
+
+//
+//
+// Convenience methods
+//
+//
+ public void writeUTF(String msg) throws IOException {
+ dout.writeUTF(msg);
+ }
+
+ public String readUTF() throws IOException {
+ return din.readUTF();
+ }
+
+ public void writeBoolean(boolean b) throws IOException {
+ dout.writeBoolean(b);
+ }
+
+ public boolean readBoolean() throws IOException {
+ return din.readBoolean();
+ }
+
+ /**
+ * Sends a serializable object.
+ */
+ public void writeObject(Object o) throws IOException {
+ ObjectOutputStream oos = new ObjectOutputStream(out);
+ oos.writeObject(o);
+ // don't close oss, which will close the underlying stream
+ // no need to flush either, given the way oos is implemented
+ }
+
+ /**
+ * Receives an object sent by {@link #writeObject(Object)}
+ */
+ public T readObject() throws IOException, ClassNotFoundException {
+ ObjectInputStream ois = new ObjectInputStream(in);
+ return (T)ois.readObject();
+ }
+
+ public void writeKey(Key key) throws IOException {
+ writeUTF(new String(Base64.encodeBase64(key.getEncoded())));
+ }
+
+ public X509EncodedKeySpec readKey() throws IOException {
+ byte[] otherHalf = Base64.decodeBase64(readUTF());
+ return new X509EncodedKeySpec(otherHalf);
+ }
+
+ /**
+ * Performs a Diffie-Hellman key exchange and produce a common secret between two ends of the connection.
+ *
+ *
+ * DH is also useful as a coin-toss algorithm. Two parties get the same random number without trusting
+ * each other.
+ */
+ public KeyAgreement diffieHellman(boolean side) throws IOException, GeneralSecurityException {
+ KeyPair keyPair;
+ PublicKey otherHalf;
+
+ if (side) {
+ AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DH");
+ paramGen.init(512);
+
+ KeyPairGenerator dh = KeyPairGenerator.getInstance("DH");
+ dh.initialize(paramGen.generateParameters().getParameterSpec(DHParameterSpec.class));
+ keyPair = dh.generateKeyPair();
+
+ // send a half and get a half
+ writeKey(keyPair.getPublic());
+ otherHalf = KeyFactory.getInstance("DH").generatePublic(readKey());
+ } else {
+ otherHalf = KeyFactory.getInstance("DH").generatePublic(readKey());
+
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("DH");
+ keyPairGen.initialize(((DHPublicKey) otherHalf).getParams());
+ keyPair = keyPairGen.generateKeyPair();
+
+ // send a half and get a half
+ writeKey(keyPair.getPublic());
+ }
+
+ KeyAgreement ka = KeyAgreement.getInstance("DH");
+ ka.init(keyPair.getPrivate());
+ ka.doPhase(otherHalf, true);
+
+ return ka;
+ }
+
+ private String detectKeyAlgorithm(KeyPair kp) {
+ return detectKeyAlgorithm(kp.getPublic());
+ }
+
+ private String detectKeyAlgorithm(PublicKey kp) {
+ if (kp instanceof RSAPublicKey) return "RSA";
+ if (kp instanceof DSAPublicKey) return "DSA";
+ throw new IllegalArgumentException("Unknown public key type: "+kp);
+ }
+
+ /**
+ * Used in conjunction with {@link #verifyIdentity(byte[])} to prove
+ * that we actually own the private key of the given key pair.
+ */
+ public void proveIdentity(byte[] sharedSecret, KeyPair key) throws IOException, GeneralSecurityException {
+ String algorithm = detectKeyAlgorithm(key);
+ writeUTF(algorithm);
+ writeKey(key.getPublic());
+
+ Signature sig = Signature.getInstance("SHA1with"+algorithm);
+ sig.initSign(key.getPrivate());
+ sig.update(key.getPublic().getEncoded());
+ sig.update(sharedSecret);
+ writeObject(sig.sign());
+ }
+
+ /**
+ * Verifies that we are talking to a peer that actually owns the private key corresponding to the public key we get.
+ */
+ public PublicKey verifyIdentity(byte[] sharedSecret) throws IOException, GeneralSecurityException {
+ try {
+ String serverKeyAlgorithm = readUTF();
+ PublicKey spk = KeyFactory.getInstance(serverKeyAlgorithm).generatePublic(readKey());
+
+ // verify the identity of the server
+ Signature sig = Signature.getInstance("SHA1with"+serverKeyAlgorithm);
+ sig.initVerify(spk);
+ sig.update(spk.getEncoded());
+ sig.update(sharedSecret);
+ sig.verify((byte[]) readObject());
+
+ return spk;
+ } catch (ClassNotFoundException e) {
+ throw new Error(e); // impossible
+ }
+ }
+
+ public void close() throws IOException {
+ in.close();
+ out.close();
+ }
+}
diff --git a/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java b/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java
index 369448ec53fd9e7ce72d415596b8c43d4cc1524f..3a6a2fa292c07340f3b85939cabea2067f61cf07 100644
--- a/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java
+++ b/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java
@@ -11,7 +11,7 @@ import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
-import sun.misc.BASE64Encoder;
+import org.apache.commons.codec.binary.Base64;
/**
* Creates a capacity-unlimited bi-directional {@link InputStream}/{@link OutputStream} pair over
@@ -38,7 +38,7 @@ public class FullDuplexHttpStream {
String authorization = null;
if (target.getUserInfo() != null) {
- authorization = new BASE64Encoder().encode(target.getUserInfo().getBytes());
+ authorization = new String(new Base64().encodeBase64(target.getUserInfo().getBytes()));
}
CrumbData crumbData = new CrumbData();
@@ -61,7 +61,7 @@ public class FullDuplexHttpStream {
input = con.getInputStream();
// make sure we hit the right URL
if(con.getHeaderField("Hudson-Duplex")==null)
- throw new IOException(target+" doesn't look like Hudson");
+ throw new IOException(target+" doesn't look like Jenkins");
// client->server uses chunked encoded POST for unlimited capacity.
con = (HttpURLConnection) target.openConnection();
diff --git a/cli/src/main/resources/hudson/cli/Messages.properties b/cli/src/main/resources/hudson/cli/Messages.properties
deleted file mode 100644
index a06ccbabf7d76b116e6a661f66123d1a623c1208..0000000000000000000000000000000000000000
--- a/cli/src/main/resources/hudson/cli/Messages.properties
+++ /dev/null
@@ -1,9 +0,0 @@
-CLI.Usage=Hudson CLI\n\
- Usage: java -jar hudson-cli.jar [-s URL] command [opts...] args...\n\
- Options:\n\
- \ -s URL : specify the server URL (defaults to the HUDSON_URL env var)\n\
- \n\
- The available commands depend on the server. Run the 'help' command to\n\
- see the list.
-CLI.NoURL=Neither -s nor the HUDSON_URL env var is specified.
-CLI.VersionMismatch=Version mismatch. This CLI cannot work with this Hudson server
\ No newline at end of file
diff --git a/cli/src/main/resources/hudson/cli/Messages_de.properties b/cli/src/main/resources/hudson/cli/Messages_de.properties
deleted file mode 100644
index 4b290fa05f21bcb813dd59cceaec77955319f966..0000000000000000000000000000000000000000
--- a/cli/src/main/resources/hudson/cli/Messages_de.properties
+++ /dev/null
@@ -1,9 +0,0 @@
-CLI.Usage=Hudson Kommandozeilenschnittstelle (Hudson CLI)\n\
- Verwendung: java -jar hudson-cli.jar [-s URL] command [opts...] args...\n\
- Optionen:\n\
- \ -s URL : URL des Hudson-Servers (Wert der Umgebungsvariable HUDSON_URL ist der Vorgabewert)\n\
- \n\
- Die verfügbaren Kommandos hängen vom kontaktierten Server ab. Verwenden Sie das Kommando \
- 'help', um eine Liste aller verfügbaren Kommandos anzuzeigen.
-CLI.NoURL=Weder die Option -s noch eine Umgebungsvariable HUDSON_URL wurde spezifiziert.
-CLI.VersionMismatch=Versionskonflikt: Diese Version von Hudson CLI ist nicht mit dem Hudson-Server kompatibel.
diff --git a/cli/src/main/resources/hudson/cli/Messages_es.properties b/cli/src/main/resources/hudson/cli/Messages_es.properties
deleted file mode 100644
index 5da93a62e2a6bd45e06d7c9e0972af522e962f38..0000000000000000000000000000000000000000
--- a/cli/src/main/resources/hudson/cli/Messages_es.properties
+++ /dev/null
@@ -1,9 +0,0 @@
-CLI.Usage=Hudson CLI\n\
- Usar: java -jar hudson-cli.jar [-s URL] command [opts...] args...\n\
- Options:\n\
- \ -s URL : dirección web (por defecto se usa la variable HUDSON_URL)\n\
- \n\
- La lista completa de comandos disponibles depende del servidor. Ejecuta\n\
- el comando ''help'' para ver la lista.
-CLI.NoURL=No se ha especificado el parámetro -s ni la variable HUDSON_URL
-CLI.VersionMismatch=La versión no coincide. Esta CLI no se puede usar en este Hudson
diff --git a/cli/src/main/resources/hudson/cli/client/Messages.properties b/cli/src/main/resources/hudson/cli/client/Messages.properties
new file mode 100644
index 0000000000000000000000000000000000000000..327b04a6f436150d8688e9d335909b81c32cf360
--- /dev/null
+++ b/cli/src/main/resources/hudson/cli/client/Messages.properties
@@ -0,0 +1,10 @@
+CLI.Usage=Jenkins CLI\n\
+ Usage: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\
+ Options:\n\
+ \ -s URL : specify the server URL (defaults to the JENKINS_URL env var)\n\
+ \n\
+ The available commands depend on the server. Run the 'help' command to\n\
+ see the list.
+CLI.NoURL=Neither -s nor the JENKINS_URL env var is specified.
+CLI.VersionMismatch=Version mismatch. This CLI cannot work with this Hudson server
+CLI.NoSuchFileExists=No such file exists: {0}
diff --git a/cli/src/main/resources/hudson/cli/Messages_da.properties b/cli/src/main/resources/hudson/cli/client/Messages_da.properties
similarity index 88%
rename from cli/src/main/resources/hudson/cli/Messages_da.properties
rename to cli/src/main/resources/hudson/cli/client/Messages_da.properties
index 264c9c146a1c8aa78b987fb6463d3d808fa5374d..84a2e91e10546ac9600d9aaa2fcde5c5870c9de2 100644
--- a/cli/src/main/resources/hudson/cli/Messages_da.properties
+++ b/cli/src/main/resources/hudson/cli/client/Messages_da.properties
@@ -21,8 +21,8 @@
# THE SOFTWARE.
CLI.VersionMismatch=Versionskonflikt. CLI''en fungerer ikke med denne Hudson server
-CLI.Usage=Hudson CLI\n\
-Brug: java -jar hudson-cli.jar [-s URL] command [opts...] args...\n\
+CLI.Usage=Jenkins_ CLI\n\
+Brug: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\
Tilvalg:\n\
De tilg\u00e6ngelige kommandoer afh\u00e6nger af serveren. K\u00f8r 'help' kommandoen for at se listen.
-CLI.NoURL=Hverken -s eller HUDSON_URL milj\u00f8variablen er defineret
+CLI.NoURL=Hverken -s eller JENKINS_URL milj\u00f8variablen er defineret
diff --git a/cli/src/main/resources/hudson/cli/client/Messages_de.properties b/cli/src/main/resources/hudson/cli/client/Messages_de.properties
new file mode 100644
index 0000000000000000000000000000000000000000..43dc33ec171b823f63294a18e9116e37f1a71eb6
--- /dev/null
+++ b/cli/src/main/resources/hudson/cli/client/Messages_de.properties
@@ -0,0 +1,9 @@
+CLI.Usage=Jenkins Kommandozeilenschnittstelle (Jenkins CLI)\n\
+ Verwendung: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\
+ Optionen:\n\
+ \ -s URL : URL des Hudson-Servers (Wert der Umgebungsvariable JENKINS_URL ist der Vorgabewert)\n\
+ \n\
+ Die verfügbaren Kommandos hängen vom kontaktierten Server ab. Verwenden Sie das Kommando \
+ 'help', um eine Liste aller verfügbaren Kommandos anzuzeigen.
+CLI.NoURL=Weder die Option -s noch eine Umgebungsvariable JENKINS_URL wurde spezifiziert.
+CLI.VersionMismatch=Versionskonflikt: Diese Version von Jenkins CLI ist nicht mit dem Hudson-Server kompatibel.
diff --git a/cli/src/main/resources/hudson/cli/client/Messages_es.properties b/cli/src/main/resources/hudson/cli/client/Messages_es.properties
new file mode 100644
index 0000000000000000000000000000000000000000..014dc22dc4ffa19fc2aedcb0d73ed8e4fb2057e9
--- /dev/null
+++ b/cli/src/main/resources/hudson/cli/client/Messages_es.properties
@@ -0,0 +1,9 @@
+CLI.Usage=Jenkins CLI\n\
+ Usar: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\
+ Options:\n\
+ \ -s URL : dirección web (por defecto se usa la variable JENKINS_URL)\n\
+ \n\
+ La lista completa de comandos disponibles depende del servidor. Ejecuta\n\
+ el comando ''help'' para ver la lista.
+CLI.NoURL=No se ha especificado el parámetro -s ni la variable JENKINS_URL
+CLI.VersionMismatch=La versión no coincide. Esta consola "CLI" no se puede usar en este Jenkins
diff --git a/cli/src/main/resources/hudson/cli/Messages_ja.properties b/cli/src/main/resources/hudson/cli/client/Messages_ja.properties
similarity index 82%
rename from cli/src/main/resources/hudson/cli/Messages_ja.properties
rename to cli/src/main/resources/hudson/cli/client/Messages_ja.properties
index 3be9f9dda28d8f00893093a973bea3c6ae88f704..deb0256320d73183c9b2bf10297bc922e10bdb3c 100644
--- a/cli/src/main/resources/hudson/cli/Messages_ja.properties
+++ b/cli/src/main/resources/hudson/cli/client/Messages_ja.properties
@@ -20,20 +20,20 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-# Neither -s nor the HUDSON_URL env var is specified.
-CLI.NoURL=-s\u3082\u74B0\u5883\u5909\u6570HUDSON_URL\u3082\u6307\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002
+# Neither -s nor the JENKINS_URL env var is specified.
+CLI.NoURL=-s\u3082\u74B0\u5883\u5909\u6570JENKINS_URL\u3082\u6307\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002
# Version mismatch. This CLI cannot work with this Hudson server
CLI.VersionMismatch=\u30D0\u30FC\u30B8\u30E7\u30F3\u30DF\u30B9\u30DE\u30C3\u30C1\u3067\u3059\u3002\u3053\u306ECLI\u306FHudson\u30B5\u30FC\u30D0\u3067\u306F\u52D5\u304D\u307E\u305B\u3093\u3002
# Hudson CLI\n\
-# Usage: java -jar hudson-cli.jar [-s URL] command [opts...] args...\n\
+# Usage: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\
# Options:\n\
# \ -s URL : specify the server URL (defaults to the HUDSON_URL env var)\n\
# \n\
# The available commands depend on the server. Run the 'help' command to\n\
# see the list.
-CLI.Usage=Hudson CLI\n\
- \u4F7F\u7528\u65B9\u6CD5: java -jar hudson-cli.jar [-s URL] command [opts...] args...\n\
+CLI.Usage=Jenkins CLI\n\
+ \u4F7F\u7528\u65B9\u6CD5: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\
\u30AA\u30D7\u30B7\u30E7\u30F3:\n\
- \ -s URL : \u30B5\u30FC\u30D0\u306EURL\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306F\u74B0\u5883\u5909\u6570HUDSON_URL\u3067\u3059\uFF09\u3002\n\
+ \ -s URL : \u30B5\u30FC\u30D0\u306EURL\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306F\u74B0\u5883\u5909\u6570JENKINS_URL\u3067\u3059\uFF09\u3002\n\
\n\
\u5229\u7528\u53EF\u80FD\u306A\u30B3\u30DE\u30F3\u30C9\u306F\u30B5\u30FC\u30D0\u306B\u4F9D\u5B58\u3057\u307E\u3059\u3002\u305D\u306E\u30EA\u30B9\u30C8\u3092\u307F\u308B\u306B\u306F'help'\u30B3\u30DE\u30F3\u30C9\u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002
diff --git a/cli/src/main/resources/hudson/cli/Messages_pt_BR.properties b/cli/src/main/resources/hudson/cli/client/Messages_pt_BR.properties
similarity index 81%
rename from cli/src/main/resources/hudson/cli/Messages_pt_BR.properties
rename to cli/src/main/resources/hudson/cli/client/Messages_pt_BR.properties
index 82f3dafc3939299fe5fba265c80ed50e06871903..1131ec8d17d04bacd323a99eaa3669896088d60a 100644
--- a/cli/src/main/resources/hudson/cli/Messages_pt_BR.properties
+++ b/cli/src/main/resources/hudson/cli/client/Messages_pt_BR.properties
@@ -23,19 +23,19 @@
# Version mismatch. This CLI cannot work with this Hudson server
CLI.VersionMismatch=A vers\ufffdo n\ufffdo coincide. Esta CLI n\ufffdo pode funcionar com este servidor Hudson
# Hudson CLI\n\
-# Usage: java -jar hudson-cli.jar [-s URL] command [opts...] args...\n\
+# Usage: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\
# Options:\n\
-# \ -s URL : specify the server URL (defaults to the HUDSON_URL env var)\n\
+# \ -s URL : specify the server URL (defaults to the JENKINS_URL env var)\n\
# \n\
# The available commands depend on the server. Run the 'help' command to\n\
# see the list.
-CLI.Usage=Hudson CLI\n\
- Uso: java -jar hudson-cli.jar [-s URL] comando [op\ufffd\ufffdes...] par\ufffdmetros...\n\
+CLI.Usage=Jenkins CLI\n\
+ Uso: java -jar jenkins-cli.jar [-s URL] comando [op\ufffd\ufffdes...] par\ufffdmetros...\n\
Op\ufffd\ufffdes:\n\
- \ -s URL : a URL do servidor (por padr\ufffdo a vari\ufffdvel de ambiente HUDSON_URL \ufffd usada)\n\
+ \ -s URL : a URL do servidor (por padr\ufffdo a vari\ufffdvel de ambiente JENKINS_URL \ufffd usada)\n\
\n\
Os comandos dispon\ufffdveis dependem do servidor. Execute o comando 'help' para\n\
ver a lista.
-# Neither -s nor the HUDSON_URL env var is specified.
-CLI.NoURL=N\ufffdo foi especificado nem '-s' e nem a vari\ufffdvel de ambiente HUDSON_URL
+# Neither -s nor the JENKINS_URL env var is specified.
+CLI.NoURL=N\ufffdo foi especificado nem '-s' e nem a vari\ufffdvel de ambiente JENKINS_URL
diff --git a/core/pom.xml b/core/pom.xml
index 759a3dd22c5e1bc1ea44bca5a6a8769df270a46e..cf3c93b607ab59451055bd83324ece4b9fa3d892 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -25,322 +25,31 @@ THE SOFTWARE.
4.0.0
+
- org.jvnet.hudson.main
+ org.jenkins-ci.mainpom
- 1.390-SNAPSHOT
+ 1.433-SNAPSHOT../pom.xml
- hudson-core
- stapler-jar
- Hudson core
+ jenkins-core
+
+ Jenkins core
- Contains the core Hudson code and view files to render HTML.
+ Contains the core Jenkins code and view files to render HTML.
-
+
true
+ 1.174
-
-
-
- org.jvnet.hudson.tools
- maven-encoding-plugin
- 1.1
-
-
-
- check-encoding
-
- compile
-
-
-
-
- com.infradna.tool
- bridge-method-injector
- 1.3
-
-
-
- process
-
-
-
-
-
- org.kohsuke.stapler
- maven-stapler-plugin
- true
-
-
- org.jvnet.maven-jellydoc-plugin
- maven-jellydoc-plugin
- 1.4
-
-
-
- ${staplerFork}
- 128m
-
-
-
- org.jvnet.localizer
- maven-localizer-plugin
- 1.10
-
-
-
- generate
-
-
- Messages.properties
- target/generated-sources/localizer
-
-
-
-
-
- org.kohsuke
- access-modifier-checker
- 1.0
-
-
-
- enforce
-
-
-
-
-
- org.codehaus.mojo
- antlr-maven-plugin
- 2.1
-
-
- cron
-
- generate
-
-
- ${basedir}/src/main/grammar
- crontab.g
-
-
-
- labelExpr
-
- generate
-
-
- ${basedir}/src/main/grammar
- labelExpr.g
-
-
-
-
-
- org.jvnet.maven-antrun-extended-plugin
- maven-antrun-extended-plugin
-
-
- generate-resources
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- version=${build.version}
-
-
-
-
-
-
-
-
- run
-
-
-
-
-
- maven-jar-plugin
-
-
-
- hudson.Main
- true
-
-
-
-
-
-
-
- org.codehaus.mojo
- apt-maven-plugin
- 1.0-alpha-2
-
- true
- org.jvnet.hudson.tools.ExtensionPointListerFactory
-
-
-
-
-
-
-
- org.jvnet.hudson.tools
- extension-point-lister
- 1.7
-
-
- com.sun
- tools
-
-
- commons-logging
- commons-logging
-
-
-
-
-
-
-
-
-
-
- debug
-
- true
-
-
-
- release
-
-
- ${project.version}
-
-
-
-
- org.codehaus.mojo
- apt-maven-plugin
- 1.0-alpha-2
-
-
-
- process
-
-
-
-
-
-
- org.kohsuke.stapler
- maven-stapler-plugin
-
-
-
- jelly-taglibdoc
-
-
-
- /lib/.*
-
-
-
-
-
-
-
-
-
-
- findbugs
-
-
-
- org.codehaus.mojo
- findbugs-maven-plugin
- 1.2
-
- Max
- Normal
- hudson.-
- src/findbugs-filter.xml
-
-
-
-
-
-
-
- cobertura
-
-
-
- org.kohsuke.gmaven
- gmaven-plugin
-
-
-
- test
-
- execute
-
-
-
- ${project.basedir}/src/build-script
-
-
-
-
-
-
-
-
- maven-surefire-plugin
-
- true
-
-
-
-
-
-
-
${project.groupId}remoting
- ${project.version}
+
${project.groupId}
@@ -348,10 +57,15 @@ THE SOFTWARE.
${project.version}
- org.jvnet.hudson
- crypto-util
+ org.jenkins-ci
+ version-number1.0
+
+ org.jenkins-ci
+ crypto-util
+ 1.1
+ org.jvnet.hudsonjtidy
@@ -364,6 +78,13 @@ THE SOFTWARE.
+
+ org.jenkins-ci
+ core-annotation-processors
+ 1.0
+ true
+
+
org.jvnet.hudsonguice
@@ -387,8 +108,8 @@ THE SOFTWARE.
org.kohsuke.stapler
- stapler-jelly
- 1.154
+ stapler-groovy
+ ${stapler.version}commons-jelly
@@ -398,14 +119,29 @@ THE SOFTWARE.
commons-jexlcommons-jexl
+
+
+ org.codehaus.groovy
+ groovy
+
+
+ org.kohsuke.stapler
+ stapler-jrebel
+ ${stapler.version}
+ org.kohsuke.staplerstapler-adjunct-timeline
- 1.2
+ 1.3
+
+
+ org.kohsuke.stapler
+ stapler-adjunct-codemirror
+ 1.1
-
+ org.kohsuke.staplerstapler-adjunct-timeline1.0
@@ -416,13 +152,24 @@ THE SOFTWARE.
com.infradna.toolbridge-method-annotation
- 1.2
+ 1.4org.kohsuke.staplerjson-lib
- 2.1-rev6
+ 2.1-rev7
+
+
+ org.jenkins-ci
+ htmlunit
+ 2.6-jenkins-5
+
+
+ xml-apis
+ xml-apis
+
+ args4j
@@ -444,11 +191,6 @@ THE SOFTWARE.
localizer1.10
-
- org.kohsuke
- graph-layouter
- 1.0
- antlrantlr
@@ -457,7 +199,7 @@ THE SOFTWARE.
org.jvnet.hudsonxstream
- 1.3.1-hudson-8
+ 1.3.1-jenkins-9jfree
@@ -467,7 +209,6 @@ THE SOFTWARE.
org.apache.antant
- 1.8.0javax.servlet
@@ -478,7 +219,6 @@ THE SOFTWARE.
commons-iocommons-io
- 1.4commons-lang
@@ -601,6 +341,10 @@ THE SOFTWARE.
commons-clicommons-cli
+
+ org.jvnet.hudson
+ commons-jelly
+
@@ -624,9 +368,20 @@ THE SOFTWARE.
- org.codehaus.groovy
+ org.jenkins-ci.groovygroovy-all
- 1.6.0
+ 1.8.2-jenkins-1
+
+
+ jline
+ jline
+ 0.9.94
+ compile
+
+
+ org.fusesource.jansi
+ jansi
+ 1.6
+ org.springframeworkspring-aop2.5
@@ -656,6 +411,7 @@ THE SOFTWARE.
junitjunit4.3.1
+ testjavax.servlet
@@ -683,52 +439,9 @@ THE SOFTWARE.
1.14
- org.jvnet.hudson
+ org.jenkins-cimemory-monitor
- 1.3
-
-
- com.octo.captcha
- jcaptcha-all
- 1.0-RC6
-
-
- hsqldb
- hsqldb
-
-
- hsqldb
- hsqldb
-
-
- net.sf.ehcache
- ehcache
-
-
- quartz
- quartz
-
-
- xerces
- xercesImpl
-
-
- xerces
- xmlParserAPIs
-
-
- org.springframework
- spring
-
-
- commons-dbcp
- commons-dbcp
-
-
- concurrent
- concurrent
-
-
+ 1.5org.codehaus.woodstox
@@ -736,14 +449,14 @@ THE SOFTWARE.
3.2.7
- org.jvnet.hudson
+ org.jenkins-cijmdns
- 3.1.6-hudson-2
+ 3.4.0-jenkins-3com.sun.winswwinsw
- 1.9
+ 1.10binexeprovided
@@ -751,17 +464,17 @@ THE SOFTWARE.
net.java.dev.jnajna
- 3.2.4
+ 3.3.0-jenkins-3
- com.sun.akuma
+ org.kohsukeakuma
- 1.2
+ 1.5org.jvnet.libpam4jlibpam4j
- 1.2
+ 1.4org.jvnet.libzfs
@@ -776,17 +489,17 @@ THE SOFTWARE.
net.java.sezpozsezpoz
- 1.7
+ 1.9
- org.jvnet.hudson
+ org.jenkins-cijinterop-wmi
- 1.0
+ 1.1
- org.jvnet.hudson
+ org.jenkins-ciwindows-remote-command
- 1.0
+ 1.3org.kohsuke.metainf-services
@@ -797,10 +510,10 @@ THE SOFTWARE.
org.jvnet.robust-http-clientrobust-http-client
- 1.1
+ 1.2
-
+ commons-codeccommons-codec1.4
@@ -819,6 +532,13 @@ THE SOFTWARE.
1.0
+
+ findbugs
+ annotations
+ 1.0.0
+ provided
+
+
+
+
+
+ org.jenkins-ci.tools
+ maven-hpi-plugin
+
+
+
+ generate-taglib-interface
+
+
+
+
+
+ org.jvnet.hudson.tools
+ maven-encoding-plugin
+
+
+
+
+ check-encoding
+
+ compile
+
+
+
+
+ com.infradna.tool
+ bridge-method-injector
+
+
+
+
+ process
+
+
+
+
+
+ org.kohsuke.stapler
+ maven-stapler-plugin
+ 1.16
+
+
+ ${staplerFork}
+ 128m
+
+
+
+ org.jvnet.localizer
+ maven-localizer-plugin
+
+
+
+
+ generate
+
+
+ Messages.properties
+ target/generated-sources/localizer
+
+
+
+
+
+ org.kohsuke
+ access-modifier-checker
+
+
+
+
+ enforce
+
+
+
+
+
+ org.codehaus.mojo
+ antlr-maven-plugin
+
+
+
+ cron
+
+ generate
+
+
+ ${basedir}/src/main/grammar
+ crontab.g
+
+
+
+ labelExpr
+
+ generate
+
+
+ ${basedir}/src/main/grammar
+ labelExpr.g
+
+
+
+
+
+ maven-dependency-plugin
+
+
+ winsw
+ generate-resources
+
+
+ copy
+
+
+
+
+ com.sun.winsw
+ winsw
+ 1.10
+ bin
+ exe
+ ${project.build.outputDirectory}/windows-service
+ jenkins.exe
+
+
+
+
+
+
+
+ maven-jar-plugin
+
+
+
+
+ hudson.Main
+ true
+
+
+
+
+
+
+
+
@@ -843,11 +708,117 @@ THE SOFTWARE.
maven-project-info-reports-plugin
- 2.1
+ 2.4false
-
+
+
+
+
+ debug
+
+ true
+
+
+
+ release
+
+
+
+ org.codehaus.mojo
+ apt-maven-plugin
+
+
+
+
+ process
+
+
+
+
+
+
+ org.kohsuke.stapler
+ maven-stapler-plugin
+
+
+
+
+ jelly-taglibdoc
+
+
+
+ /lib/.*
+
+
+
+
+
+
+
+
+
+
+ findbugs
+
+
+
+ org.codehaus.mojo
+ findbugs-maven-plugin
+ 2.3.2
+
+ Max
+ High
+ src/findbugs-filter.xml
+
+
+
+
+
+
+
+ cobertura
+
+
+
+ org.kohsuke.gmaven
+ gmaven-plugin
+
+
+
+
+ test
+
+ execute
+
+
+
+ ${project.basedir}/src/build-script
+
+
+
+
+
+
+
+
+ maven-surefire-plugin
+
+
+ true
+
+
+
+
+
+
diff --git a/core/src/filter/resources/hudson/model/hudson-version.properties b/core/src/filter/resources/hudson/model/hudson-version.properties
new file mode 100644
index 0000000000000000000000000000000000000000..9b9343d10d659357b7acccbcf34bc2aece573625
--- /dev/null
+++ b/core/src/filter/resources/hudson/model/hudson-version.properties
@@ -0,0 +1 @@
+version=${build.version}
\ No newline at end of file
diff --git a/core/src/filter/resources/jenkins/model/jenkins-version.properties b/core/src/filter/resources/jenkins/model/jenkins-version.properties
new file mode 100644
index 0000000000000000000000000000000000000000..9b9343d10d659357b7acccbcf34bc2aece573625
--- /dev/null
+++ b/core/src/filter/resources/jenkins/model/jenkins-version.properties
@@ -0,0 +1 @@
+version=${build.version}
\ No newline at end of file
diff --git a/core/src/findbugs-filter.xml b/core/src/findbugs-filter.xml
index 9245bc78a90fe65204fe9ff5064f972c620849cd..751d3fa8a5d470e36e579dd1169bcd88ea649507 100644
--- a/core/src/findbugs-filter.xml
+++ b/core/src/findbugs-filter.xml
@@ -1,139 +1,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
diff --git a/core/src/main/grammar/labelExpr.g b/core/src/main/grammar/labelExpr.g
index bbfa77784e843b7bfb7f82b1121b3f05ac5a0e57..6feb4de34e65202943e552ba449f009670fe3626 100644
--- a/core/src/main/grammar/labelExpr.g
+++ b/core/src/main/grammar/labelExpr.g
@@ -41,9 +41,10 @@ returns [Label l]
term1
returns [Label l]
{ Label r; }
- : l=term2( IFF r=term2 {l=l.iff(r);} )?
+ : l=term2( IFF r=term2 {l=l.iff(r);} )*
;
+// (a->b)->c != a->(b->c) (for example in case of a=F,b=T,c=F) so don't allow chaining
term2
returns [Label l]
{ Label r; }
@@ -53,13 +54,13 @@ returns [Label l]
term3
returns [Label l]
{ Label r; }
- : l=term4 ( OR r=term4 {l=l.or(r);} )?
+ : l=term4 ( OR r=term4 {l=l.or(r);} )*
;
term4
returns [Label l]
{ Label r; }
- : l=term5 ( AND r=term5 {l=l.and(r);} )?
+ : l=term5 ( AND r=term5 {l=l.and(r);} )*
;
term5
diff --git a/core/src/main/groovy/hudson/util/LoadMonitor.groovy b/core/src/main/groovy/hudson/util/LoadMonitor.groovy
index a5c55ad70d1e66e5dcb26f90a1e14662ce66b8ee..804094d2a4560aacbb2f186f42b42bd3aa309bc1 100644
--- a/core/src/main/groovy/hudson/util/LoadMonitor.groovy
+++ b/core/src/main/groovy/hudson/util/LoadMonitor.groovy
@@ -24,7 +24,7 @@
package hudson.util
import hudson.model.Computer
-import hudson.model.Hudson
+import jenkins.model.Jenkins
import hudson.model.Label
import hudson.model.Queue.BlockedItem
import hudson.model.Queue.BuildableItem
@@ -49,7 +49,7 @@ public class LoadMonitorImpl extends SafeTimerTask {
public LoadMonitorImpl(File dataFile) {
this.dataFile = dataFile;
- labels = Hudson.getInstance().labels*.name;
+ labels = Jenkins.getInstance().labels*.name;
printHeaders();
Trigger.timer.scheduleAtFixedRate(this,0,10*1000);
}
@@ -74,7 +74,7 @@ public class LoadMonitorImpl extends SafeTimerTask {
def data = [];
data.add(quote(FORMATTER.format(now)));
- def h = Hudson.getInstance();
+ def h = Jenkins.getInstance();
def items = h.queue.items;
def filterByType = {Class type -> items.findAll { type.isInstance(it) } }
diff --git a/core/src/main/groovy/jenkins/util/groovy/AbstractGroovyViewModule.groovy b/core/src/main/groovy/jenkins/util/groovy/AbstractGroovyViewModule.groovy
new file mode 100644
index 0000000000000000000000000000000000000000..08cb1c23a3da552cc531cf3b655b0a915b43aee6
--- /dev/null
+++ b/core/src/main/groovy/jenkins/util/groovy/AbstractGroovyViewModule.groovy
@@ -0,0 +1,47 @@
+package jenkins.util.groovy
+
+import lib.FormTagLib
+import lib.LayoutTagLib
+import org.kohsuke.stapler.jelly.groovy.JellyBuilder
+import org.kohsuke.stapler.jelly.groovy.Namespace
+import lib.JenkinsTagLib
+
+/**
+ * Base class for utility classes for Groovy view scripts
+ *
+ * Usage from script of a subclass, say ViewHelper:
+ *
+ * new ViewHelper(delegate).method();
+ *
+ * see ModularizeViewScript in ui-samples for an example how to use this class.
+ *
+ * @author Stefan Wolf (wolfs)
+ */
+abstract class AbstractGroovyViewModule {
+ JellyBuilder builder
+ FormTagLib f
+ LayoutTagLib l
+ JenkinsTagLib t
+ Namespace st
+
+ public AbstractGroovyViewModule(JellyBuilder b) {
+ builder = b
+ f= builder.namespace(FormTagLib)
+ l=builder.namespace(LayoutTagLib)
+ t=builder.namespace(JenkinsTagLib)
+ st=builder.namespace("jelly:stapler")
+
+ }
+
+ def methodMissing(String name, args) {
+ builder.invokeMethod(name,args)
+ }
+
+ def propertyMissing(String name) {
+ builder.getProperty(name)
+ }
+
+ def propertyMissing(String name, value) {
+ builder.setProperty(name, value)
+ }
+}
diff --git a/core/src/main/java/hudson/AboutJenkins.java b/core/src/main/java/hudson/AboutJenkins.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8136e1f5d572ddcb202128d313cea045b33d84f
--- /dev/null
+++ b/core/src/main/java/hudson/AboutJenkins.java
@@ -0,0 +1,30 @@
+package hudson;
+
+import hudson.model.ManagementLink;
+
+/**
+ * Show "About Jenkins" link.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+@Extension
+public class AboutJenkins extends ManagementLink {
+ @Override
+ public String getIconFileName() {
+ return "help.png";
+ }
+
+ @Override
+ public String getUrlName() {
+ return "about";
+ }
+
+ public String getDisplayName() {
+ return Messages.AboutJenkins_DisplayName();
+ }
+
+ @Override
+ public String getDescription() {
+ return Messages.AboutJenkins_Description();
+ }
+}
diff --git a/core/src/main/java/hudson/AbstractMarkupText.java b/core/src/main/java/hudson/AbstractMarkupText.java
index 4f1062615b0f3377cbce34268ec83a2f6e4abed4..ed7e319711040b7bbb8ba657a7e1227dca85d626 100644
--- a/core/src/main/java/hudson/AbstractMarkupText.java
+++ b/core/src/main/java/hudson/AbstractMarkupText.java
@@ -86,6 +86,23 @@ public abstract class AbstractMarkupText {
addMarkup(startPos,endPos,"","");
}
+ /**
+ * Inserts an A tag that surrounds the given position.
+ * But this hyperlink is less visible.
+ *
+ * @since 1.395
+ */
+ public void addHyperlinkLowKey( int startPos, int endPos, String url ) {
+ addMarkup(startPos,endPos,"","");
+ }
+
+ /**
+ * Hides the given text.
+ */
+ public void hide( int startPos, int endPos ) {
+ addMarkup(startPos,endPos,"","");
+ }
+
/**
* Adds a start tag and end tag around the entire text
*/
diff --git a/core/src/main/java/hudson/BulkChange.java b/core/src/main/java/hudson/BulkChange.java
index 9625452c95119991019f79ea2f3112cf84e996ed..db48db3de78cacd7f96ffde7305d1bc9ae787aa0 100644
--- a/core/src/main/java/hudson/BulkChange.java
+++ b/core/src/main/java/hudson/BulkChange.java
@@ -24,7 +24,6 @@
package hudson;
import hudson.model.Saveable;
-import hudson.model.Hudson;
import java.io.IOException;
@@ -78,7 +77,7 @@ import java.io.IOException;
*
*
*
- * See {@link Hudson#save()} as an example if you are not sure how to implement {@link Saveable}.
+ * See {@link jenkins.model.Jenkins#save()} as an example if you are not sure how to implement {@link Saveable}.
*
* @author Kohsuke Kawaguchi
* @since 1.249
diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java
index 57d7cff079fde195fd694a02e56b2a8dcbcae828..0371bbd74d9ce293f89aa97eceed230cbc2c83b7 100644
--- a/core/src/main/java/hudson/ClassicPluginStrategy.java
+++ b/core/src/main/java/hudson/ClassicPluginStrategy.java
@@ -23,19 +23,19 @@
*/
package hudson;
+import hudson.Plugin.DummyImpl;
import hudson.PluginWrapper.Dependency;
+import hudson.model.Hudson;
import hudson.util.IOException2;
+import hudson.util.IOUtils;
import hudson.util.MaskingClassLoader;
import hudson.util.VersionNumber;
-import hudson.Plugin.DummyImpl;
-import java.io.BufferedReader;
+import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
-import java.io.Closeable;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
@@ -45,13 +45,14 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
-import java.util.jar.Manifest;
import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.logging.Level;
import java.util.logging.Logger;
+import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
-import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.taskdefs.Expand;
import org.apache.tools.ant.types.FileSet;
@@ -84,8 +85,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
boolean isLinked = archive.getName().endsWith(".hpl");
if (isLinked) {
// resolve the .hpl file to the location of the manifest file
- String firstLine = new BufferedReader(new FileReader(archive))
- .readLine();
+ final String firstLine = IOUtils.readFirstLine(new FileInputStream(archive), "UTF-8");
if (firstLine.startsWith("Manifest-Version:")) {
// this is the manifest already
} else {
@@ -172,7 +172,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
return new PluginWrapper(pluginManager, archive, manifest, baseResourceURL,
createClassLoader(paths, dependencyLoader, atts), disableFile, dependencies, optionalDependencies);
}
-
+
@Deprecated
protected ClassLoader createClassLoader(List paths, ClassLoader parent) throws IOException {
return createClassLoader( paths, parent, null );
@@ -227,8 +227,10 @@ public class ClassicPluginStrategy implements PluginStrategy {
if (shortName.equals(yourName)) return;
// some earlier versions of maven-hpi-plugin apparently puts "null" as a literal in Hudson-Version. watch out for them.
- String hudsonVersion = atts.getValue("Hudson-Version");
- if (hudsonVersion == null || hudsonVersion.equals("null") || new VersionNumber(hudsonVersion).compareTo(splitWhen) <= 0)
+ String jenkinsVersion = atts.getValue("Jenkins-Version");
+ if (jenkinsVersion==null)
+ jenkinsVersion = atts.getValue("Hudson-Version");
+ if (jenkinsVersion == null || jenkinsVersion.equals("null") || new VersionNumber(jenkinsVersion).compareTo(splitWhen) <= 0)
optionalDependencies.add(new PluginWrapper.Dependency(shortName+':'+requireVersion));
}
}
@@ -236,7 +238,9 @@ public class ClassicPluginStrategy implements PluginStrategy {
private static final List DETACHED_LIST = Arrays.asList(
new DetachedPlugin("maven-plugin","1.296","1.296"),
new DetachedPlugin("subversion","1.310","1.0"),
- new DetachedPlugin("cvs","1.340","0.1")
+ new DetachedPlugin("cvs","1.340","0.1"),
+ new DetachedPlugin("ant","1.430.*","1.0"),
+ new DetachedPlugin("javadoc","1.430.*","1.0")
);
/**
@@ -256,9 +260,41 @@ public class ClassicPluginStrategy implements PluginStrategy {
public void initializeComponents(PluginWrapper plugin) {
}
+ public List> findComponents(Class type, Hudson hudson) {
+
+ List finders;
+ if (type==ExtensionFinder.class) {
+ // Avoid infinite recursion of using ExtensionFinders to find ExtensionFinders
+ finders = Collections.singletonList(new ExtensionFinder.Sezpoz());
+ } else {
+ finders = hudson.getExtensionList(ExtensionFinder.class);
+ }
+
+ /**
+ * See {@link ExtensionFinder#scout(Class, Hudson)} for the dead lock issue and what this does.
+ */
+ if (LOGGER.isLoggable(Level.FINER))
+ LOGGER.log(Level.FINER,"Scout-loading ExtensionList: "+type, new Throwable());
+ for (ExtensionFinder finder : finders) {
+ finder.scout(type, hudson);
+ }
+
+ List> r = new ArrayList>();
+ for (ExtensionFinder finder : finders) {
+ try {
+ r.addAll(finder._find(type, hudson));
+ } catch (AbstractMethodError e) {
+ // backward compatibility
+ for (T t : finder.findExtensions(type, hudson))
+ r.add(new ExtensionComponent(t));
+ }
+ }
+ return r;
+ }
+
public void load(PluginWrapper wrapper) throws IOException {
- // override the context classloader so that XStream activity in plugin.start()
- // will be able to resolve classes in this plugin
+ // override the context classloader. This no longer makes sense,
+ // but it is left for the backward compatibility
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(wrapper.classLoader);
try {
@@ -268,7 +304,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
wrapper.setPlugin(new DummyImpl());
} else {
try {
- Class clazz = wrapper.classLoader.loadClass(className);
+ Class> clazz = wrapper.classLoader.loadClass(className);
Object o = clazz.newInstance();
if(!(o instanceof Plugin)) {
throw new IOException(className+" doesn't extend from hudson.Plugin");
diff --git a/core/src/main/java/hudson/DNSMultiCast.java b/core/src/main/java/hudson/DNSMultiCast.java
index bcc924f7207b6a47e09f138b0b3e7bf5cb52b87e..e9e85233bb8bcb8d2668b595ff944b9a12d9cfb6 100644
--- a/core/src/main/java/hudson/DNSMultiCast.java
+++ b/core/src/main/java/hudson/DNSMultiCast.java
@@ -1,6 +1,6 @@
package hudson;
-import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;
@@ -19,7 +19,7 @@ import java.util.logging.Logger;
public class DNSMultiCast implements Closeable {
private JmDNS jmdns;
- public DNSMultiCast(Hudson hudson) {
+ public DNSMultiCast(Jenkins hudson) {
if (disabled) return; // escape hatch
try {
@@ -30,7 +30,7 @@ public class DNSMultiCast implements Closeable {
if (rootURL!=null)
props.put("url", rootURL);
try {
- props.put("version",String.valueOf(Hudson.getVersion()));
+ props.put("version",String.valueOf(Jenkins.getVersion()));
} catch (IllegalArgumentException e) {
// failed to parse the version number
}
@@ -39,7 +39,11 @@ public class DNSMultiCast implements Closeable {
if (tal!=null)
props.put("slave-port",String.valueOf(tal.getPort()));
+ props.put("server-id", Util.getDigestOf(hudson.getSecretKey()));
+
jmdns.registerService(ServiceInfo.create("_hudson._tcp.local.","hudson",
+ 80,0,0,props)); // for backward compatibility
+ jmdns.registerService(ServiceInfo.create("_jenkins._tcp.local.","jenkins",
80,0,0,props));
} catch (IOException e) {
LOGGER.log(Level.WARNING,"Failed to advertise the service to DNS multi-cast",e);
@@ -48,8 +52,12 @@ public class DNSMultiCast implements Closeable {
public void close() {
if (jmdns!=null) {
- jmdns.close();
- jmdns = null;
+// try {
+ jmdns.abort();
+ jmdns = null;
+// } catch (final IOException e) {
+// LOGGER.log(Level.WARNING,"Failed to close down JmDNS instance!",e);
+// }
}
}
diff --git a/core/src/main/java/hudson/DependencyRunner.java b/core/src/main/java/hudson/DependencyRunner.java
index 3af89079cdd9fab24eeef689cfb95a0bd8952316..449c7e0e73df3094c78ecb335088c07093f94bdb 100644
--- a/core/src/main/java/hudson/DependencyRunner.java
+++ b/core/src/main/java/hudson/DependencyRunner.java
@@ -25,7 +25,7 @@
package hudson;
import hudson.model.AbstractProject;
-import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import hudson.security.ACL;
import java.util.ArrayList;
@@ -34,6 +34,7 @@ import java.util.List;
import java.util.Set;
import java.util.Collection;
import java.util.logging.Logger;
+
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContextHolder;
@@ -60,7 +61,7 @@ public class DependencyRunner implements Runnable {
Set topLevelProjects = new HashSet();
// Get all top-level projects
LOGGER.fine("assembling top level projects");
- for (AbstractProject p : Hudson.getInstance().getAllItems(AbstractProject.class))
+ for (AbstractProject p : Jenkins.getInstance().getAllItems(AbstractProject.class))
if (p.getUpstreamProjects().size() == 0) {
LOGGER.fine("adding top level project " + p.getName());
topLevelProjects.add(p);
diff --git a/core/src/main/java/hudson/DescriptorExtensionList.java b/core/src/main/java/hudson/DescriptorExtensionList.java
index 535a58a814d0d026286e66f319674ca76ce01687..1a4b3e0e45680200cae69470297fee2bd0fc0dc9 100644
--- a/core/src/main/java/hudson/DescriptorExtensionList.java
+++ b/core/src/main/java/hudson/DescriptorExtensionList.java
@@ -26,6 +26,7 @@ package hudson;
import hudson.model.Descriptor;
import hudson.model.Describable;
import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import hudson.model.ViewDescriptor;
import hudson.model.Descriptor.FormException;
import hudson.util.AdaptedIterator;
@@ -38,12 +39,10 @@ import hudson.tasks.Publisher.DescriptorExtensionListImpl;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.concurrent.CopyOnWriteArrayList;
-import java.lang.reflect.Type;
-import java.lang.reflect.ParameterizedType;
-import org.jvnet.tiger_types.Types;
import org.kohsuke.stapler.Stapler;
import net.sf.json.JSONObject;
@@ -51,7 +50,7 @@ import net.sf.json.JSONObject;
* {@link ExtensionList} for holding a set of {@link Descriptor}s, which is a group of descriptors for
* the same extension point.
*
- * Use {@link Hudson#getDescriptorList(Class)} to obtain instances.
+ * Use {@link jenkins.model.Jenkins#getDescriptorList(Class)} to obtain instances.
*
* @param
* Represents the descriptor type. This is {@code Descriptor} normally but often there are subtypes
@@ -69,11 +68,20 @@ public class DescriptorExtensionList, D extends Descrip
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static ,D extends Descriptor>
- DescriptorExtensionList createDescriptorList(Hudson hudson, Class describableType) {
+ DescriptorExtensionList createDescriptorList(Jenkins jenkins, Class describableType) {
if (describableType == (Class) Publisher.class) {
- return (DescriptorExtensionList) new DescriptorExtensionListImpl(hudson);
+ return (DescriptorExtensionList) new DescriptorExtensionListImpl(jenkins);
}
- return new DescriptorExtensionList(hudson,describableType);
+ return new DescriptorExtensionList(jenkins,describableType);
+ }
+
+ /**
+ * @deprecated as of 1.416
+ * Use {@link #create(Jenkins, Class)}
+ */
+ public static ,D extends Descriptor>
+ DescriptorExtensionList createDescriptorList(Hudson hudson, Class describableType) {
+ return createDescriptorList((Jenkins)hudson,describableType);
}
/**
@@ -81,8 +89,16 @@ public class DescriptorExtensionList, D extends Descrip
*/
private final Class describableType;
+ /**
+ * @deprecated as of 1.416
+ * Use {@link #DescriptorExtensionList(Jenkins, Class)}
+ */
protected DescriptorExtensionList(Hudson hudson, Class describableType) {
- super(hudson, (Class)Descriptor.class, (CopyOnWriteArrayList)getLegacyDescriptors(describableType));
+ this((Jenkins)hudson,describableType);
+ }
+
+ protected DescriptorExtensionList(Jenkins jenkins, Class describableType) {
+ super(jenkins, (Class)Descriptor.class, (CopyOnWriteArrayList)getLegacyDescriptors(describableType));
this.describableType = describableType;
}
@@ -124,17 +140,30 @@ public class DescriptorExtensionList, D extends Descrip
}
/**
- * Finds a descriptor by their {@link Descriptor#clazz}.
+ * Finds a descriptor by their {@link Descriptor#getId()}.
*
* If none is found, null is returned.
*/
- public Descriptor findByName(String fullyQualifiedClassName) {
- for (Descriptor d : this)
- if(d.clazz.getName().equals(fullyQualifiedClassName))
+ public D findByName(String id) {
+ for (D d : this)
+ if(d.getId().equals(id))
return d;
return null;
}
+ @Override
+ public boolean add(D d) {
+ boolean r = super.add(d);
+ hudson.getExtensionList(Descriptor.class).add(d);
+ return r;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ hudson.getExtensionList(Descriptor.class).remove(o);
+ return super.remove(o);
+ }
+
/**
* {@link #load()} in the descriptor is not a real load activity, so locking against "this" is enough.
*/
@@ -143,11 +172,6 @@ public class DescriptorExtensionList, D extends Descrip
return this;
}
- @Override
- protected void scoutLoad() {
- // no-op, since our load() doesn't by itself do any classloading
- }
-
/**
* Loading the descriptors in this case means filtering the descriptor from the master {@link ExtensionList}.
*/
@@ -156,13 +180,12 @@ public class DescriptorExtensionList, D extends Descrip
List> r = new ArrayList>();
for( ExtensionComponent c : hudson.getExtensionList(Descriptor.class).getComponents() ) {
Descriptor d = c.getInstance();
- Type subTyping = Types.getBaseClass(d.getClass(), Descriptor.class);
- if (!(subTyping instanceof ParameterizedType)) {
- LOGGER.severe(d.getClass()+" doesn't extend Descriptor with a type parameter");
- continue; // skip this one
+ try {
+ if(d.getT()==describableType)
+ r.add((ExtensionComponent)c);
+ } catch (IllegalStateException e) {
+ LOGGER.log(Level.SEVERE, d.getClass() + " doesn't extend Descriptor with a type parameter", e); // skip this one
}
- if(Types.erasure(Types.getTypeArgument(subTyping,0))==(Class)describableType)
- r.add((ExtensionComponent)c);
}
return r;
}
diff --git a/core/src/main/java/hudson/EnvVars.java b/core/src/main/java/hudson/EnvVars.java
index 0070da51e1f12fdb9c6c8b74902b06589c4df3c4..3cb2afe340fb81baa1e359fd8338c74348225420 100644
--- a/core/src/main/java/hudson/EnvVars.java
+++ b/core/src/main/java/hudson/EnvVars.java
@@ -65,7 +65,7 @@ public class EnvVars extends TreeMap {
/**
* If this {@link EnvVars} object represents the whole environment variable set,
* not just a partial list used for overriding later, then we need to know
- * the platform for which this env vars are targeted for, or else we won't konw
+ * the platform for which this env vars are targeted for, or else we won't know
* how to merge variables properly.
*
*
diff --git a/core/src/main/java/hudson/ExtensionComponent.java b/core/src/main/java/hudson/ExtensionComponent.java
index 2f7eea3e13ae0b15d32a0e0bba019564b6a0f219..8c48632c2c5b29c9a280ccf37900a80118d65ba2 100644
--- a/core/src/main/java/hudson/ExtensionComponent.java
+++ b/core/src/main/java/hudson/ExtensionComponent.java
@@ -24,6 +24,8 @@
package hudson;
+import hudson.model.Descriptor;
+
/**
* Discovered {@link Extension} object with a bit of metadata for Hudson.
* This is a plain value object.
@@ -72,6 +74,12 @@ public class ExtensionComponent implements Comparable>
double b = that.ordinal();
if (a>b) return -1;
if (a Collection findExtensions(Class type, Hudson hudson) {
@@ -94,12 +96,12 @@ public abstract class ExtensionFinder implements ExtensionPoint {
* @return
* Can be empty but never null.
* @since 1.356
- * Older implementations provide {@link #findExtensions(Class, Hudson)}
+ * Older implementations provide {@link #findExtensions(Class,Hudson)}
*/
public abstract Collection> find(Class type, Hudson hudson);
/**
- * A pointless function to work around what appears to be a HotSpot problem. See HUDSON-5756 and bug 6933067
+ * A pointless function to work around what appears to be a HotSpot problem. See JENKINS-5756 and bug 6933067
* on BugParade for more details.
*/
public Collection> _find(Class type, Hudson hudson) {
@@ -121,13 +123,13 @@ public abstract class ExtensionFinder implements ExtensionPoint {
*
* In the mean time, another thread can load and initialize a class, and that initialization
* can eventually results in listing up extensions, for example through static initializer.
- * Such activitiy locks class initialization first, then locks extension list.
+ * Such activity locks class initialization first, then locks extension list.
*
*
* This inconsistent locking order results in a dead lock, you see.
*
*
- * So to reduce the likelihood, this method is called in prior to {@link #find(Class, Hudson)} invocation,
+ * So to reduce the likelihood, this method is called in prior to {@link #find(Class,Hudson)} invocation,
* but from outside the lock. The implementation is expected to perform all the class initialization activities
* from here.
*
@@ -306,11 +308,33 @@ public abstract class ExtensionFinder implements ExtensionPoint {
* Uses Sezpoz as the underlying mechanism.
*/
public static final class Sezpoz extends ExtensionFinder {
+
+ private volatile List> indices;
+
+ /**
+ * Loads indices (ideally once but as few times as possible), then reuse them later.
+ * {@link ExtensionList#ensureLoaded()} guarantees that this method won't be called until
+ * {@link InitMilestone#PLUGINS_PREPARED} is attained, so this method is guaranteed to
+ * see all the classes and indices.
+ */
+ private List> getIndices() {
+ // this method cannot be synchronized because of a dead lock possibility in the following order of events:
+ // 1. thread X can start listing indices, locking this object 'SZ'
+ // 2. thread Y starts loading a class, locking a classloader 'CL'
+ // 3. thread X needs to load a class, now blocked on CL
+ // 4. thread Y decides to load extensions, now blocked on SZ.
+ // 5. dead lock
+ if (indices==null) {
+ ClassLoader cl = Jenkins.getInstance().getPluginManager().uberClassLoader;
+ indices = ImmutableList.copyOf(Index.load(Extension.class, Object.class, cl));
+ }
+ return indices;
+ }
+
public Collection> find(Class type, Hudson hudson) {
List> result = new ArrayList>();
- ClassLoader cl = hudson.getPluginManager().uberClassLoader;
- for (IndexItem item : Index.load(Extension.class, Object.class, cl)) {
+ for (IndexItem item : getIndices()) {
try {
AnnotatedElement e = item.element();
Class> extType;
@@ -333,11 +357,9 @@ public abstract class ExtensionFinder implements ExtensionPoint {
} catch (LinkageError e) {
// sometimes the instantiation fails in an indirect classloading failure,
// which results in a LinkageError
- LOGGER.log(item.annotation().optional() ? Level.FINE : Level.WARNING,
- "Failed to load "+item.className(), e);
+ LOGGER.log(logLevel(item), "Failed to load "+item.className(), e);
} catch (InstantiationException e) {
- LOGGER.log(item.annotation().optional() ? Level.FINE : Level.WARNING,
- "Failed to load "+item.className(), e);
+ LOGGER.log(logLevel(item), "Failed to load "+item.className(), e);
}
}
@@ -346,9 +368,12 @@ public abstract class ExtensionFinder implements ExtensionPoint {
@Override
public void scout(Class extensionType, Hudson hudson) {
- ClassLoader cl = hudson.getPluginManager().uberClassLoader;
- for (IndexItem item : Index.load(Extension.class, Object.class, cl)) {
+ for (IndexItem item : getIndices()) {
try {
+ // we might end up having multiple threads concurrently calling into element(),
+ // but we can't synchronize this --- if we do, the one thread that's supposed to load a class
+ // can block while other threads wait for the entry into the element call().
+ // looking at the sezpoz code, it should be safe to do so
AnnotatedElement e = item.element();
Class> extType;
if (e instanceof Class) {
@@ -361,19 +386,22 @@ public abstract class ExtensionFinder implements ExtensionPoint {
extType = ((Method)e).getReturnType();
} else
throw new AssertionError();
- // accroding to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6459208
+ // according to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6459208
// this appears to be the only way to force a class initialization
Class.forName(extType.getName(),true,extType.getClassLoader());
} catch (InstantiationException e) {
- LOGGER.log(item.annotation().optional() ? Level.FINE : Level.WARNING,
- "Failed to scout "+item.className(), e);
+ LOGGER.log(logLevel(item), "Failed to scout "+item.className(), e);
} catch (ClassNotFoundException e) {
- LOGGER.log(Level.WARNING,"Failed to scout "+item.className(), e);
+ LOGGER.log(logLevel(item), "Failed to scout "+item.className(), e);
} catch (LinkageError e) {
- LOGGER.log(Level.WARNING,"Failed to scout "+item.className(), e);
+ LOGGER.log(logLevel(item), "Failed to scout "+item.className(), e);
}
}
}
+
+ private Level logLevel(IndexItem item) {
+ return item.annotation().optional() ? Level.FINE : Level.WARNING;
+ }
}
private static final Logger LOGGER = Logger.getLogger(ExtensionFinder.class.getName());
diff --git a/core/src/main/java/hudson/ExtensionList.java b/core/src/main/java/hudson/ExtensionList.java
index 208431b164236c5fcb014da935db6362ef7fa8f9..c168789ebafa4b9b49d1e3444d461424df7a02ca 100644
--- a/core/src/main/java/hudson/ExtensionList.java
+++ b/core/src/main/java/hudson/ExtensionList.java
@@ -25,6 +25,7 @@ package hudson;
import hudson.init.InitMilestone;
import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import hudson.util.AdaptedIterator;
import hudson.util.DescriptorList;
import hudson.util.Memoizer;
@@ -51,20 +52,25 @@ import java.util.logging.Logger;
* manual registration,
*
*
- * All {@link ExtensionList} instances should be owned by {@link Hudson}, even though
- * extension points can be defined by anyone on any type. Use {@link Hudson#getExtensionList(Class)}
- * and {@link Hudson#getDescriptorList(Class)} to obtain the instances.
+ * All {@link ExtensionList} instances should be owned by {@link jenkins.model.Jenkins}, even though
+ * extension points can be defined by anyone on any type. Use {@link jenkins.model.Jenkins#getExtensionList(Class)}
+ * and {@link jenkins.model.Jenkins#getDescriptorList(Class)} to obtain the instances.
*
* @param
* Type of the extension point. This class holds instances of the subtypes of 'T'.
*
* @author Kohsuke Kawaguchi
* @since 1.286
- * @see Hudson#getExtensionList(Class)
- * @see Hudson#getDescriptorList(Class)
+ * @see jenkins.model.Jenkins#getExtensionList(Class)
+ * @see jenkins.model.Jenkins#getDescriptorList(Class)
*/
public class ExtensionList extends AbstractList {
+ /**
+ * @deprecated as of 1.417
+ * Use {@link #jenkins}
+ */
public final Hudson hudson;
+ public final Jenkins jenkins;
public final Class extensionType;
/**
@@ -79,8 +85,24 @@ public class ExtensionList extends AbstractList {
*/
private final CopyOnWriteArrayList> legacyInstances;
+ /**
+ * @deprecated as of 1.416
+ * Use {@link #ExtensionList(Jenkins, Class)}
+ */
protected ExtensionList(Hudson hudson, Class extensionType) {
- this(hudson,extensionType,new CopyOnWriteArrayList>());
+ this((Jenkins)hudson,extensionType);
+ }
+
+ protected ExtensionList(Jenkins jenkins, Class extensionType) {
+ this(jenkins,extensionType,new CopyOnWriteArrayList>());
+ }
+
+ /**
+ * @deprecated as of 1.416
+ * Use {@link #ExtensionList(Jenkins, Class, CopyOnWriteArrayList)}
+ */
+ protected ExtensionList(Hudson hudson, Class extensionType, CopyOnWriteArrayList> legacyStore) {
+ this((Jenkins)hudson,extensionType,legacyStore);
}
/**
@@ -90,8 +112,9 @@ public class ExtensionList extends AbstractList {
* omits this uses a new {@link Vector}, making the storage lifespan tied to the life of {@link ExtensionList}.
* If the manually registered instances are scoped to VM level, the caller should pass in a static list.
*/
- protected ExtensionList(Hudson hudson, Class extensionType, CopyOnWriteArrayList> legacyStore) {
- this.hudson = hudson;
+ protected ExtensionList(Jenkins jenkins, Class extensionType, CopyOnWriteArrayList> legacyStore) {
+ this.hudson = (Hudson)jenkins;
+ this.jenkins = jenkins;
this.extensionType = extensionType;
this.legacyInstances = legacyStore;
}
@@ -154,7 +177,7 @@ public class ExtensionList extends AbstractList {
}
@Override
- public synchronized T remove(int index) {
+ public final synchronized T remove(int index) {
T t = get(index);
remove(t);
return t;
@@ -195,22 +218,12 @@ public class ExtensionList extends AbstractList {
return null;
}
-
- /**
- * Returns {@link ExtensionFinder}s used to search for the extension instances.
- */
- protected Iterable extends ExtensionFinder> finders() {
- return hudson.getExtensionList(ExtensionFinder.class);
- }
-
private List> ensureLoaded() {
if(extensions!=null)
return extensions; // already loaded
- if(Hudson.getInstance().getInitLevel().compareTo(InitMilestone.PLUGINS_PREPARED)<0)
+ if(Jenkins.getInstance().getInitLevel().compareTo(InitMilestone.PLUGINS_PREPARED)<0)
return legacyInstances; // can't perform the auto discovery until all plugins are loaded, so just make the legacy instances visible
- scoutLoad();
-
synchronized (getLoadLock()) {
if(extensions==null) {
List> r = load();
@@ -225,7 +238,7 @@ public class ExtensionList extends AbstractList {
* Chooses the object that locks the loading of the extension instances.
*/
protected Object getLoadLock() {
- return hudson.lookup.setIfNull(Lock.class,new Lock());
+ return jenkins.lookup.setIfNull(Lock.class,new Lock());
}
/**
@@ -235,17 +248,6 @@ public class ExtensionList extends AbstractList {
*/
private static final class Lock {}
- /**
- * See {@link ExtensionFinder#scout(Class, Hudson)} for the dead lock issue and what this does.
- */
- protected void scoutLoad() {
- if (LOGGER.isLoggable(Level.FINER))
- LOGGER.log(Level.FINER,"Scout-loading ExtensionList: "+extensionType, new Throwable());
- for (ExtensionFinder finder : finders()) {
- finder.scout(extensionType, hudson);
- }
- }
-
/**
* Loads all the extensions.
*/
@@ -253,17 +255,7 @@ public class ExtensionList extends AbstractList {
if (LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE,"Loading ExtensionList: "+extensionType, new Throwable());
- List> r = new ArrayList>();
- for (ExtensionFinder finder : finders()) {
- try {
- r.addAll(finder._find(extensionType, hudson));
- } catch (AbstractMethodError e) {
- // backward compatibility
- for (T t : finder.findExtensions(extensionType, hudson))
- r.add(new ExtensionComponent(t));
- }
- }
- return r;
+ return jenkins.getPluginManager().getPluginStrategy().findComponents(extensionType, hudson);
}
/**
@@ -279,22 +271,19 @@ public class ExtensionList extends AbstractList {
return r;
}
+ /**
+ * @deprecated as of 1.416
+ * Use {@link #create(Jenkins, Class)}
+ */
public static ExtensionList create(Hudson hudson, Class type) {
- if(type==ExtensionFinder.class)
- return new ExtensionList(hudson,type) {
- /**
- * If this ExtensionList is searching for ExtensionFinders, calling hudson.getExtensionList
- * results in infinite recursion.
- */
- @Override
- protected Iterable extends ExtensionFinder> finders() {
- return Collections.singleton(new ExtensionFinder.Sezpoz());
- }
- };
+ return create((Jenkins)hudson,type);
+ }
+
+ public static ExtensionList create(Jenkins jenkins, Class type) {
if(type.getAnnotation(LegacyInstancesAreScopedToHudson.class)!=null)
- return new ExtensionList(hudson,type);
+ return new ExtensionList(jenkins,type);
else {
- return new ExtensionList(hudson,type,staticLegacyInstances.get(type));
+ return new ExtensionList(jenkins,type,staticLegacyInstances.get(type));
}
}
diff --git a/core/src/main/java/hudson/ExtensionListView.java b/core/src/main/java/hudson/ExtensionListView.java
index 070aebd237f7e620ab17a85a8b90fd643e8e32db..4c2e8d9604fc9a243a4db6540639c2fd3320daaf 100644
--- a/core/src/main/java/hudson/ExtensionListView.java
+++ b/core/src/main/java/hudson/ExtensionListView.java
@@ -23,7 +23,7 @@
*/
package hudson;
-import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import hudson.tasks.UserNameResolver;
import hudson.util.CopyOnWriteList;
@@ -37,7 +37,7 @@ import java.util.Collection;
*
*
* Instances of this class can be created statically as a singleton, but it provides the view
- * to {@link ExtensionList} of the current {@link Hudson}.
+ * to {@link ExtensionList} of the current {@link jenkins.model.Jenkins}.
* Write operations to this list will update the legacy instances on {@link ExtensionList}.
*
*
@@ -58,7 +58,7 @@ public class ExtensionListView {
public static List createList(final Class type) {
return new AbstractList() {
private ExtensionList storage() {
- return Hudson.getInstance().getExtensionList(type);
+ return Jenkins.getInstance().getExtensionList(type);
}
@Override
@@ -103,7 +103,7 @@ public class ExtensionListView {
public static CopyOnWriteList createCopyOnWriteList(final Class type) {
return new CopyOnWriteList() {
private ExtensionList storage() {
- return Hudson.getInstance().getExtensionList(type);
+ return Jenkins.getInstance().getExtensionList(type);
}
@Override
diff --git a/core/src/main/java/hudson/ExtensionPoint.java b/core/src/main/java/hudson/ExtensionPoint.java
index 613a142dd7faa1b61a526dc416fb8cf5027a2846..cd5ef8cc21572666c567d9936073d0c2493d86ca 100644
--- a/core/src/main/java/hudson/ExtensionPoint.java
+++ b/core/src/main/java/hudson/ExtensionPoint.java
@@ -23,8 +23,6 @@
*/
package hudson;
-import hudson.model.Hudson;
-
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -50,7 +48,7 @@ import java.lang.annotation.Target;
public interface ExtensionPoint {
/**
* Used by designers of extension points (direct subtypes of {@link ExtensionPoint}) to indicate that
- * the legacy instances are scoped to {@link Hudson} instance. By default, legacy instances are
+ * the legacy instances are scoped to {@link jenkins.model.Jenkins} instance. By default, legacy instances are
* static scope.
*/
@Target(TYPE)
diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java
index c64e61f858e6daa5a635049ebb3713b591bcafa6..54ed063e8ab2ec6600c4d4591525987fa632b5d5 100644
--- a/core/src/main/java/hudson/FilePath.java
+++ b/core/src/main/java/hudson/FilePath.java
@@ -2,7 +2,8 @@
* The MIT License
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
- * Eric Lefevre-Ardant, Erik Ramfelt, Michael B. Donohue, Alan Harder
+ * Eric Lefevre-Ardant, Erik Ramfelt, Michael B. Donohue, Alan Harder,
+ * Manufacture Francaise des Pneumatiques Michelin, Romain Seguy
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,7 +27,7 @@ package hudson;
import hudson.Launcher.LocalLauncher;
import hudson.Launcher.RemoteLauncher;
-import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import hudson.model.TaskListener;
import hudson.model.AbstractProject;
import hudson.model.Item;
@@ -38,6 +39,8 @@ import hudson.remoting.Pipe;
import hudson.remoting.RemoteOutputStream;
import hudson.remoting.VirtualChannel;
import hudson.remoting.RemoteInputStream;
+import hudson.remoting.Which;
+import hudson.security.AccessControlled;
import hudson.util.DirScanner;
import hudson.util.IOException2;
import hudson.util.HeadBufferingStream;
@@ -46,7 +49,6 @@ import hudson.util.IOUtils;
import static hudson.util.jna.GNUCLibrary.LIBC;
import static hudson.Util.fixEmpty;
import static hudson.FilePath.TarCompression.GZIP;
-import hudson.os.PosixAPI;
import hudson.org.apache.tools.tar.TarInputStream;
import hudson.util.io.Archiver;
import hudson.util.io.ArchiverFactory;
@@ -61,7 +63,6 @@ import org.apache.commons.fileupload.FileItem;
import org.kohsuke.stapler.Stapler;
import org.jvnet.robust_http_client.RetryableHttpStream;
-import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
@@ -84,6 +85,7 @@ import java.util.List;
import java.util.StringTokenizer;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.concurrent.ExecutionException;
@@ -91,10 +93,15 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.zip.GZIPOutputStream;
import java.util.zip.GZIPInputStream;
-import java.util.zip.ZipInputStream;
import com.sun.jna.Native;
+import java.util.Enumeration;
+import java.util.logging.Logger;
+import org.apache.tools.ant.taskdefs.Chmod;
+import org.apache.tools.zip.ZipFile;
+import org.apache.tools.zip.ZipEntry;
+
/**
* {@link File} like object with remoting support.
*
@@ -413,8 +420,12 @@ public final class FilePath implements Serializable {
*/
public void unzip(final FilePath target) throws IOException, InterruptedException {
target.act(new FileCallable() {
+
public Void invoke(File dir, VirtualChannel channel) throws IOException {
- unzip(dir,FilePath.this.read());
+ if (FilePath.this.isRemote())
+ unzip(dir, FilePath.this.read()); // use streams
+ else
+ unzip(dir, new File(FilePath.this.getRemote())); // shortcut to local file
return null;
}
private static final long serialVersionUID = 1L;
@@ -461,21 +472,40 @@ public final class FilePath implements Serializable {
}
private void unzip(File dir, InputStream in) throws IOException {
+ File tmpFile = File.createTempFile("tmpzip", null); // uses java.io.tmpdir
+ try {
+ IOUtils.copy(in, tmpFile);
+ unzip(dir,tmpFile);
+ }
+ finally {
+ tmpFile.delete();
+ }
+ }
+
+ private void unzip(File dir, File zipFile) throws IOException {
dir = dir.getAbsoluteFile(); // without absolutization, getParentFile below seems to fail
- ZipInputStream zip = new ZipInputStream(new BufferedInputStream(in));
- java.util.zip.ZipEntry e;
+ ZipFile zip = new ZipFile(zipFile);
+ Enumeration entries = zip.getEntries();
try {
- while((e=zip.getNextEntry())!=null) {
- File f = new File(dir,e.getName());
- if(e.isDirectory()) {
+ while (entries.hasMoreElements()) {
+ ZipEntry e = entries.nextElement();
+ File f = new File(dir, e.getName());
+ if (e.isDirectory()) {
f.mkdirs();
} else {
File p = f.getParentFile();
- if(p!=null) p.mkdirs();
- IOUtils.copy(zip, f);
+ if (p != null) {
+ p.mkdirs();
+ }
+ IOUtils.copy(zip.getInputStream(e), f);
f.setLastModified(e.getTime());
- zip.closeEntry();
+ try {
+ FilePath target = new FilePath(f);
+ target.chmod(e.getUnixMode());
+ } catch (InterruptedException ex) {
+ LOGGER.log(Level.WARNING, "unable to set permissions", ex);
+ }
}
}
} finally {
@@ -625,7 +655,7 @@ public final class FilePath implements Serializable {
listener.getLogger().println(message);
// for HTTP downloads, enable automatic retry for added resilience
- InputStream in = archive.getProtocol().equals("http") ? new RetryableHttpStream(archive) : con.getInputStream();
+ InputStream in = archive.getProtocol().startsWith("http") ? ProxyConfiguration.getInputStream(archive) : con.getInputStream();
CountingInputStream cis = new CountingInputStream(in);
try {
if(archive.toExternalForm().endsWith(".zip"))
@@ -750,7 +780,7 @@ public final class FilePath implements Serializable {
}
} else {
// the file is on the local machine.
- return callable.invoke(new File(remote), Hudson.MasterComputer.localChannel);
+ return callable.invoke(new File(remote), Jenkins.MasterComputer.localChannel);
}
}
@@ -760,7 +790,7 @@ public final class FilePath implements Serializable {
*/
public Future actAsync(final FileCallable callable) throws IOException, InterruptedException {
try {
- return (channel!=null ? channel : Hudson.MasterComputer.localChannel)
+ return (channel!=null ? channel : Jenkins.MasterComputer.localChannel)
.callAsync(new FileCallableWrapper(callable));
} catch (IOException e) {
// wrap it into a new IOException so that we get the caller's stack trace as well.
@@ -1078,13 +1108,44 @@ public final class FilePath implements Serializable {
if(!isUnix() || mask==-1) return;
act(new FileCallable() {
public Void invoke(File f, VirtualChannel channel) throws IOException {
- if(File.separatorChar=='/' && LIBC.chmod(f.getAbsolutePath(),mask)!=0)
- throw new IOException("Failed to chmod "+f+" : "+LIBC.strerror(Native.getLastError()));
+ _chmod(f, mask);
+
return null;
}
});
}
+ /**
+ * Run chmod via libc if we can, otherwise fall back to Ant.
+ */
+ private static void _chmod(File f, int mask) throws IOException {
+ if (Functions.isWindows()) return; // noop
+
+ try {
+ if(LIBC.chmod(f.getAbsolutePath(),mask)!=0) {
+ throw new IOException("Failed to chmod "+f+" : "+LIBC.strerror(Native.getLastError()));
+ }
+ } catch(NoClassDefFoundError e) { // cf. https://groups.google.com/group/hudson-dev/browse_thread/thread/6d16c3e8ea0dbc9?hl=fr
+ _chmodAnt(f, mask);
+ } catch(UnsatisfiedLinkError e2) { // HUDSON-8155: use Ant's chmod task on non-GNU C systems
+ _chmodAnt(f, mask);
+ }
+ }
+
+ private static void _chmodAnt(File f, int mask) {
+ if (!CHMOD_WARNED) { // only warn this once to avoid flooding the log
+ CHMOD_WARNED = true;
+ LOGGER.warning("GNU C Library not available: Using Ant's chmod task instead.");
+ }
+ Chmod chmodTask = new Chmod();
+ chmodTask.setProject(new Project());
+ chmodTask.setFile(f);
+ chmodTask.setPerm(Integer.toOctalString(mask));
+ chmodTask.execute();
+ }
+
+ private static boolean CHMOD_WARNED = false;
+
/**
* Gets the file permission bit mask.
*
@@ -1097,7 +1158,7 @@ public final class FilePath implements Serializable {
if(!isUnix()) return -1;
return act(new FileCallable() {
public Integer invoke(File f, VirtualChannel channel) throws IOException {
- return PosixAPI.get().stat(f.getPath()).mode();
+ return IOUtils.mode(f);
}
});
}
@@ -1164,9 +1225,23 @@ public final class FilePath implements Serializable {
* can be empty but always non-null.
*/
public FilePath[] list(final String includes) throws IOException, InterruptedException {
+ return list(includes, null);
+ }
+
+ /**
+ * List up files in this directory that matches the given Ant-style filter.
+ *
+ * @param includes
+ * @param excludes
+ * See {@link FileSet} for the syntax. String like "foo/*.zip" or "foo/**/*.xml"
+ * @return
+ * can be empty but always non-null.
+ * @since 1.407
+ */
+ public FilePath[] list(final String includes, final String excludes) throws IOException, InterruptedException {
return act(new FileCallable() {
public FilePath[] invoke(File f, VirtualChannel channel) throws IOException {
- String[] files = glob(f,includes);
+ String[] files = glob(f,includes,excludes);
FilePath[] r = new FilePath[files.length];
for( int i=0; i)subject ==null) return FormValidation.ok();
+ if(value==null) return FormValidation.ok();
// a common mistake is to use wildcard
if(value.contains("*")) return FormValidation.error(Messages.FilePath_validateRelativePath_wildcardNotAllowed());
@@ -1822,6 +1933,14 @@ public final class FilePath implements Serializable {
}
}
+ private static void checkPermissionForValidate() {
+ AccessControlled subject = Stapler.getCurrentRequest().findAncestorObject(AbstractProject.class);
+ if (subject == null)
+ Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
+ else
+ subject.checkPermission(Item.CONFIGURE);
+ }
+
/**
* A convenience method over {@link #validateRelativePath(String, boolean, boolean)}.
*/
@@ -1841,7 +1960,7 @@ public final class FilePath implements Serializable {
public VirtualChannel getChannel() {
if(channel!=null) return channel;
- else return Hudson.MasterComputer.localChannel;
+ else return Jenkins.MasterComputer.localChannel;
}
/**
@@ -1877,6 +1996,8 @@ public final class FilePath implements Serializable {
public static int SIDE_BUFFER_SIZE = 1024;
+ private static final Logger LOGGER = Logger.getLogger(FilePath.class.getName());
+
/**
* Adapts {@link FileCallable} to {@link Callable}.
*/
diff --git a/core/src/main/java/hudson/FileSystemProvisioner.java b/core/src/main/java/hudson/FileSystemProvisioner.java
index 40b28d9c541c317ff884c8ffd017230881472023..0d9f355e12603f596486e65f7ee6ffa165423588 100644
--- a/core/src/main/java/hudson/FileSystemProvisioner.java
+++ b/core/src/main/java/hudson/FileSystemProvisioner.java
@@ -30,7 +30,7 @@ import hudson.model.Computer;
import hudson.model.Describable;
import hudson.model.Job;
import hudson.model.TaskListener;
-import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import hudson.model.listeners.RunListener;
import hudson.scm.SCM;
@@ -174,7 +174,7 @@ public abstract class FileSystemProvisioner implements ExtensionPoint, Describab
public abstract WorkspaceSnapshot snapshot(AbstractBuild,?> build, FilePath ws, String glob, TaskListener listener) throws IOException, InterruptedException;
public FileSystemProvisionerDescriptor getDescriptor() {
- return (FileSystemProvisionerDescriptor) Hudson.getInstance().getDescriptorOrDie(getClass());
+ return (FileSystemProvisionerDescriptor) Jenkins.getInstance().getDescriptorOrDie(getClass());
}
/**
@@ -186,7 +186,7 @@ public abstract class FileSystemProvisioner implements ExtensionPoint, Describab
* Returns all the registered {@link FileSystemProvisioner} descriptors.
*/
public static DescriptorExtensionList all() {
- return Hudson.getInstance().getDescriptorList(FileSystemProvisioner.class);
+ return Jenkins.getInstance().getDescriptorList(FileSystemProvisioner.class);
}
/**
diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java
index 1098c5add69faafc0b73a59c1700e59f061370d5..b15cc76599410564420593533a97de3f7889a098 100644
--- a/core/src/main/java/hudson/Functions.java
+++ b/core/src/main/java/hudson/Functions.java
@@ -2,7 +2,8 @@
* The MIT License
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
- * Yahoo! Inc., Stephen Connolly, Tom Huybrechts, Alan Harder, Romain Seguy
+ * Yahoo! Inc., Stephen Connolly, Tom Huybrechts, Alan Harder, Manufacture
+ * Francaise des Pneumatiques Michelin, Romain Seguy
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,33 +25,17 @@
*/
package hudson;
+import hudson.cli.CLICommand;
import hudson.console.ConsoleAnnotationDescriptor;
import hudson.console.ConsoleAnnotatorFactory;
-import hudson.model.AbstractProject;
-import hudson.model.Action;
-import hudson.model.Describable;
-import hudson.model.Descriptor;
-import hudson.model.Hudson;
-import hudson.model.Item;
-import hudson.model.ItemGroup;
-import hudson.model.Items;
-import hudson.model.Job;
-import hudson.model.JobPropertyDescriptor;
-import hudson.model.ModelObject;
-import hudson.model.Node;
-import hudson.model.PageDecorator;
-import hudson.model.ParameterDefinition;
+import hudson.model.*;
import hudson.model.ParameterDefinition.ParameterDescriptor;
-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;
import hudson.security.Permission;
import hudson.security.SecurityRealm;
+import hudson.security.captcha.CaptchaSupport;
import hudson.security.csrf.CrumbIssuer;
import hudson.slaves.Cloud;
import hudson.slaves.ComputerLauncher;
@@ -69,6 +54,9 @@ import hudson.scm.SCMDescriptor;
import hudson.util.Secret;
import hudson.views.MyViewsTabBar;
import hudson.views.ViewsTabBar;
+import hudson.widgets.RenderOnDemandClosure;
+import jenkins.model.GlobalConfiguration;
+import jenkins.model.Jenkins;
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyTagException;
@@ -82,7 +70,9 @@ import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
+import org.kohsuke.stapler.jelly.InternationalizedStringExpression.RawHtmlArgument;
+import javax.management.modelmbean.DescriptorSupport;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@@ -112,6 +102,7 @@ import java.util.ConcurrentModificationException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -120,6 +111,7 @@ import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
import java.util.regex.Pattern;
+import org.apache.commons.lang.StringUtils;
/**
* Utility functions used in views.
@@ -182,7 +174,7 @@ public class Functions {
}
public JDK.DescriptorImpl getJDKDescriptor() {
- return Hudson.getInstance().getDescriptorByType(JDK.DescriptorImpl.class);
+ return Jenkins.getInstance().getDescriptorByType(JDK.DescriptorImpl.class);
}
/**
@@ -286,7 +278,7 @@ public class Functions {
*
*
*
- * The head portion is the part of the URL from the {@link Hudson}
+ * The head portion is the part of the URL from the {@link jenkins.model.Jenkins}
* object to the first {@link Run} subtype. When "next/prev build"
* is chosen, this part remains intact.
*
@@ -371,7 +363,7 @@ public class Functions {
}
public static List getLogRecords() {
- return Hudson.logRecords;
+ return Jenkins.logRecords;
}
public static String printLogRecord(LogRecord r) {
@@ -535,8 +527,36 @@ public class Functions {
return Util.xmlEscape(s);
}
+ public static String xmlUnescape(String s) {
+ return s.replace("<","<").replace(">",">").replace("&","&");
+ }
+
+ public static String htmlAttributeEscape(String text) {
+ StringBuilder buf = new StringBuilder(text.length()+64);
+ for( int i=0; i')
+ buf.append(">");
+ else
+ if(ch=='&')
+ buf.append("&");
+ else
+ if(ch=='"')
+ buf.append(""");
+ else
+ if(ch=='\'')
+ buf.append("'");
+ else
+ buf.append(ch);
+ }
+ return buf.toString();
+ }
+
public static void checkPermission(Permission permission) throws IOException, ServletException {
- checkPermission(Hudson.getInstance(),permission);
+ checkPermission(Jenkins.getInstance(),permission);
}
public static void checkPermission(AccessControlled object, Permission permission) throws IOException, ServletException {
@@ -565,7 +585,7 @@ public class Functions {
return;
}
}
- checkPermission(Hudson.getInstance(),permission);
+ checkPermission(Jenkins.getInstance(),permission);
}
}
@@ -576,7 +596,7 @@ public class Functions {
* If null, returns true. This defaulting is convenient in making the use of this method terse.
*/
public static boolean hasPermission(Permission permission) throws IOException, ServletException {
- return hasPermission(Hudson.getInstance(),permission);
+ return hasPermission(Jenkins.getInstance(),permission);
}
/**
@@ -596,14 +616,14 @@ public class Functions {
return ((AccessControlled)o).hasPermission(permission);
}
}
- return Hudson.getInstance().hasPermission(permission);
+ return Jenkins.getInstance().hasPermission(permission);
}
}
public static void adminCheck(StaplerRequest req, StaplerResponse rsp, Object required, Permission permission) throws IOException, ServletException {
// this is legacy --- all views should be eventually converted to
// the permission based model.
- if(required!=null && !Hudson.adminCheck(req,rsp)) {
+ if(required!=null && !Hudson.adminCheck(req, rsp)) {
// check failed. commit the FORBIDDEN response, then abort.
rsp.setStatus(HttpServletResponse.SC_FORBIDDEN);
rsp.getOutputStream().close();
@@ -619,7 +639,7 @@ public class Functions {
* Infers the hudson installation URL from the given request.
*/
public static String inferHudsonURL(StaplerRequest req) {
- String rootUrl = Hudson.getInstance().getRootUrl();
+ String rootUrl = Jenkins.getInstance().getRootUrl();
if(rootUrl !=null)
// prefer the one explicitly configured, to work with load-balancer, frontend, etc.
return rootUrl;
@@ -632,6 +652,20 @@ public class Functions {
return buf.toString();
}
+ /**
+ * Returns the link to be displayed in the footer of the UI.
+ */
+ public static String getFooterURL() {
+ if(footerURL == null) {
+ footerURL = System.getProperty("hudson.footerURL");
+ if(StringUtils.isBlank(footerURL)) {
+ footerURL = "http://jenkins-ci.org/";
+ }
+ }
+ return footerURL;
+ }
+ private static String footerURL = null;
+
public static List getJobPropertyDescriptors(Class extends Job> clazz) {
return JobPropertyDescriptor.getPropertyDescriptors(clazz);
}
@@ -661,7 +695,7 @@ public class Functions {
}
public static List> getComputerLauncherDescriptors() {
- return Hudson.getInstance().>getDescriptorList(ComputerLauncher.class);
+ return Jenkins.getInstance().>getDescriptorList(ComputerLauncher.class);
}
public static List>> getRetentionStrategyDescriptors() {
@@ -672,6 +706,10 @@ public class Functions {
return ParameterDefinition.all();
}
+ public static List> getCaptchaSupportDescriptors() {
+ return CaptchaSupport.all();
+ }
+
public static List> getViewsTabBarDescriptors() {
return ViewsTabBar.all();
}
@@ -682,7 +720,7 @@ public class Functions {
public static List getNodePropertyDescriptors(Class extends Node> clazz) {
List result = new ArrayList();
- Collection list = (Collection) Hudson.getInstance().getDescriptorList(NodeProperty.class);
+ Collection list = (Collection) Jenkins.getInstance().getDescriptorList(NodeProperty.class);
for (NodePropertyDescriptor npd : list) {
if (npd.isApplicable(clazz)) {
result.add(npd);
@@ -694,22 +732,59 @@ public class Functions {
/**
* Gets all the descriptors sorted by their inheritance tree of {@link Describable}
* so that descriptors of similar types come nearby.
+ *
+ *
+ * We sort them by {@link Extension#ordinal()} but only for {@link GlobalConfiguration}s,
+ * as the value is normally used to compare similar kinds of extensions, and we needed
+ * {@link GlobalConfiguration}s to be able to position themselves in a layer above.
+ * This however creates some asymmetry between regular {@link Descriptor}s and {@link GlobalConfiguration}s.
+ * Perhaps it is better to introduce another annotation element? But then,
+ * extensions shouldn't normally concern themselves about ordering too much, and the only reason
+ * we needed this for {@link GlobalConfiguration}s are for backward compatibility.
*/
public static Collection getSortedDescriptorsForGlobalConfig() {
- Map r = new TreeMap();
- for (Descriptor> d : Hudson.getInstance().getExtensionList(Descriptor.class)) {
+ class Tag implements Comparable {
+ double ordinal;
+ String hierarchy;
+ Descriptor d;
+
+ Tag(double ordinal, Descriptor d) {
+ this.ordinal = ordinal;
+ this.d = d;
+ this.hierarchy = buildSuperclassHierarchy(d.clazz, new StringBuilder()).toString();
+ }
+
+ private StringBuilder buildSuperclassHierarchy(Class c, StringBuilder buf) {
+ Class sc = c.getSuperclass();
+ if (sc!=null) buildSuperclassHierarchy(sc,buf).append(':');
+ return buf.append(c.getName());
+ }
+
+ public int compareTo(Tag that) {
+ int r = Double.compare(this.ordinal, that.ordinal);
+ if (r!=0) return -r; // descending for ordinal
+ return this.hierarchy.compareTo(that.hierarchy);
+ }
+ }
+
+ ExtensionList exts = Jenkins.getInstance().getExtensionList(Descriptor.class);
+ List r = new ArrayList(exts.size());
+
+ for (ExtensionComponent c : exts.getComponents()) {
+ Descriptor d = c.getInstance();
if (d.getGlobalConfigPage()==null) continue;
- r.put(buildSuperclassHierarchy(d.clazz, new StringBuilder()).toString(),d);
+
+ r.add(new Tag(d instanceof GlobalConfiguration ? c.ordinal() : 0, d));
}
- return r.values();
- }
+ Collections.sort(r);
- private static StringBuilder buildSuperclassHierarchy(Class c, StringBuilder buf) {
- Class sc = c.getSuperclass();
- if (sc!=null) buildSuperclassHierarchy(sc,buf).append(':');
- return buf.append(c.getName());
+ List answer = new ArrayList(r.size());
+ for (Tag d : r) answer.add(d.d);
+
+ return DescriptorVisibilityFilter.apply(Jenkins.getInstance(),answer);
}
+
/**
* Computes the path to the icon of the given action
* from the context path.
@@ -754,7 +829,7 @@ public class Functions {
ItemGroup ig = i.getParent();
url = i.getShortUrl()+url;
- if(ig==Hudson.getInstance()) {
+ if(ig== Jenkins.getInstance()) {
assert i instanceof TopLevelItem;
if(view!=null && view.contains((TopLevelItem)i)) {
// if p and the current page belongs to the same view, then return a relative path
@@ -954,14 +1029,14 @@ public class Functions {
}
public static String getVersion() {
- return Hudson.VERSION;
+ return Jenkins.VERSION;
}
/**
* Resoruce path prefix.
*/
public static String getResourcePath() {
- return Hudson.RESOURCE_PATH;
+ return Jenkins.RESOURCE_PATH;
}
public static String getViewResource(Object it, String path) {
@@ -973,7 +1048,7 @@ public class Functions {
clazz = ((Descriptor)it).clazz;
StringBuilder buf = new StringBuilder(Stapler.getCurrentRequest().getContextPath());
- buf.append(Hudson.VIEW_RESOURCE_PATH).append('/');
+ buf.append(Jenkins.VIEW_RESOURCE_PATH).append('/');
buf.append(clazz.getName().replace('.','/').replace('$','/'));
buf.append('/').append(path);
@@ -1049,7 +1124,7 @@ public class Functions {
* Checks if the current user is anonymous.
*/
public static boolean isAnonymous() {
- return Hudson.getAuthentication() instanceof AnonymousAuthenticationToken;
+ return Jenkins.getAuthentication() instanceof AnonymousAuthenticationToken;
}
/**
@@ -1093,7 +1168,6 @@ public class Functions {
public static String getActionUrl(String itUrl,Action action) {
String urlName = action.getUrlName();
if(urlName==null) return null; // to avoid NPE and fail to render the whole page
-
if(SCHEME.matcher(urlName).matches())
return urlName; // absolute URL
if(urlName.startsWith("/"))
@@ -1137,7 +1211,7 @@ public class Functions {
public String getServerName() {
// Try to infer this from the configured root URL.
// This makes it work correctly when Hudson runs behind a reverse proxy.
- String url = Hudson.getInstance().getRootUrl();
+ String url = Jenkins.getInstance().getRootUrl();
try {
if(url!=null) {
String host = new URL(url).getHost();
@@ -1187,7 +1261,7 @@ public class Functions {
*/
public static List 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();
+ if(Jenkins.getInstance()==null) return Collections.emptyList();
return PageDecorator.all();
}
@@ -1209,13 +1283,13 @@ public class Functions {
}
public static String getCrumb(StaplerRequest req) {
- Hudson h = Hudson.getInstance();
+ Jenkins h = Jenkins.getInstance();
CrumbIssuer issuer = h != null ? h.getCrumbIssuer() : null;
return issuer != null ? issuer.getCrumb(req) : "";
}
public static String getCrumbRequestField() {
- Hudson h = Hudson.getInstance();
+ Jenkins h = Jenkins.getInstance();
CrumbIssuer issuer = h != null ? h.getCrumbIssuer() : null;
return issuer != null ? issuer.getDescriptor().getCrumbRequestField() : "";
}
@@ -1224,6 +1298,16 @@ public class Functions {
return new Date();
}
+ public static Locale getCurrentLocale() {
+ Locale locale=null;
+ StaplerRequest req = Stapler.getCurrentRequest();
+ if(req!=null)
+ locale = req.getLocale();
+ if(locale==null)
+ locale = Locale.getDefault();
+ return locale;
+ }
+
/**
* Generate a series of <script> tags to include script.js
* from {@link ConsoleAnnotatorFactory}s and {@link ConsoleAnnotationDescriptor}s.
@@ -1273,6 +1357,10 @@ public class Functions {
if (o instanceof Secret) return ((Secret)o).getEncryptedValue();
return o.toString();
}
+
+ public List filterDescriptors(Object context, Iterable descriptors) {
+ return DescriptorVisibilityFilter.apply(context,descriptors);
+ }
private static final Pattern SCHEME = Pattern.compile("[a-z]+://.+");
@@ -1288,14 +1376,74 @@ public class Functions {
* {@code false} otherwise.
*
*
When the {@link Run#ARTIFACTS} permission is not turned on using the
- * {@code hudson.security.ArtifactsPermission}, this permission must not be
- * considered to be set to {@code false} for every user. It must rather be
- * like if the permission doesn't exist at all (which means that every user
- * has to have an access to the artifacts but the permission can't be
- * configured in the security screen). Got it?
+ * {@code hudson.security.ArtifactsPermission} system property, this
+ * permission must not be considered to be set to {@code false} for every
+ * user. It must rather be like if the permission doesn't exist at all
+ * (which means that every user has to have an access to the artifacts but
+ * the permission can't be configured in the security screen). Got it?
*/
public static boolean isArtifactsPermissionEnabled() {
return Boolean.getBoolean("hudson.security.ArtifactsPermission");
}
+ /**
+ * Returns {@code true} if the {@link Item#WIPEOUT} permission is enabled,
+ * {@code false} otherwise.
+ *
+ *
The "Wipe Out Workspace" action available on jobs is controlled by the
+ * {@link Item#BUILD} permission. For some specific projects, however, it is
+ * not acceptable to let users have this possibility, even it they can
+ * trigger builds. As such, when enabling the {@code hudson.security.WipeOutPermission}
+ * system property, a new "WipeOut" permission will allow to have greater
+ * control on the "Wipe Out Workspace" action.
+ */
+ public static boolean isWipeOutPermissionEnabled() {
+ return Boolean.getBoolean("hudson.security.WipeOutPermission");
+ }
+
+ public static String createRenderOnDemandProxy(JellyContext context, String attributesToCapture) {
+ return Stapler.getCurrentRequest().createJavaScriptProxy(new RenderOnDemandClosure(context,attributesToCapture));
+ }
+
+ public static String getCurrentDescriptorByNameUrl() {
+ return Descriptor.getCurrentDescriptorByNameUrl();
+ }
+
+ public static String setCurrentDescriptorByNameUrl(String value) {
+ String o = getCurrentDescriptorByNameUrl();
+ Stapler.getCurrentRequest().setAttribute("currentDescriptorByNameUrl", value);
+
+ return o;
+ }
+
+ public static void restoreCurrentDescriptorByNameUrl(String old) {
+ Stapler.getCurrentRequest().setAttribute("currentDescriptorByNameUrl", old);
+ }
+
+ public static List getRequestHeaders(String name) {
+ List r = new ArrayList();
+ Enumeration e = Stapler.getCurrentRequest().getHeaders(name);
+ while (e.hasMoreElements()) {
+ r.add(e.nextElement().toString());
+ }
+ return r;
+ }
+
+ /**
+ * Used for arguments to internationalized expressions to avoid escape
+ */
+ public static Object rawHtml(Object o) {
+ return o==null ? null : new RawHtmlArgument(o);
+ }
+
+ public static ArrayList getCLICommands() {
+ ArrayList all = new ArrayList(CLICommand.all());
+ Collections.sort(all, new Comparator() {
+ public int compare(CLICommand cliCommand, CLICommand cliCommand1) {
+ return cliCommand.getName().compareTo(cliCommand1.getName());
+ }
+ });
+ return all;
+ }
+
}
diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java
index cdc5082f787e16dc191dd374b55a8839fb2554f3..aa984ab3a1c712629f2d92e2b2ac95bc5633d2f5 100644
--- a/core/src/main/java/hudson/Launcher.java
+++ b/core/src/main/java/hudson/Launcher.java
@@ -1,7 +1,7 @@
/*
* The MIT License
*
- * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Stephen Connolly
+ * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Stephen Connolly, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,9 +24,8 @@
package hudson;
import hudson.Proc.LocalProc;
-import hudson.Proc.RemoteProc;
import hudson.model.Computer;
-import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import hudson.model.TaskListener;
import hudson.model.Node;
import hudson.remoting.Callable;
@@ -44,7 +43,9 @@ import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InterruptedIOException;
import java.io.OutputStream;
+import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
import java.util.List;
@@ -129,7 +130,7 @@ public abstract class Launcher {
* {@link Computer#currentComputer()}
*/
public Computer getComputer() {
- for( Computer c : Hudson.getInstance().getComputers() )
+ for( Computer c : Jenkins.getInstance().getComputers() )
if(c.getChannel()==channel)
return c;
return null;
@@ -144,8 +145,19 @@ public abstract class Launcher {
protected boolean[] masks;
protected FilePath pwd;
protected OutputStream stdout = NULL_OUTPUT_STREAM, stderr;
- protected InputStream stdin = new NullInputStream(0);
+ protected InputStream stdin = NULL_INPUT_STREAM;
protected String[] envs;
+ /**
+ * True to reverse the I/O direction.
+ *
+ * For example, if {@link #reverseStdout}==true, then we expose
+ * {@link InputStream} from {@link Proc} and expect the client to read from it,
+ * whereas normally we take {@link OutputStream} via {@link #stdout(OutputStream)}
+ * and feed stdout into that output.
+ *
+ * @since 1.399
+ */
+ protected boolean reverseStdin, reverseStdout, reverseStderr;
public ProcStarter cmds(String... args) {
return cmds(Arrays.asList(args));
@@ -266,6 +278,50 @@ public abstract class Launcher {
return envs;
}
+ /**
+ * Indicates that the caller will pump {@code stdout} from the child process
+ * via {@link Proc#getStdout()} (whereas by default you call {@link #stdout(OutputStream)}
+ * and let Jenkins pump stdout into your {@link OutputStream} of choosing.
+ *
+ *
+ * When this method is called, {@link Proc#getStdout()} will read the combined output
+ * of {@code stdout/stderr} from the child process, unless {@link #readStderr()} is called
+ * separately, which lets the caller read those two streams separately.
+ *
+ * @since 1.399
+ */
+ public ProcStarter readStdout() {
+ reverseStdout = true;
+ stdout = stderr = null;
+ return this;
+ }
+
+ /**
+ * In addition to the effect of {@link #readStdout()}, indicate that the caller will pump {@code stderr}
+ * from the child process separately from {@code stdout}. The stderr will be readable from
+ * {@link Proc#getStderr()} while {@link Proc#getStdout()} reads from stdout.
+ *
+ * @since 1.399
+ */
+ public ProcStarter readStderr() {
+ reverseStdout = true;
+ reverseStderr = true;
+ return this;
+ }
+
+ /**
+ * Indicates that the caller will directly write to the child process {@code stin} }via
+ * {@link Proc#getStdin()} (whereas by default you call {@link #stdin(InputStream)}
+ * and let Jenkins pump your {@link InputStream} of choosing to stdin.
+ * @since 1.399
+ */
+ public ProcStarter writeStdin() {
+ reverseStdin = true;
+ stdin = null;
+ return this;
+ }
+
+
/**
* Starts the new process as configured.
*/
@@ -284,7 +340,11 @@ public abstract class Launcher {
* Copies a {@link ProcStarter}.
*/
public ProcStarter copy() {
- return new ProcStarter().cmds(commands).pwd(pwd).masks(masks).stdin(stdin).stdout(stdout).stderr(stderr).envs(envs);
+ ProcStarter rhs = new ProcStarter().cmds(commands).pwd(pwd).masks(masks).stdin(stdin).stdout(stdout).stderr(stderr).envs(envs);
+ rhs.reverseStdin = this.reverseStdin;
+ rhs.reverseStderr = this.reverseStderr;
+ rhs.reverseStdout = this.reverseStdout;
+ return rhs;
}
}
@@ -617,7 +677,7 @@ public abstract class Launcher {
*/
public static class LocalLauncher extends Launcher {
public LocalLauncher(TaskListener listener) {
- this(listener,Hudson.MasterComputer.localChannel);
+ this(listener, Jenkins.MasterComputer.localChannel);
}
public LocalLauncher(TaskListener listener, VirtualChannel channel) {
@@ -635,7 +695,11 @@ public abstract class Launcher {
for ( int idx = 0 ; idx < jobCmd.length; idx++ )
jobCmd[idx] = jobEnv.expand(ps.commands.get(idx));
- return new LocalProc(jobCmd, Util.mapToEnv(jobEnv), ps.stdin, ps.stdout, ps.stderr, toFile(ps.pwd));
+ return new LocalProc(jobCmd, Util.mapToEnv(jobEnv),
+ ps.reverseStdin ?LocalProc.SELFPUMP_INPUT:ps.stdin,
+ ps.reverseStdout?LocalProc.SELFPUMP_OUTPUT:ps.stdout,
+ ps.reverseStderr?LocalProc.SELFPUMP_OUTPUT:ps.stderr,
+ toFile(ps.pwd));
}
private File toFile(FilePath f) {
@@ -716,10 +780,14 @@ public abstract class Launcher {
public Proc launch(ProcStarter ps) throws IOException {
final OutputStream out = ps.stdout == null ? null : new RemoteOutputStream(new CloseProofOutputStream(ps.stdout));
final OutputStream err = ps.stderr==null ? null : new RemoteOutputStream(new CloseProofOutputStream(ps.stderr));
- final InputStream in = ps.stdin==null ? null : new RemoteInputStream(ps.stdin);
+ final InputStream in = (ps.stdin==null || ps.stdin==NULL_INPUT_STREAM) ? null : new RemoteInputStream(ps.stdin,false);
final String workDir = ps.pwd==null ? null : ps.pwd.getRemote();
- return new RemoteProc(getChannel().callAsync(new RemoteLaunchCallable(ps.commands, ps.masks, ps.envs, in, out, err, workDir, listener)));
+ try {
+ return new ProcImpl(getChannel().call(new RemoteLaunchCallable(ps.commands, ps.masks, ps.envs, in, ps.reverseStdin, out, ps.reverseStdout, err, ps.reverseStderr, workDir, listener)));
+ } catch (InterruptedException e) {
+ throw (IOException)new InterruptedIOException().initCause(e);
+ }
}
public Channel launchChannel(String[] cmd, OutputStream err, FilePath _workDir, Map envOverrides) throws IOException, InterruptedException {
@@ -762,9 +830,64 @@ public abstract class Launcher {
private static final long serialVersionUID = 1L;
}
+
+ public static final class ProcImpl extends Proc {
+ private final RemoteProcess process;
+ private final IOTriplet io;
+
+ public ProcImpl(RemoteProcess process) {
+ this.process = process;
+ this.io = process.getIOtriplet();
+ }
+
+ @Override
+ public void kill() throws IOException, InterruptedException {
+ process.kill();
+ }
+
+ @Override
+ public int join() throws IOException, InterruptedException {
+ return process.join();
+ }
+
+ @Override
+ public boolean isAlive() throws IOException, InterruptedException {
+ return process.isAlive();
+ }
+
+ @Override
+ public InputStream getStdout() {
+ return io.stdout;
+ }
+
+ @Override
+ public InputStream getStderr() {
+ return io.stderr;
+ }
+
+ @Override
+ public OutputStream getStdin() {
+ return io.stdin;
+ }
+ }
}
- private static class RemoteLaunchCallable implements Callable {
+ public static class IOTriplet implements Serializable {
+ InputStream stdout,stderr;
+ OutputStream stdin;
+ private static final long serialVersionUID = 1L;
+ }
+ /**
+ * Remoting interface of a remote process
+ */
+ public static interface RemoteProcess {
+ int join() throws InterruptedException, IOException;
+ void kill() throws IOException, InterruptedException;
+ boolean isAlive() throws IOException, InterruptedException;
+ IOTriplet getIOtriplet();
+ }
+
+ private static class RemoteLaunchCallable implements Callable {
private final List cmd;
private final boolean[] masks;
private final String[] env;
@@ -773,8 +896,9 @@ public abstract class Launcher {
private final OutputStream err;
private final String workDir;
private final TaskListener listener;
+ private final boolean reverseStdin, reverseStdout, reverseStderr;
- RemoteLaunchCallable(List cmd, boolean[] masks, String[] env, InputStream in, OutputStream out, OutputStream err, String workDir, TaskListener listener) {
+ RemoteLaunchCallable(List cmd, boolean[] masks, String[] env, InputStream in, boolean reverseStdin, OutputStream out, boolean reverseStdout, OutputStream err, boolean reverseStderr, String workDir, TaskListener listener) {
this.cmd = new ArrayList(cmd);
this.masks = masks;
this.env = env;
@@ -783,19 +907,51 @@ public abstract class Launcher {
this.err = err;
this.workDir = workDir;
this.listener = listener;
+ this.reverseStdin = reverseStdin;
+ this.reverseStdout = reverseStdout;
+ this.reverseStderr = reverseStderr;
}
- public Integer call() throws IOException {
+ public RemoteProcess call() throws IOException {
Launcher.ProcStarter ps = new LocalLauncher(listener).launch();
ps.cmds(cmd).masks(masks).envs(env).stdin(in).stdout(out).stderr(err);
if(workDir!=null) ps.pwd(workDir);
+ if (reverseStdin) ps.writeStdin();
+ if (reverseStdout) ps.readStdout();
+ if (reverseStderr) ps.readStderr();
- Proc p = ps.start();
- try {
- return p.join();
- } catch (InterruptedException e) {
- return -1;
- }
+ final Proc p = ps.start();
+
+ return Channel.current().export(RemoteProcess.class,new RemoteProcess() {
+ public int join() throws InterruptedException, IOException {
+ try {
+ return p.join();
+ } finally {
+ // make sure I/O is delivered to the remote before we return
+ try {
+ Channel.current().syncIO();
+ } catch (Throwable _) {
+ // this includes a failure to sync, slave.jar too old, etc
+ }
+ }
+ }
+
+ public void kill() throws IOException, InterruptedException {
+ p.kill();
+ }
+
+ public boolean isAlive() throws IOException, InterruptedException {
+ return p.isAlive();
+ }
+
+ public IOTriplet getIOtriplet() {
+ IOTriplet r = new IOTriplet();
+ if (reverseStdout) r.stdout = new RemoteInputStream(p.getStdout());
+ if (reverseStderr) r.stderr = new RemoteInputStream(p.getStderr());
+ if (reverseStdin) r.stdin = new RemoteOutputStream(p.getStdin());
+ return r;
+ }
+ });
}
private static final long serialVersionUID = 1L;
@@ -860,11 +1016,13 @@ public abstract class Launcher {
m.override(o.getKey(),m.expand(o.getValue()));
return m;
}
-
+
/**
* Debug option to display full current path instead of just the last token.
*/
public static boolean showFullPath = false;
+ private static final NullInputStream NULL_INPUT_STREAM = new NullInputStream(0);
+
private static final Logger LOGGER = Logger.getLogger(Launcher.class.getName());
}
diff --git a/core/src/main/java/hudson/LauncherDecorator.java b/core/src/main/java/hudson/LauncherDecorator.java
index becd81a3ec3a0eb2c7858df96aa84bc40e72d50d..0abfca30e2a9eb7da98fccab621ada8e5f5d2244 100644
--- a/core/src/main/java/hudson/LauncherDecorator.java
+++ b/core/src/main/java/hudson/LauncherDecorator.java
@@ -1,6 +1,6 @@
package hudson;
-import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import hudson.model.Node;
import hudson.model.Executor;
import hudson.tasks.BuildWrapper;
@@ -45,6 +45,6 @@ public abstract class LauncherDecorator implements ExtensionPoint {
* Returns all the registered {@link LauncherDecorator}s.
*/
public static ExtensionList all() {
- return Hudson.getInstance().getExtensionList(LauncherDecorator.class);
+ return Jenkins.getInstance().getExtensionList(LauncherDecorator.class);
}
}
diff --git a/core/src/main/java/hudson/LocalPluginManager.java b/core/src/main/java/hudson/LocalPluginManager.java
index 74ecbb4ac493e1c97b995d973a4f6cd59cd004b7..184ce191c5e01299d3054c238be9026b67c3ca9d 100644
--- a/core/src/main/java/hudson/LocalPluginManager.java
+++ b/core/src/main/java/hudson/LocalPluginManager.java
@@ -24,8 +24,9 @@
package hudson;
-import hudson.model.Hudson;
+import jenkins.model.Jenkins;
+import javax.servlet.ServletContext;
import java.io.File;
import java.io.IOException;
import java.net.URL;
@@ -42,10 +43,12 @@ import java.util.logging.Logger;
* @author Kohsuke Kawaguchi
*/
public class LocalPluginManager extends PluginManager {
- private final Hudson hudson;
- public LocalPluginManager(Hudson hudson) {
- super(hudson.servletContext, new File(hudson.getRootDir(),"plugins"));
- this.hudson = hudson;
+ public LocalPluginManager(Jenkins jenkins) {
+ super(jenkins.servletContext, new File(jenkins.getRootDir(),"plugins"));
+ }
+
+ public LocalPluginManager(File rootDir) {
+ super(null, new File(rootDir,"plugins"));
}
/**
@@ -63,7 +66,9 @@ public class LocalPluginManager extends PluginManager {
Set names = new HashSet();
- for( String path : Util.fixNull((Set)hudson.servletContext.getResourcePaths("/WEB-INF/plugins"))) {
+ ServletContext context = Jenkins.getInstance().servletContext;
+
+ for( String path : Util.fixNull((Set)context.getResourcePaths("/WEB-INF/plugins"))) {
String fileName = path.substring(path.lastIndexOf('/')+1);
if(fileName.length()==0) {
// see http://www.nabble.com/404-Not-Found-error-when-clicking-on-help-td24508544.html
@@ -73,7 +78,7 @@ public class LocalPluginManager extends PluginManager {
try {
names.add(fileName);
- URL url = hudson.servletContext.getResource(path);
+ URL url = context.getResource(path);
copyBundledPlugin(url, fileName);
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Failed to extract the bundled plugin "+fileName,e);
diff --git a/core/src/main/java/hudson/Main.java b/core/src/main/java/hudson/Main.java
index 89ad4b9076abb253b36e433b94a2f610a70197d3..45067f932baf92499c5e3b1018fe381c19e8326a 100644
--- a/core/src/main/java/hudson/Main.java
+++ b/core/src/main/java/hudson/Main.java
@@ -26,13 +26,12 @@ package hudson;
import hudson.util.DualOutputStream;
import hudson.util.EncodingStream;
import com.thoughtworks.xstream.core.util.Base64Encoder;
+import hudson.util.IOUtils;
-import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.HttpRetryException;
@@ -64,7 +63,7 @@ public class Main {
public static int run(String[] args) throws Exception {
String home = getHudsonHome();
if (home==null) {
- System.err.println("HUDSON_HOME is not set.");
+ System.err.println("JENKINS_HOME is not set.");
return -1;
}
if (args.length < 2) {
@@ -76,6 +75,8 @@ public class Main {
}
private static String getHudsonHome() {
+ String home = EnvVars.masterEnvVars.get("JENKINS_HOME");
+ if (home!=null) return home;
return EnvVars.masterEnvVars.get("HUDSON_HOME");
}
@@ -121,9 +122,7 @@ public class Main {
HttpURLConnection con = open(new URL(home +
"crumbIssuer/api/xml?xpath=concat(//crumbRequestField,\":\",//crumb)'"));
if (auth != null) con.setRequestProperty("Authorization", auth);
- BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
- String line = in.readLine();
- in.close();
+ String line = IOUtils.readFirstLine(con.getInputStream(),"UTF-8");
String[] components = line.split(":");
if (components.length == 2) {
crumbField = components[0];
diff --git a/core/src/main/java/hudson/MarkupText.java b/core/src/main/java/hudson/MarkupText.java
index 03ef1fcb02f18f6bc44ee1ec11993cdaf0cd9171..1274ccb8a270e70837debfc80a6efe844f81c1ec 100644
--- a/core/src/main/java/hudson/MarkupText.java
+++ b/core/src/main/java/hudson/MarkupText.java
@@ -193,7 +193,7 @@ public class MarkupText extends AbstractMarkupText {
* Replaces the group tokens like "$0", "$1", and etc with their actual matches.
*/
public String replace(String s) {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder(s.length() + 10);
for( int i=0; i= 10.6
+ */
+ public static boolean isSnowLeopardOrLater() {
+ try {
+ return isDarwin() && new VersionNumber(System.getProperty("os.version")).compareTo(new VersionNumber("10.6"))>=0;
+ } catch (IllegalArgumentException e) {
+ // failed to parse the version
+ return false;
+ }
+ }
}
diff --git a/core/src/main/java/hudson/Plugin.java b/core/src/main/java/hudson/Plugin.java
index 4652901e95b4f23c9300b067f4e180ac6bc50d1f..bd3223fbbf75c3a1e4399b3ebec697674e29f68b 100644
--- a/core/src/main/java/hudson/Plugin.java
+++ b/core/src/main/java/hudson/Plugin.java
@@ -23,7 +23,7 @@
*/
package hudson;
-import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import hudson.model.Descriptor;
import hudson.model.Saveable;
import hudson.model.listeners.ItemListener;
@@ -74,7 +74,7 @@ import com.thoughtworks.xstream.XStream;
public abstract class Plugin implements Saveable {
/**
- * Set by the {@link PluginManager}.
+ * Set by the {@link PluginManager}, before the {@link #start()} method is called.
* This points to the {@link PluginWrapper} that wraps
* this {@link Plugin} object.
*/
@@ -95,12 +95,21 @@ public abstract class Plugin implements Saveable {
public void setServletContext(ServletContext context) {
}
+ /**
+ * Gets the paired {@link PluginWrapper}.
+ *
+ * @since 1.426
+ */
+ public PluginWrapper getWrapper() {
+ return wrapper;
+ }
+
/**
* Called to allow plugins to initialize themselves.
*
*
* This method is called after {@link #setServletContext(ServletContext)} is invoked.
- * You can also use {@link Hudson#getInstance()} to access the singleton hudson instance,
+ * You can also use {@link jenkins.model.Jenkins#getInstance()} to access the singleton hudson instance,
* although the plugin start up happens relatively early in the initialization
* stage and not all the data are loaded in Hudson.
*
@@ -227,8 +236,9 @@ public abstract class Plugin implements Saveable {
*/
public void save() throws IOException {
if(BulkChange.contains(this)) return;
- getConfigXml().write(this);
- SaveableListener.fireOnChange(this, getConfigXml());
+ XmlFile config = getConfigXml();
+ config.write(this);
+ SaveableListener.fireOnChange(this, config);
}
/**
@@ -241,8 +251,8 @@ public abstract class Plugin implements Saveable {
* @since 1.245
*/
protected XmlFile getConfigXml() {
- return new XmlFile(Hudson.XSTREAM,
- new File(Hudson.getInstance().getRootDir(),wrapper.getShortName()+".xml"));
+ return new XmlFile(Jenkins.XSTREAM,
+ new File(Jenkins.getInstance().getRootDir(),wrapper.getShortName()+".xml"));
}
diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java
index e1a459c41ee58dd3175055356e3d288f35d750e1..f9879e4e18bfeb2d1cb232ab229deefadcb497cd 100644
--- a/core/src/main/java/hudson/PluginManager.java
+++ b/core/src/main/java/hudson/PluginManager.java
@@ -33,17 +33,19 @@ import hudson.init.InitStrategy;
import hudson.init.InitializerFinder;
import hudson.model.AbstractModelObject;
import hudson.model.Failure;
-import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import hudson.model.UpdateCenter;
import hudson.model.UpdateSite;
import hudson.util.CyclicGraphDetector;
import hudson.util.CyclicGraphDetector.CycleDetectedException;
+import hudson.util.FormValidation;
import hudson.util.PersistedList;
import hudson.util.Service;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
import org.apache.commons.logging.LogFactory;
import org.jvnet.hudson.reactor.Executable;
import org.jvnet.hudson.reactor.Reactor;
@@ -69,11 +71,13 @@ import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
+import java.util.ListIterator;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -91,7 +95,7 @@ public abstract class PluginManager extends AbstractModelObject {
/**
* All active plugins.
*/
- protected final List activePlugins = new ArrayList();
+ protected final List activePlugins = new CopyOnWriteArrayList();
protected final List failedPlugins = new ArrayList();
@@ -102,7 +106,7 @@ public abstract class PluginManager extends AbstractModelObject {
/**
* @deprecated as of 1.355
- * {@link PluginManager} can now live longer than {@link Hudson} instance, so
+ * {@link PluginManager} can now live longer than {@link jenkins.model.Jenkins} instance, so
* use {@code Hudson.getInstance().servletContext} instead.
*/
public final ServletContext context;
@@ -149,7 +153,7 @@ public abstract class PluginManager extends AbstractModelObject {
/**
* Called immediately after the construction.
* This is a separate method so that code executed from here will see a valid value in
- * {@link Hudson#pluginManager}.
+ * {@link jenkins.model.Jenkins#pluginManager}.
*/
public TaskBuilder initTasks(final InitStrategy initStrategy) {
TaskBuilder builder;
@@ -187,8 +191,6 @@ public abstract class PluginManager extends AbstractModelObject {
p.isBundled = bundledPlugins.contains(arc.getName());
plugins.add(p);
- if(p.isActive())
- activePlugins.add(p);
} catch (IOException e) {
failedPlugins.add(new FailedPlugin(arc.getName(),e));
throw e;
@@ -212,34 +214,43 @@ public abstract class PluginManager extends AbstractModelObject {
});
}
- g.requires(PLUGINS_PREPARED).add("Checking cyclic dependencies",new Executable() {
+ g.followedBy().attains(PLUGINS_LISTED).add("Checking cyclic dependencies", new Executable() {
/**
* Makes sure there's no cycle in dependencies.
*/
public void run(Reactor reactor) throws Exception {
try {
- new CyclicGraphDetector() {
+ CyclicGraphDetector cgd = new CyclicGraphDetector() {
@Override
protected List getEdges(PluginWrapper p) {
List next = new ArrayList();
- addTo(p.getDependencies(),next);
- addTo(p.getOptionalDependencies(),next);
+ addTo(p.getDependencies(), next);
+ addTo(p.getOptionalDependencies(), next);
return next;
}
private void addTo(List dependencies, List r) {
for (Dependency d : dependencies) {
PluginWrapper p = getPlugin(d.shortName);
- if (p!=null)
+ if (p != null)
r.add(p);
}
}
- }.run(getPlugins());
+ };
+ cgd.run(getPlugins());
+
+ // obtain topologically sorted list and overwrite the list
+ ListIterator litr = plugins.listIterator();
+ for (PluginWrapper p : cgd.getSorted()) {
+ litr.next();
+ litr.set(p);
+ if(p.isActive())
+ activePlugins.add(p);
+ }
} catch (CycleDetectedException e) {
stop(); // disable all plugins since classloading from them can lead to StackOverflow
throw e; // let Hudson fail
}
- Collections.sort(plugins);
}
});
@@ -264,7 +275,7 @@ public abstract class PluginManager extends AbstractModelObject {
* Once the plugins are listed, schedule their initialization.
*/
public void run(Reactor session) throws Exception {
- Hudson.getInstance().lookup.set(PluginInstanceStore.class,new PluginInstanceStore());
+ Jenkins.getInstance().lookup.set(PluginInstanceStore.class,new PluginInstanceStore());
TaskGraphBuilder g = new TaskGraphBuilder();
Hudson.getInstance().container = Guice.createInjector();
@@ -351,7 +362,7 @@ public abstract class PluginManager extends AbstractModelObject {
/**
* Creates a hudson.PluginStrategy, looking at the corresponding system property.
*/
- private PluginStrategy createPluginStrategy() {
+ protected PluginStrategy createPluginStrategy() {
String strategyName = System.getProperty(PluginStrategy.class.getName());
if (strategyName != null) {
try {
@@ -458,6 +469,20 @@ public abstract class PluginManager extends AbstractModelObject {
return result;
}
+ /**
+ * Return the {@link PluginWrapper} that loaded the given class 'c'.
+ *
+ * @since 1.402.
+ */
+ public PluginWrapper whichPlugin(Class c) {
+ ClassLoader cl = c.getClassLoader();
+ for (PluginWrapper p : activePlugins) {
+ if (p.classLoader==cl)
+ return p;
+ }
+ return null;
+ }
+
/**
* Orderly terminates all the plugins.
*/
@@ -473,10 +498,10 @@ public abstract class PluginManager extends AbstractModelObject {
}
public HttpResponse doUpdateSources(StaplerRequest req) throws IOException {
- Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
+ Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
if (req.hasParameter("remove")) {
- UpdateCenter uc = Hudson.getInstance().getUpdateCenter();
+ UpdateCenter uc = Jenkins.getInstance().getUpdateCenter();
BulkChange bc = new BulkChange(uc);
try {
for (String id : req.getParameterValues("sources"))
@@ -502,7 +527,7 @@ public abstract class PluginManager extends AbstractModelObject {
n = n.substring(7);
if (n.indexOf(".") > 0) {
String[] pluginInfo = n.split("\\.");
- UpdateSite.Plugin p = Hudson.getInstance().getUpdateCenter().getById(pluginInfo[1]).getPlugin(pluginInfo[0]);
+ UpdateSite.Plugin p = Jenkins.getInstance().getUpdateCenter().getById(pluginInfo[1]).getPlugin(pluginInfo[0]);
if(p==null)
throw new Failure("No such plugin: "+n);
p.deploy();
@@ -517,8 +542,8 @@ public abstract class PluginManager extends AbstractModelObject {
* Bare-minimum configuration mechanism to change the update center.
*/
public HttpResponse doSiteConfigure(@QueryParameter String site) throws IOException {
- Hudson hudson = Hudson.getInstance();
- hudson.checkPermission(Hudson.ADMINISTER);
+ Jenkins hudson = Jenkins.getInstance();
+ hudson.checkPermission(Jenkins.ADMINISTER);
UpdateCenter uc = hudson.getUpdateCenter();
PersistedList sites = uc.getSites();
for (UpdateSite s : sites) {
@@ -536,30 +561,46 @@ public abstract class PluginManager extends AbstractModelObject {
@QueryParameter("proxy.port") String port,
@QueryParameter("proxy.userName") String userName,
@QueryParameter("proxy.password") String password) throws IOException {
- Hudson hudson = Hudson.getInstance();
- hudson.checkPermission(Hudson.ADMINISTER);
+ Jenkins hudson = Jenkins.getInstance();
+ hudson.checkPermission(Jenkins.ADMINISTER);
server = Util.fixEmptyAndTrim(server);
if(server==null) {
hudson.proxy = null;
ProxyConfiguration.getXmlFile().delete();
} else try {
- hudson.proxy = new ProxyConfiguration(server,Integer.parseInt(Util.fixNull(port)),
+ int proxyPort = Integer.parseInt(Util.fixNull(port));
+ if (proxyPort < 0 || proxyPort > 65535) {
+ throw new Failure(Messages.PluginManager_PortNotInRange(0, 65535));
+ }
+ hudson.proxy = new ProxyConfiguration(server, proxyPort,
Util.fixEmptyAndTrim(userName),Util.fixEmptyAndTrim(password));
hudson.proxy.save();
} catch (NumberFormatException nfe) {
- return HttpResponses.error(StaplerResponse.SC_BAD_REQUEST,
- new IllegalArgumentException("Invalid proxy port: " + port, nfe));
+ throw new Failure(Messages.PluginManager_PortNotANumber());
}
return new HttpRedirect("advanced");
}
+
+ public FormValidation doCheckProxyPort(@QueryParameter String value) {
+ int port;
+ try {
+ port = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return FormValidation.error(Messages.PluginManager_PortNotANumber());
+ }
+ if (port < 0 || port > 65535) {
+ return FormValidation.error(Messages.PluginManager_PortNotInRange(0, 65535));
+ }
+ return FormValidation.ok();
+ }
/**
* Uploads a plugin.
*/
public HttpResponse doUploadPlugin(StaplerRequest req) throws IOException, ServletException {
try {
- Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
+ Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
@@ -573,6 +614,10 @@ public abstract class PluginManager extends AbstractModelObject {
fileItem.write(new File(rootDir, fileName));
fileItem.delete();
+ PluginWrapper existing = getPlugin(FilenameUtils.getBaseName(fileName));
+ if (existing!=null && existing.isBundled)
+ existing.doPin();
+
pluginUploaded = true;
return new HttpRedirect(".");
@@ -610,16 +655,6 @@ public abstract class PluginManager extends AbstractModelObject {
else generatedClasses.remove(name,wc);
}
- // first, use the context classloader so that plugins that are loading
- // can use its own classloader first.
- ClassLoader cl = Thread.currentThread().getContextClassLoader();
- if(cl!=null && cl!=this)
- try {
- return cl.loadClass(name);
- } catch(ClassNotFoundException e) {
- // not found. try next
- }
-
for (PluginWrapper p : activePlugins) {
try {
return p.classLoader.loadClass(name);
@@ -649,6 +684,13 @@ public abstract class PluginManager extends AbstractModelObject {
}
return Collections.enumeration(resources);
}
+
+ @Override
+ public String toString()
+ {
+ // only for debugging purpose
+ return "classLoader " + getClass().getName();
+ }
}
private static final Logger LOGGER = Logger.getLogger(PluginManager.class.getName());
diff --git a/core/src/main/java/hudson/PluginStrategy.java b/core/src/main/java/hudson/PluginStrategy.java
index 0fe0e7233b53baf3b73df74d0e00226923f64745..b8bd1ed2f2f875a088071c3d21412bc2e8bcda9b 100644
--- a/core/src/main/java/hudson/PluginStrategy.java
+++ b/core/src/main/java/hudson/PluginStrategy.java
@@ -23,8 +23,12 @@
*/
package hudson;
+import hudson.model.Hudson;
+import jenkins.model.Jenkins;
+
import java.io.File;
import java.io.IOException;
+import java.util.List;
/**
* Pluggability point for how to create {@link PluginWrapper}.
@@ -63,4 +67,14 @@ public interface PluginStrategy extends ExtensionPoint {
*/
public abstract void initializeComponents(PluginWrapper plugin);
-}
\ No newline at end of file
+ /**
+ * Find components of the given type using the assigned strategy.
+ *
+ *
+ * @param type The component type
+ * @param hudson The Hudson scope
+ * @return Sequence of components
+ * @since 1.400
+ */
+ public abstract List> findComponents(Class type, Hudson hudson);
+}
diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java
index a1090dea80218c03ab96b4f16ed14ff924cce7a7..9db9bbeb5ddd4692a709e926d0615e65f91d7d9d 100644
--- a/core/src/main/java/hudson/PluginWrapper.java
+++ b/core/src/main/java/hudson/PluginWrapper.java
@@ -25,7 +25,7 @@
package hudson;
import hudson.PluginManager.PluginInstanceStore;
-import hudson.model.Hudson;
+import jenkins.model.Jenkins;
import hudson.model.UpdateCenter;
import hudson.model.UpdateSite;
import hudson.util.VersionNumber;
@@ -50,8 +50,8 @@ import java.util.Enumeration;
import java.util.jar.JarFile;
/**
- * Represents a Hudson plug-in and associated control information
- * for Hudson to control {@link Plugin}.
+ * Represents a Jenkins plug-in and associated control information
+ * for Jenkins to control {@link Plugin}.
*
*
* A plug-in is packaged into a jar file whose extension is ".hpi",
@@ -60,9 +60,9 @@ import java.util.jar.JarFile;
*
* At the runtime, a plugin has two distinct state axis.
*
- *
Enabled/Disabled. If enabled, Hudson is going to use it
- * next time Hudson runs. Otherwise the next run will ignore it.
- *
Activated/Deactivated. If activated, that means Hudson is using
+ *
Enabled/Disabled. If enabled, Jenkins is going to use it
+ * next time Jenkins runs. Otherwise the next run will ignore it.
+ *
Activated/Deactivated. If activated, that means Jenkins is using
* the plugin in this session. Otherwise it's not.
*
*
@@ -92,7 +92,7 @@ public class PluginWrapper implements Comparable {
/**
* Base URL for loading static resources from this plugin.
* Null if disabled. The static resources are mapped under
- * hudson/plugin/SHORTNAME/.
+ * CONTEXTPATH/plugin/SHORTNAME/.
*/
public final URL baseResourceURL;
@@ -104,7 +104,7 @@ public class PluginWrapper implements Comparable {
/**
* Used to control the unpacking of the bundled plugin.
- * If a pin file exists, Hudson assumes that the user wants to pin down a particular version
+ * If a pin file exists, Jenkins assumes that the user wants to pin down a particular version
* of a plugin, and will not try to overwrite it. Otherwise, it'll be overwritten
* by a bundled copy, to ensure consistency across upgrade/downgrade.
* @since 1.325
@@ -113,7 +113,7 @@ public class PluginWrapper implements Comparable {
/**
* Short name of the plugin. The artifact Id of the plugin.
- * This is also used in the URL within Hudson, so it needs
+ * This is also used in the URL within Jenkins, so it needs
* to remain stable even when the *.hpi file name is changed
* (like Maven does.)
*/
@@ -129,7 +129,7 @@ public class PluginWrapper implements Comparable {
private final List optionalDependencies;
/**
- * Is this plugin bundled in hudson.war?
+ * Is this plugin bundled in jenkins.war?
*/
/*package*/ boolean isBundled;
@@ -255,7 +255,7 @@ public class PluginWrapper implements Comparable {
* Gets the instance of {@link Plugin} contributed by this plugin.
*/
public Plugin getPlugin() {
- return Hudson.lookup(PluginInstanceStore.class).store.get(this);
+ return Jenkins.lookup(PluginInstanceStore.class).store.get(this);
}
/**
@@ -349,7 +349,7 @@ public class PluginWrapper implements Comparable {
}
/**
- * Enables this plugin next time Hudson runs.
+ * Enables this plugin next time Jenkins runs.
*/
public void enable() throws IOException {
if(!disableFile.delete())
@@ -357,7 +357,7 @@ public class PluginWrapper implements Comparable {
}
/**
- * Disables this plugin next time Hudson runs.
+ * Disables this plugin next time Jenkins runs.
*/
public void disable() throws IOException {
// creates an empty file
@@ -378,7 +378,7 @@ public class PluginWrapper implements Comparable {
/**
* If true, the plugin is going to be activated next time
- * Hudson runs.
+ * Jenkins runs.
*/
public boolean isEnabled() {
return !disableFile.exists();
@@ -389,7 +389,7 @@ public class PluginWrapper implements Comparable {
}
public void setPlugin(Plugin plugin) {
- Hudson.lookup(PluginInstanceStore.class).store.put(this,plugin);
+ Jenkins.lookup(PluginInstanceStore.class).store.put(this,plugin);
plugin.wrapper = this;
}
@@ -397,6 +397,15 @@ public class PluginWrapper implements Comparable {
return manifest.getMainAttributes().getValue("Plugin-Class");
}
+ public boolean hasLicensesXml() {
+ try {
+ new URL(baseResourceURL,"WEB-INF/licenses.xml").openStream().close();
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
/**
* Makes sure that all the dependencies exist, and then accept optional dependencies
* as real dependencies.
@@ -430,7 +439,7 @@ public class PluginWrapper implements Comparable {
* the user may have installed a plugin locally developed.
*/
public UpdateSite.Plugin getUpdateInfo() {
- UpdateCenter uc = Hudson.getInstance().getUpdateCenter();
+ UpdateCenter uc = Jenkins.getInstance().getUpdateCenter();
UpdateSite.Plugin p = uc.getPlugin(getShortName());
if(p!=null && p.isNewerThan(getVersion())) return p;
return null;
@@ -440,7 +449,7 @@ public class PluginWrapper implements Comparable