- Working around a GZip compression bug in jzlib affecting transfer of certain large, repetitive artifacts.
- (issue 19473)
-
- Lazy-loading bug: builds go missing.
- (issue 19418)
- (re)create build number->id symlink if missing when updating permalink.
- (issue 19034)
+ IllegalArgumentException from AbstractProject.getEnvironment when trying to get environment variables from an offline slave.
+ (issue 23517)
+
+ Overall.READ is sufficient to access /administrativeMonitor/hudsonHomeIsFull/
+ (SECURITY-134)
+
+ Master computer is not notified using ComputerListener
+ (issue 23481)
+
- Display the full display name in title for jobs and views.
- (pull request 884)
-
- Added a new extension point to control where archived artifacts get stored.
- (issue 17236)
+ Add CLI commands to add jobs to and remove jobs from views (add-job-to-view, remove-job-from-view).
+ (issue 23361)
- Use fine-grained permissions for node manipulation via REST API & CLI
- (issue 18485)
-
- Make the link to the aggregated test result from the project page work.
- (issue 9637)
+ UI improvements / refreshing.
+ (issue 23492)
+
+ Failed to correctly resave a project configuration containing both a forward and a reverse build trigger.
+ (issue 23191)
+
+ Long log output resulted in missing Console link in popup.
+ (issue 14264)
+
+ HTTP error 405 when trying to restart ssh host.
+ (issue 23094)
+
+ Move 'None' Source Code Management option to top position.
+ (issue 23434)
+
+ Fixed NullPointerException when ArctifactArchiver is called for a build with the undefined status.
+ (issue 23526)
+
+ Allow disabling use of default exclude patterns in ArctifactArchiver (.git, .svn, etc.).
+ (issue 20086)
+
+ Fixed NullPointerException when "properties" element is missing in a job's configuration submission by JSON
+ (issue 23437)
+ Jenkins can now kill Win32 processes from Win64 JVMs.
+ (issue 23410)
+
+ Allow custom security realm plugins to fire events to SecurityListeners.
+ (issue 23417)
- Deleting an external run did not immediately remove it from build list, leading to errors from log rotation.
- (issue 19377)
+ Recover gracefully if a build permalink has a non-numeric value.
+ (issue 21631)
- When copying a directory from master to slave fails due to an error on the slave, properly report it.
- (issue 9540)
+ Fix form submission via the Enter key for Internet Explorer version 9.
+ (issue 22373)
+
+ When Jenkins had a lot of jobs, submitting a view configuration change could overload the web server, even if few of the jobs were selected.
+ (issue 20327)
+
- Identify user agent for Internet Explorer 11.
- (issue 19171)
+ Fixed JNLP connection handling problem
+ (issue 22932)
- Since 1.518, fingerprint serialization broke when job or file names contained XML special characters like ampersands.
- (issue 18337)
+ Fixed NullPointerException caused by the uninitialized ProcessStarter environment in build wrappers
+ (issue 20559)
+
+ Support the range notation for pagination in API
+ (issue 23228)
- Robustness against truncated fingerprint files.
- (issue 19515)
+ Incorrect redirect after deleting a folder.
+ (issue 23375)
+
+ Incorrect links from Build History page inside a folder.
+ (issue 19310)
- JavaScript error in the checkUrl computation shouldn't break the job configuration page.
- (issue 19457)
+ API changes allowing new job types to use SCM plugins.
+ (issue 23365)
- Annotate the Advanced section if some fields are already customized.
- (issue 3107)
-
- No events fired when project is enable/disable or the description is changed
- (issue 17108)
+ API changes allowing to create nested launchers (DecoratedLauncher)
+ (issue 19454)
+ Fixed a reference counting bug in the remoting layer.
+ (commit)
+
+ Avoid repeatedly reading symlinks from disk to resolve build permalinks.
+ (issue 22822)
+
+ Show custom build display name in executors widget.
+ (issue 10477)
+
+ CodeMirror support for shell steps broke initial configuration.
+ (issue 23151)
+
+ Jenkins on Linux can not restart after plugin update when started without full path to java executable
+ (issue 22818)
+
+ Fixed NullPointerException when a build triggering returns null cause
+ (issue 20499)
+
+ Fixed NullPointerException on plugin installations when invalid update center is set
+ (issue 20031)
+
+ Use DISABLED_ANIME icon while building a disabled project
+ (issue 8358)
+
+ Process the items hierarchy when displaying the Show Poll Thread Count option
+ (issue 22934)
+
+ Compressed output was turned on even before Access Denied errors were shown for disallowed Remote API requests, yielding a confusing error.
+ (issue 17374)
+ (issue 18116)
+
+ Properly close input streams in FileParameterValue
+ (issue 22693)
+
+ Incorrect failure age in the JUnit test results
+ (issue 18626)
+
- Send Maven agent JARs to slaves on demand, not unconditionally upon connection.
- (issue 16261)
-
- Occasional race condition during startup.
- (issue 18775)
-
- Robustness against startup error for users of Global Build Stats plugin.
- (issue 17248)
-
- 404s from Javadoc and HTML Publisher plugins.
- (issue 19168)
+ Prevent up to two-minute delay before scheduling jobs from a cron trigger.
+ (pull request 1216)
+
+ Occasional attempts to delete a build during log rotation which had already been deleted.
+ (issue 22395)
+
+ Again show proper display names for build parameters.
+ (issue 22755)
+
- Build number symlinks and permalinks not updated for Maven module builds.
- (issue 18846)
+ Next build link was not reliably available from a previous build after starting a new one.
+ (issue 20662)
+
+ Debian postinst: check for present user/group before adding them.
+ (issue 22715)
+
+ Add distance between time tick labels on load statistics.
+ (issue 22686)
+
+ Correctly show load statistics for master node.
+ (issue 22689)
+
+ Make load statistics graph font configurable, use sans serif font by default.
+ (issue 22688)
+
+ Add links to nodes on thread dump page for easier navigation.
+ (issue 22672)
+ Fixed a corner case handling of tool installation.
+ (issue 16846)
- With Apache Maven 3.1 build, logging configuration from the Apache Maven distribution is not used.
+ Enabled log rotation on the OSX package
+ (issue 15178)
+
+ When measuring the length of the queue, jobs that consist of multiple subtasks should
+ count as more than 1.
+ (pull request 742)
- Avoid log duplication with Apache Maven 3.1 builds
+ Close drop-down button menu when clicking outside
+ (issue 17050)
- Ungraceful handling of empty matrix project axes.
- (issue 19135)
+ RunParameter with filtering enabled incorrectly includes builds which have not yet completed
+ (issue 20974)
+ Fixed a JavaScript problem in sortable table with IE8.
+ (issue 21729)
+
+ More efficient deletion of old builds (specified by date).
+ (issue 22607)
- Command line now supports "--sessionTimeout" option for controlling session timeout
+ The matrix project type was moved into its own plugin.
- Form validation methods weren't getting triggered when one of its dependency controls change.
- (issue 19124)
+ Linkage errors in notifiers could leak workspace leases.
+ (issue 21622)
- When POST is required for some HTTP operation but GET was used, the response should have status code 405.
- (issue 16918)
+ Better correction of the anomalous condition that several builds of a job specify the same number.
+ (issue 22631)
- Correct help text of Label field in automatic installation of tools in global configuration.
- (issue 19091)
+ Under certain conditions, a running build could mistakenly be shown as completed (and failed), while still producing output.
+ (issue 22681)
+
+ Fix a bug which only showed the first detail part for radio buttons.
+ (issue 22583)
- Use Guice from Google rather than a fork
+ Update version of bundled Mailer plugin to 1.8 to avoid issues with older versions
+
+ Show larger load statistics graphs.
+ (issue 22674)
+
+ Linebreak project names less aggressively.
+ (issue 22670)
+
+ Added a new extension point for more pluggable JNLP slave handling
- Jenkins does not invoke ProcessKillers for Windows
- (issue 19156)
-
+ Don't ask for confirmation when it doesn't make any sense.
+ (issue 21720)
- Fixed NoSuchFieldError: triggers with older Maven plugin
- (issue 18677)
+ Jenkins asks for confirmation before leaving form even though user is not authorized to make changes.
+ (issue 20597)
- Added bytecode transformation driven compatibility ensurance mechanism
- (discussion)
+ Make the computers monitor status row look different from regular node rows.
+ (pull request 1095)
- Windows path separators not correctly escaped in Maven properties configuration.
- (issue 10539)
+ Properly close resources in case of exceptions.
+ (pull request 737)
- Improved EnvironmentContributor to support project-level insertion.
- (issue 19042)
+ Fix warning on JBoss AS7 due to unnecessary xpp3_min dependency.
+ (pull request 733)
+
+ Enforcing build trigger authentication at runtime by checking authentication associated with a build, rather than at configuration time.
+ For compatibility, enforcement is skipped by default; you must install the Authorize Project plugin or similar for this to take effect.
+ The “upstream pseudo trigger” was also removed in favor of a true trigger configured on the downstream project;
+ you may use either the post-build action or the trigger according to where you want this configuration stored, and who is authorized to change it.
+ (issue 16956)
- HudsonAuthenticationEntryPoint can break CLI support, because the port isn't exposed properly.
- (issue 18634)
+ Fixed NPE from view new job name autocompletion since 1.553.
+ (issue 22142)
+
+ Deadlocks in concurrent builds under some conditions since 1.556.
+ (issue 22560)
- Report an user friendly error page if a deletion of a build fails.
- (pull request 827)
+ JNLP slaves are now handled through NIO-based remoting channels for better scalability.
+
- MavenModuleSetBuild.getResult is expensive.
- (issue 18895)
+ Fixed NoSuchMethodException when destroying processes using JDK1.8.
+ (issue 21341)
+
+ Avoid irrelevant job queing while node is offline.
+ (issue 21394)
+
+ Debian package now creates 'jenkins' group
+
+ Suppress fingerprint link if fingerprint record isn't available.
+ (issue 21818)
- Revisited fix to be compatible for plugins.
- (issue 18119)
+ Job hangs if one of multiple triggered builds was aborted
+ (issue 21932)
- Ensuring /log/all shows only INFO and above messages, even if custom loggers display FINE or below.
- (issue 18959)
+ Don't submit form on Save after Apply in new window on some browsers.
+ (issue 20245)
+
+ Remotely triggered builds now show correct IP address through proxy.
+ (issue 18008)
+
- Added a new monitor that detects and fixse out-of-order builds records.
- (issue 18289)
+ Slaves connected via Java Web Start now restart themselves when a connection to Jenkins is lost.
+ (issue 19055)
+
+ Fixed NPE from Slave.createLauncher.
+ (issue 21999)
+
+ Faster rendering of views containing many items.
+ (issue 18364)
+
- Since 1.520, Jenkins requires Java 6 or later, breaking Maven builds set to use JDK 5. Now falls back to JVM of slave agent but sets compile/test flags to use defined JDK.
- (issue 18403)
+ NoSuchMethodError: hudson.model.BuildAuthorizationToken.checkPermission(…) from Build Token Root plugin since 1.556.
+ (issue 22382)
+
+ Allow a Trigger to be a DependencyDeclarer.
+ (issue 22397)
+
+ Fixed a slow down in resource loading caused by fix to JENKINS-18677.
+ (issue 21579)
+
+ jenkins.war file shouldn't be exploded into /tmp
+ (issue 22442)
+
- Since 1.517, Maven projects using Maven 2 could not build projects using extensions depending on Apache Commons Codec.
- (issue 18178)
+ JNLP slaves now satisfies stricter requirements imposed by JDK7u45.
+ (issue 20204)
- Test harness was packing copies of Maven into plugin archives under some conditions.
- (issue 18918)
+ Fixed NPE executing Pipe.EOF with ProxyWriter
+ (issue 20769)
+
- Provided maven settings.xml in maven builder is lost.
- (issue 15976)
+ Fixed ArrayIndexOutOfBoundsException in XStream with Oracle JDK8 release version
+ (issue 18537)
- Exception when running polling with a Maven installation not defined on master.
- (issue 18898)
+ Corrected permission checks for copy-job and create-job CLI commands.
+ (issue 22262)
- Since 1.477 GET on /view/…/config.xml included a spurious wrapper element.
- (issue 17302)
-
- Clearer display of log messages: chronological order, and coloration of repeated vs. fresh metadata (date, log level, log source).
+ identity.key, used to secure some communications with Jenkins, now stored encrypted with the master key.
- Fixed a regression that broke some plugins' form validation
- (issue 18776)
+ When dynamically loading a plugin which another loaded plugin already had an optional dependency on, class loading errors could result before restart.
+ (issue 19976)
- People View does Not Populate if JQuery plugin enabled.
- (issue 18641)
+ Memory leaks in the old data monitor.
+ (issue 19544)
+
+ Ability for custom view types to disable automatic refresh.
+ (issue 21190)
+ (issue 21191)
+
+ Option to download metadata directly from Jenkins rather than going through the browser.
+ (issue 19081)
+
+ Allow JDK8 (and other versions) to be downloaded by JDKInstaller correctly.
+ (issue 22347)
- Fixed another possible cause of an NPE from MatrixConfiguration.newBuild.
- (issue 17728)
+ Jenkins should recover gracefully from a failure to process "remember me" cookie
+ (issue 11643)
- NPE in MavenFingerprinter.getArtifactRepositoryMaven21.
- (issue 18441)
-
- More reliability improvement in remote slave reconnection.
+ Fixed Up link in matrix projects
+ (issue 21773)
- Fixed: claiming of tests doesn't work in Maven jobs (claim-plugin)
- (issue 14585)
+ Archiving of symlinks as artifacts did not work in some cases.
+ (issue 21958)
+
+ Slow rendering of directories with many entries in remote workspaces.
+ (issue 21780)
- Fixed a regression in the config form with some plugins
- (issue 18585)
+ Build history widget only showed the last day of builds.
+ (Due to JENKINS-20892, even with this fix at most 20 builds are shown.)
+ (issue 21159)
- Fixed a dead lock in the Project class and improved the signature of the persisted XML form a bit.
- (issue 18589)
+ Random class loading error mostly known to affect static analysis plugins.
+ (issue 12124)
- Improved memory efficiency in parsing test reports with large stdio output files.
- (issue 15382)
+ After restarting Jenkins, users known only from changelogs could be shown as First Last _first.last@some.org_, breaking mail delivery.
+ (issue 16332)
+
+ CLI build -s -v command caused 100% CPU usage on the master.
+ (issue 20965)
- Node monitoring now happens concurrently across all the slaves, so it'll be affected less by problematic slaves.
- (issue 18438)
+ Slave started from Java Web Start can now install itself as a systemd service.
+
+ Split the “raw HTML” markup formatter out of core into a bundled plugin.
- Deadlock during Maven builds Parsing POM step
- (issue 15846)
+ Do not show Maven modules and matrix configurations in the Copy Job dialog.
+ (issue 19559)
- If every node is restricted to tied jobs only, Matrix build jobs can never start.
+ Fix autocompletion for items in folders.
+ (pull request 1124)
- Build with parameters returns empty web page
- (issue 18425)
-
- Access denied error results in ERR_CONTENT_DECODING_FAILED on most browsers, masking the root cause.
- (issue 15437)
+ Fixed handling of default JENKINS_HOME when storing CLI credentials
+ (issue 21772)
- Fixed the master/slave handshake problem when a slave runs on non-ASCII compatible encoding (such as EBCDIC.)
+ Fixed broken action links on Label page
+ (issue 21778)
- Added a diagnosis for StreamCorruptedException problem
- (issue 8856)
+ Allow Actions to contribute to Labels' main page
+ (issue 21777)
+
+ Expensive symlink-related calls on Windows can be simplified.
+ (issue 20534)
- Matrix project's parent can be now tied to labels/slaves.
- (issue 7825)
+ Improve detection of broken reverse proxy setups.
+
+ Valentine's day security release that contains more than a dozen security fixes.
+ (security advisory)
+
+ Regression in Windows slaves since 1.547.
+ (issue 21373)
- Clean up fingerprint records that correspond to the deleted build recods
- (issue 18417)
+ Using java -jar jenkins-core.jar folder/external-monitor-job cmd … did not work.
+ (issue 21525)
- Fixed "Comparison method violates its general contract" error in BuildTrigger.execute
- (issue 17247)
+ Jenkins crash on startup after upgrade from 1.546 to 1.548.
+ (issue 21474)
- Edited description wasn't reflected when pressing the "Apply" button.
- (issue 18436)
+ f:combobox is narrow.
+ (issue 21612)
- Fixed a regression in remoting since 1.519 that caused FindBugs plugins to break.
- (issue 18349,
- issue 18405)
+ The workspace cleanup thread failed to handle the modern workspace location on master, and mishandled folders.
+ (issue 21023)
+
+ Fixed missing help items on "Configure Global Security" page
+ (issue 19832)
- Revisited the extension point added in 1.519 that adds custom plexus components.
-
+ Sort groups on user index page alphabetically.
+ (issue 21673)
- Slave launch thread should have the background activity credential.
- (issue 15578)
+ Should not be able to create a job named . (period).
+ (issue 21639)
+
- “Build Now” link did not work for multijobs.
- (issue 16974)
+ Wrong log message for out-of-order build record repair.
+ (issue 20730)
- Unix vs. Windows mode not correctly retained for command launchers under some conditions.
- (issue 18368)
+ Existing Fingerprint Action is reused and not added a second time.
+ (issue 19832)
- Edit views with non-ASCII names did not work since 1.500.
- (issue 18373)
-
- Fixed API incompatibility since 1.489.
- (issue 18356)
+ TestObject doesn't replace '%' character
+ (issue 21707)
- “Projects tied to slave” shows unrelated Maven module jobs.
- (issue 17451)
+ "java -jar jenkins.war" should use unique session cookie for users who run multiple Jenkins on the same host.
+
- Fixed file descriptor leak in fingerprint computation.
- (issue 18351)
+ Report number of all jobs as part of usage statistics
+ (issue 21448)
- Test history was not shown if suite name was part of the test name.
- (issue 15380)
-
- Added a new extension point to monitor the flow of stuff in the queue.
-
- Added a new extension point to monitor the provisioning of nodes from clouds.
- (pull request 819)
+ Replace description in error dialog instead of appending
+ (issue 21457)
+
- Executors running the builds can be now a subject of access control.
- (issue 18285)
+ API for adding actions to a wide class of model objects at once.
+ (issue 18224)
- Core started relying on Java 1.6 as per the agreement in the dev list.
- If you have a serious objection against it, please let us know
- before we really start relying on 1.6 features.
-
- Some actions confirmed by dialog were not working when CSRF crumbs were enabled.
- (issue 17977)
- (issue 18032)
+ Added infrastructure for moving items into or out of folders.
+ (issue 20008)
+ (issue 18028)
+ (issue 18680)
+
+ Apply buttons did not work in Internet Explorer in compatibility mode.
+ (issue 19826)
+
+ Builds can seem to disappear from a job in a folder if that folder is renamed.
+ (issue 18694)
+
+ /login offers link to /opensearch.xml which anonymous users cannot retrieve.
+ (issue 21254)
- CLI list-jobs command should list all nested jobs.
- (pull request 793)
+ Added API class SecurityListener to receive login events and similar.
+ (issue 20999)
- Provide a mechanism to differentiate between node properties that are applicable
- to the master node only and node properties that can be applied to all nodes
- (issue 18381)
-
- Maven module links in the module list page are broken.
- (issue 17713)
-
- 100% CPU pegging in Deflator.deflateBytes
- (issue 14362)
+ Option to hold lazy-loaded build references strongly, weakly, and more.
+ (issue 19400)
+ Builds disappear after renaming a job.
+ (issue 18678)
- NPE in DefaultMatrixExecutionStrategyImpl.waitForCompletion.
- (issue 18024)
-
- Optimizations in fingerprint recording.
- (issue 16301)
+ When clicking Apply to rename a job, tell the user that Save must be used instead.
+ (issue 17401)
+
+ Exception from XStream running Maven builds on strange Java versions.
+ (issue 21183)
- Using JNR-POSIX rather than JNA-POSIX for better platform support.
- (issue 14351)
-
- Errors searching build records when builds were misordered.
- (issue 15652)
-
- Finding the last failed build for a job (e.g. from a view column) broke lazy loading.
- (issue 16023)
+ When clicking Apply results in an exception (error page), show it, rather than creating an empty dialog.
+ (issue 20772)
+
- Do not fail startup in case ListView.includeRegex was syntactically malformed.
+ CannotResolveClassException breaks loading of entire containing folder, not just one job.
+ (issue 20951)
- CSS stylesheets misrendered in Chrome due to caching.
- (issue 17684)
+ Better robustness against XML deserialization errors.
+ (issue 21024)
- User icon in People broken if Jenkins root URL unconfigured.
- (issue 18118)
+ Minimizing disk I/O while loading the names of build records during Jenkins startup.
+ (issue 21078)
- Progress bar sometimes broken in People.
- (issue 18119)
+ Avoiding serializing the owning build as part of a test result action, as this can lead to errors later.
+ (issue 18410)
+ RingBufferLogHandler throws ArrayIndexOutOfBoundsException after int-overflow.
+ (issue 9120)
+
+ Hudson shows 0GB free space when space available drops below 1GB.
+ (issue 7776)
- Enable word breaking in potentially long strings like job names.
- (issue 17030)
-
- Allow filtering of the Run parameter build list by result.
- (issue 7280)
-
- Add support for scalatest-maven-plugin.
- (issue 18086)
+ Added filter field for installed plugins tab.
+ (issue 20219)
- When copying a folder, the display names of contained jobs were gratuitously cleared.
- (issue 18074)
+ groovysh command did not work in authenticated Jenkins instances.
+ (issue 17929)
- “Recurse in subfolders” option for list views produced exceptions when used with native Maven projects.
- (issue 18025)
+ Avoid eagerly loading all builds when displaying lists of them (Build History and Build Time Trend UIs).
+ (issue 20892)
- Using proper directory separator character for permalinks on Windows.
- (issue 17681)
-
- Use markup formater to display parameter description.
- (issue 18427)
-
+ Error page should be visible even if the anonymous user does not have overall/read access.
+ (issue 20866)
+
+ JavaScript errors when navigating away from a page with a build timeline widget while the timeline is loading.
+ (pull request 1041)
+
+ Fixed a possible dead lock problem in deleting projects.
+ (issue 19446)
- Should be able to collect all log records at a given level using a blank logger name.
- (issue 17983)
+ Improved error diagnosis for jzlib deflate problem.
+ (issue 20618)
- Reworked Upload Plugin gesture to work more like installation from an update center, and in particular to support dynamic load.
- (issue 16652)
+ Improved error diagnosis for CLI stream corruption.
+ (issue 18058)
- Errors in init.groovy halted startup; changed to just log a warning.
- (issue 17933)
+ Don't hold off building until saved for jobs copied from CLI.
+ (issue 20744)
+
+ Allow build queue and executor status panes to be collapsed.
+ (issue 5622)
+
+ Jenkins 1.540 just doesn't boot on Windows at all.
+ (issue 20630)
- Windows services now auto-restart in case of abnormal process termination.
-
- <f:dropdownDescriptorSelector> does not allow defaulting to specifig instance
- (issue 17858)
+ Add option to create view by copying an existing one.
+ (issue 13978)
- mark maven settings / global settings as default for new jobs
- (issue 17723)
+ Introduced the boot failure hook script that gets executed when Jenkins fails to start.
+ (issue 20609)
- Symlink handling problem with build permalinks on Windows.
- (issue 17681)
+ Fixed "java.lang.NoClassDefFoundError: JarURLConnection" on OpenJDK
+ (issue 20163)
- List views missing a required field were unloadable.
- (issue 15309)
+ FileAlreadyExistsException upon “deleted” symlink while (re)creating it.
+ (issue 20610)
- Maven module artifacts were not being deleted by the log rotator.
- (issue 17508)
-
- Properly find parent POMs when fingerprinting a Maven project.
- (issue 17775)
-
- Allow the combination filter to accept parameter values.
- (issue 7285)
-
- Extension point to transform test names (for use with alternative JVM languages).
- (issue 17478)
+ Allow background tasks to run simultaneously, preventing task blockage.
+ (issue 19622)
+
+ Fixed failed tests displaying as Yellow in JUnit history plot
+ (issue 7866)
+ CLI over HTTP was not working since 1.535.
+ (issue 20128)
+
+ hudson appears in a the webpage title.
+ (issue 14380)
+
+ Linkage error in InitializerFinder.discoverTasks blocks startup.
+ (issue 20442)
- Added a new set-build-parameter command that can update a build variable from within a build.
+ Add Test button to check proxy connection
+ (issue 20191)
- Can use -Dhudson.udp=-1 to disable UDP broadcast without showing an ugly exception.
+ Collect and report JVM crash dump files to assist trouble-shooting
- Third-party license display for core was broken since 1.506.
- (issue 17724)
+ ClassCastExceptions sometimes shown from views set to be recursive.
+ (issue 20415)
+
+ Show different “up” link for jobs in folders.
+ (issue 20106)
+
+ Add log handling line beginning with 'file://' as URL.
+ (issue 19866)
- Mitigation of exception from fingerprinting in a Maven project when a parent POM could not be located.
- (issue 17775)
+ Builds of a concurrently executable job might end up colliding on the same workspace.
+ (issue 10615)
- NPE from MatrixConfiguration.newBuild.
- (issue 17728)
-
- NPE configuring Copy Artifact with Maven jobs.
- (issue 17402)
+ Fixed error during installation of .deb package (/var/run/jenkins doesn't exists)
+ (issue 20407)
+
+ Global search box now remembers entered text
+ (issue 18192)
- /about now links to license information for plugins as well.
+ Add extension point to allow plugins to contribute to the checking of
+ assigned labels.
+ (issue 20514)
+
+ Fixed issue where CLI required giving Overall read permission to anonymous.
+ (issue 8815)
+
+ Jar cache option wasn't taking effect on JNLP slaves.
+ (issue 20093)
+
+ Interrupting remote class loading can lead to NoClassDefFoundError: Could not initialize class.
+ (issue 19453)
- JNLP slave installers can now work transparently with secured Jenkins.
- (SECURITY-54 / despite the ticket marker, this is not a security vulnerability)
-
- "Discard old build records" behavior is now pluggable, allowing plugins to define custom logic.
-
- Slave's Name should be trimmed of spaces at the beginning and end of the Name on Save.
- (issue 15836)
-
- Added new switch to ignore post-commit hooks in SCM polling triggers.
- This requires that the SCM plugin supports this feature, too!
- (issue 6846)
-
-
- ArrayIndexOutOfBoundsException or StringIndexOutOfBoundsException launching processes such as Mercurial polling. (Regression in 1.489.)
- (issue 15733)
-
- Fixed an XSS and a few open-redirect problems
-
- Support failsafe the same way as surefire in maven2 jobs
-
- Support settings.xml provider for freestyle maven buildstep too
-
- Invert dependency of maven-plugin and config-file-provider plugin (if config-file-provider is installed, this change requires an update of the config-file-provider to >= 2.3)
- (issue 14914)
-
- Invalid warning message when the config-file-provider plugin is not installed
- (issue 15207)
-
- JDK installation failed on some slaves with InvalidClassException: hudson.tools.JDKInstaller$Platform$1; local class incompatible: …
- (issue 14667)
-
- Provide symlink support on all possible platforms when using Java 7+, including newer versions of Windows.
- (issue 13202)
-
- NPE at hudson.maven.MavenModuleSet.getMaven
- (issue 14510)
-
- Invalid JSON gets produced with duplicate keys (seen on change sets)
- (issue 13336)
-
- Command line options to control the HTTP request handling thread behavior weren't working.
-
- Default max # of concurrent HTTP request handling threads were brought down to a sane number (from 1000(!) to 20)
-
- Display non-default update site URLs in the Advanced tab of Plugin Manager. (Currently not configurable from this UI.)
-
- Fixed the lock contention problem on Queue.getItems()
- (issue 16468)
-
- Put slave back online automatically, if there's enough disk space again
- (pull 514)
-
- Track and verify plugins used in configuration XML
- (issue 15003)
-
- Refactored behavior.js to run more predictably.
- Plugin JavaScript should use Behaviour.specify in place of
- Behaviour.register, Behaviour.list,
- hudsonRules, and jenkinsRules.
- (issue 14495 cont'd)
-
- Fixed a possible race condition in the remoting layer.
- (issue 14909)
-
- TarArchiver.visitSymlink can throw undeclared PosixException.
- (issue 14922)
-
- Parameterized Trigger plugin can break form validation for regular “Build after other projects are built” trigger.
- (issue 14995)
-
- hpi:run failed due to IllegalAccessErrors.
- (issue 14983)
-
- Fixed a synchronization problem between master/slave data communication.
- (issue 11251)
-
- Added a mechanism to filter extension points as they are discovered.
-
- Exposed the master's own node properties to be configured in /computer/(master)/configure
- (whereas /configure controls global node properties that apply to all nodes.)
-
- Made the list of environment variables in the help page pluggable
- (pull 434)
-
- Added a new hook to enable matrix project axes to change its values per build.
- (pull 449)
-
- Build Status page continues to show flashing "building" icons after build completion.
- (issue 13217)
-
- New Breadcrumb bar covers search suggestions
- (issue 13195)
-
- Fixed a log rotation portability problem on RedHat RPM package.
- (issue 5784)
-
- Computer.getHostName() returns null when it is not.
- (issue 13185)
-
- Fixed a socket file descriptor leak.
-
- Run $JENKINS_HOME/init.groovy.d/*.groovy as the init script if present
- (feature)
-
- Improved the page loading performance, especially on large latency network
-
- Resolve dependency issue between 'maven-plugin' and 'config-file-provider' plugin. If you are using the 'config-file-provider' plugin, you have to upgrade to version 1.9.1!
-
- Supported programmatic retrieval/update of slave config.xml
-
- Breadcrumb now supports drop-down menu for faster navigation
- (discussion)
-
- Configuration pages show a navigation drop-down menu in the breadcrumb bar to jump to sections
-
- Hyperlinks to model objects also supports drop-down menu for faster navigation.
- (discussion)
-
- New ExtensionPoint to enforce naming conventions for projects/jobs and two implementations: Default (no restriction), Pattern (regex).
- (issue 12928)
-
- java -jar jenkins.war now uses the HTTP only session cookie that's more robust against XSS vulnerability.
-
- Build fails on "Deploy artifacts to Maven repository" due to trying to upload parent POM twice for release artifacts.
- (issue 11248)
-
- Fixed an occasional "URI must start with a slash" error when the anonymous user doesn't have the read access.
-
- OS X installer can optionally create a new user "jenkins" and use
- it. This user has a writable home directory, making it possible to set up ssh for Jenkins.
-
- No workspace available message includes wiped out workspace as a potential cause.
- (issue 10432)
-
- Stop users being created in memory if they failed to provide all the required registration information correctly.
- (issue 7096)
-
- java -jar jenkins.war finally detects invalid command line options and report that as an error.
-
- When run in terminal, warning/error messages are colored.
-
- Sorting "diff" in test result requires 2 clicks
- (issue 5460)
-
- java.io.IOException: Unexpected termination of the channel - SEVERE: I/O error in channel Chunked connection when using jenkins-cli.jar (works on older Hudson version)
- (issue 11130)
-
- Debian init script now returns the proper exit code from the 'status' command.
- (issue 11306)
-
- Fixed a bug in Mac OS X ProcessKiller argument parsing.
- (issue 9634)
-
- Fixed a plugin boot problem that causes Jenkins to startup gracefully when some optional plugin dependencies aren't met (such as ivy to nant)
- (issue 11279)
-
- Avoid overwriting the repository definitions.
- (issue 11229)
-
- "Tailing" the console of an active job broken in 1.434
- (issue 11307)
-
- Fingerprint's age should be sorted by its elapsed time
- (issue 9210)
-
- Improved the classloading performance
-
- Expose original file name as a String parameter on for FileParameters
- (issue 11326)
-
- 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 <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)
-
- Avoid AbstractMethodError in the executors rendering.
-
- Don't litter HUDSON_HOME with atomic*.xml files.
-
- Hudson is made more robust in the face of malformed console annotations.
-
- Add parameter definition type and job name to job API
- (issue 8133)
-
- "Install as a service" now supports Vista and Windows 7.
-
- "Restart Hudson" button should appear when a plugin is manually installed.
-
- In this release only the background is changed until Dec 5th to i387 chip,
- to celebrate our 1.387 release (the feature is time bombed and will revert
- to the butler after that date.)
-
- Fixed a pipe clogging problem that can result in a hanging build.
- (issue 5977,
- issue 7572)
-
- Fixed a possible NPE in computing dependency changes.
-
- Fixed the malformed HTTP request error recovery behavior in Winstone.
- (issue 7201)
-
- When checking module descendant relationships, SCM changelog paths were using system file separators while module paths were always using /s.
- (issue 7611)
-
- Hudson was creating multiple instances of PageDecorators, resulting in data consistency problem.
- (report)
-
- Fixed a possible AbstractMethodError
- (issue 7546)
-
- Supported failsafe reports for the Maven2 job type.
- (issue 4229)
-
- Improving the master/slave communication to avoid pipe clogging problem.
- (issue 5977)
-
- Rolling back to Ant 1.8.0 due to bug in Ant 1.8.1 file copy with large files.
- (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)
-
- 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)
-
- Properly handle incremental builds of Maven projects using relative paths to modules.
- (issue 5357)
-
- Setting of MAXOPENFILES was not reflected in the debian init script.
- (issue 5721)
-
- Do not expose static resources under WEB-INF to clients
- (issue 7457)
-
- Console annotations are added to highlight warnings/errors in Maven
-
- If a polling initiated a build, capture its log to the build.
-
- 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)
-
- Added a new classloader ("a la" child first for plugin)
- (issue 5360)
-
- Make /buildWithParameters support remote cause and user supplied cause text
- for build via authentication token, just as /build does.
- (issue 7004)
-
- Auto install of JDK when master/slave are different platforms would fail.
- (issue 6880)
-
- Modified to work with Tomcat 7.
- (issue 6738)
-
- Fixed a possible security issue where a malicious user with the project
- configuration access can trick Hudson into leaking the proxy password,
- if Hudson is configured with a proxy with username/password.
- (SECURITY-3)
-
- 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)
-
- Hide some sidepanel links that should not be shown in user-private views.
- (issue 6832)
-
- Fix for file parameters that are copied to a subdirectory of the workspace.
- (issue 6889)
-
- File parameters uploaded via the CLI are now displayed correctly on the build Parameters page.
- (issue 6896)
-
- Allowed file parameters to be downloaded even when the name contains URL-unfriendly characters.
- (issue 6897)
-
- Fixed a garbage in the raw console plain text output.
- (issue 6034)
-
- "Hudson is loading" page didn't take the user back to the same page.
-
- Hudson can now remotely install JDK on Windows slaves when connecting via the
- "Let Hudson control this Windows slave as a Windows service" mode.
-
- The "Let Hudson control this Windows slave as a Windows service" mode now allows the same Windows slave
- to be used by multiple Hudson masters.
-
- Fix queue handling to close locking gap between removing job from queue and starting build,
- to prevent unintended concurrent builds (refactor of change first made in 1.360).
- (report)
-
- Allow multiple dependencies between same two projects, as they may trigger under
- different conditions and with different parameters.
- (issue 5708)
-
- Timeline on build trend page should use server timezone instead of always GMT.
- (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)
-
- Cache downloaded JDKs.
-
- Reinstall a JDK when a different version is selected.
- (issue 5551)
-
- Fix javascript error when a plugin uses an empty dropdownList, resulting in LOADING overlay being left up.
- (issue 6542)
-
- Add setting so job views may show only enabled or disabled jobs.
- (issue 6673)
-
- File parameters can now be downloaded from the build Parameters page.
- (issue 6719)
+ Name channel executor threads for better diagnosability.
+ (issue 19004)
- Added an ability to point to different update sites.
-
- Added a new extension point to plug in custom utility to kill processes.
-
- Added a proactive error diagnostics to look for a broken reverse proxy setup.
- (report)
+ Better diagnosability for remoting StreamCorruptedException
+ (issue 8856)
- A Java6 dependency had crept in in 1.359.
- (issue 6653)
-
- Workaround for bug in Glassfish Enterprise.
- (issue 6459)
+ Core started relying on Java6 API, completing Java5 -> Java6 migration.
+ (discussion)
- Added an extension point to control the assignment of tasks to nodes.
- (issue 6598)
+ Adding a batch of contributed localization from the community.
- Fix css conflict introduced in 1.357 that caused missing data display in analysis plugins.
- (issue 6496)
+ Disabled, aborted, and not-build status now has different image names to allow
+ themes to use different icons.
+ (issue 19438)
- Support "optional=true" parameter for @Extension.
+ Ask for confirmation if an user tries to leave an edited configuration page.
+ (issue 19835)
- Supported OpenSSL-style certificate/key file format with "java -jar hudson.war"
+ Test failure summary appearance is improved.
+ (issue 19884)
- If --httpsPort option is given without the certificate, run with a one-time self-signed certificate.
+ Added CLI commands that manipulate views
+ (issue 19996)
- Hudson shouldn't show a login error page unless the user really failed to login (think about when the user presses a back button.)
-
- Maven builds abort unexpectedly due to a SocketTimeoutException on machine with poor resources.
- (issue 3273)
-
- Fix incorrect handling of ".." in paths with mix of / and \ separators since 1.349.
- (issue 5951)
-
- Javadoc publishing should not fail build if javadoc is already current.
- (issue 6332)
+ Improved the /cli help screen.
+ (issue 20023)
- Fix download of files/artifacts larger than 2GB.
- (issue 6351)
+ Polling-triggered jobs get scheduled en-mass on start-up if slaves aren't online yet.
+ (issue 8408)
- Build page may not list all of the artifacts since 1.348.
- (issue 6371)
+ Fixed the handling of nested variable expansion.
+ (issue 20280)
- Add workaround for Opera 10.52/53 bug causing error in saving job configuration.
- (issue 6424)
+ NPE thrown from CLI build command under some circumstances.
+ (pull request 979)
- Fix createSymlink problem on *nix systems that do not use GNUCLibrary since 1.356.
- (issue 6437)
+ Fixed a bug in the compatibility transformer (since 1.527) that causes VerifyError in Ivy plugin and possibly others.
+ (issue 19383)
- Hide add/edit description link on test result pages when user does not have
- permission to submit a description.
+ Pass full list of all possible jobs to ViewJobFilter when recurse option is set
+ (issue 20143)
- Changed permission required to set description on test result pages
- from Build Job to Update Run.
+ get-job and update-job CLI commands can now work with folders, or indeed any AbstractItem.
+ (issue 20236)
- Add "LOADING" overlay on global and job config pages until form is ready for use.
-
- Email recipient lists now support build parameters.
- (issue 6394)
-
- Make it easier to see the latest update jobs on the Update Center page.
- (issue 4255)
-
- Allow plugins to use forms with an onsubmit handler, and fix "no-json" handling.
- (issue 5927)
-
- Updated bundled subversion plugin to version 1.17.
-
- Fix StringIndexOutOfBoundsException in console log from UrlAnnotator.
- (issue 6252)
-
- Fixed potential deadlock between saving project config and getting project page.
- (issue 6269)
-
- Fixed timeline display on build time trend page.
- (issue 6439)
-
- Fixed garbled response of XML API if xpath is specified.
- (ja@hudson.dev.javanet)
-
- 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)
-
- Extension points can be now sorted.
+ Added API allowing plugins to hide entries from the context menu even while they appear in the sidepanel.
+ (issue 19173)
+
+ Upgrade bundled plugin versions: ssh-slaves to 1.5, and credentials to 1.9.1
+ (issue 20071)
- 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)
-
- Console page while build is running did not wrap lines when viewed in IE.
- (issue 5869)
-
- Fixed build history to indicate test failure for MavenBuild and MavenModuleSetBuild.
-
- Make dropdownList work in repeatable content, such as a build step.
-
- Fixed a bug where a job created via XML didn't properly receive upstream/downstream computation.
- (report)
-
- Argument masking wasn't working correctly for commands run on slaves
- (report)
-
- Added the slave retention strategy based on a schedule.
+ Build button column was broken in 1.535 for parameterized builds.
+ (issue 20080)
+
+ Miscalculation of environment variables caused some binaries (such as ssh) to not be found.
+ (issue 19926)
+ Extension point for secure users of REST APIs (permitting JSONP and primitive XPath).
+ (issue 16936)
- POM parsing was still using the module root as the base for relative paths for alternate settings files.
- (issue 6080)
+ “Run a build” link in page shown when no workspace existed for a job was not functional; unlinking.
- Fix dynamic updates of build history table when CSRF protection is turned on.
- (issue 6072)
-
- Improved the error reporting mechanism in LDAP setting.
-
- Raw console output contains garbage.
- (issue 6034)
-
- Fixed a file handle leak in the slave connection.
- (issue 6137)
-
- Quiet period wasn't taking effect properly when doing parameterized builds.
+ Integer overflow could cause JavaScript functions to break in long-running Jenkins processes.
+ (issue 20085)
+
+ Reverted the JENKINS-18629 fix in 1.536 as it causes various regressions in plugins.
+ (issue 20262)
- Fix possible form submission error when using multiple combobox elements.
- (issue 6025)
+ Fixed two file descriptor leaks.
+ (issue 14336)
- Better escaping of test case names in test results pages.
- (issue 5982)
+ RuntimeException if you try to save a config with a choice parameter that has no choices.
+ (issue 18434)
- Make radio buttons work in repeatable content, such as a build step.
- (issue 5028)
-
- Fixed the handling of verifying that the POM path entered for Maven projects exists.
- (issue 4693)
-
- Added link to builds in buildTimeTrend
- (issue 3993)
+ 1.534 made ZIP downloads of artifacts work again, but missing a base directory inside the ZIP.
+ (issue 19947)
+
+ Stapler error saving certain kinds of configuration.
+ (issue 18629)
+
+ Upgrade Trilead SSH client library to version that does not cause connection loss when
+ there is a lot of logging on the build slave and the performance improvements in
+ ssh-slaves 0.27+ are enabled (
+ issue 18836,
+ issue 18879,
+ issue 19619)
+
+ Upgrade bundled iplugin versions: ssh-slaves to 1.4, ssh-credentials to 1.5.3 and
+ credentials to 1.8.3
+ (issue 19945)
+
+ Executor threads are now created only on demand.
- Fixed a file handle leak when a copy fails.
- (issue 5899)
-
- Replace '>' with '_' in username, as already done for '<'.
- (issue 5833)
-
- Fix editableComboBox to select item when mouse click takes more than 100ms.
- (issue 2722)
-
- Fixed NPE when configuring a view without "Regular expression".
-
- Page shouldn't scroll up when the user opens/closes a stack trace in the test failure report.
+ Windows JDK installer failed in a path with spaces.
+ (issue 19447)
- Fixed a bug where Hudson can put a wrong help file link.
- (report)
+ Windows JDK installer should not install a public JRE.
+ (issue 8957)
+
+ After deleting last build, next build of last build is zombie.
+ (issue 19920)
+
+ Split matrix authorization strategies into an independent plugin.
+
+ UI Samples plugin fully separated from core. To view samples during plugin development or at any other time, just install from the update center.
- Fixed Maven site goal archiving from slaves.
- (issue 5943)
+ View description should be clearly separated from the Jenkins system message.
+ (issue 18633)
- Fixed a regression with NetBeans Hudson plugin progressive console output.
- (issue 5941)
+ SCM polling sometimes broken since 1.527 due to a change in how environment variables are calculated.
+ (issue 19307)
- Fixed a situation where a failure in plugin start up can prevent massive number of job loss.
-
- CLI commands on protected Hudson now asks a password interactively, if run on Java6.
-
- Added CLI 'login' and 'logout' commands so that you don't have to specify a credential
- for individual CLI invocation.
-
- URLs in the console output are now hyperlinks.
+ Breadcrumb bar moves away from header when scrolling past end of page on OS X.
+ (issue 19803)
+
+ "java -jar jenkins.war" now runs on Jetty8. Command line options are still compatible.
+ (issue 18366)
- Improved the URL annotation logic.
+ "java -jar jenkins.war" gets the "--spdy" option to enable SPDY.
- Add drag&drop support for f:repeatable lists and use this for
- the JDK/Ant/Maven installations in global config so these can be reordered.
+ Expand all/Collapse all functionality for artifact tree view.
+ (pull request 616)
- Integrated a new round of community-contributed localizations (ca, es, fi, fr, hi_IN, it, nl, ru, and sv_SE locales.)
+ Visualize queued jobs in view.
+ (pull request 531)
- Fix handling of relative paths in alternate settings.xml path for Maven projects.
- (issue 4693)
-
- Alternate settings, private repository, profiles, etc were not used in embedded Maven for
- deploy publisher.
- (issue 4939)
-
- Make editableComboBox work in repeatable content, such as a build step.
-
- If content is captured using <j:set var="..">..content..</j:set>,
- fixed this to use proper HTML rendering when appropriate.
-
- '<' and '&' in the console output was not escaped since 1.349
- (issue 5852)
+ Default crumb issuer configurations saved in older releases did not load as of Jenkins 1.531.
+ (issue 19613)
- Fixed an AbstractMethodError in SCM polling under some circumstances.
- (issue 5756)
+ As of 1.532 download of artifact ZIPs was broken.
+ (issue 19752)
- Fixed a ClassCastException in the Subversion plugin - now using Subversion plugin 1.13.
- (issue 5827)
-
- The Maven Integration plugin link in the Update Center was going to a dead location.
- (issue 4811)
-
- On RPM/DEB/etc installation, don't offer the self upgrade. It should be done by the native package manager.
- (report)
-
- Fixed a possible lock up of slaves.
-
- Added advanced option to LogRotator to allow for removing artifacts from old builds
- without removing the logs, history, etc.
- (issue 834)
-
- Authentication support in Hudson CLI.
- (issue 3796)
-
- Added console annotation support to SCM polling logs.
-
- Fix deserialization problem with fields containing double underscore.
- (issue 5768)
-
- Fix deserialization problem for Exception objects where the XML has bad/old data.
- (issue 5769)
-
- Fix serialization problem with empty CopyOnWriteMap.Tree.
- (issue 5776)
-
- Fixed a bug that can cause 404 in the form validation check.
-
- Remote build result submission shouldn't hang forever even if Hudson goes down.
-
- Added a monitor for old or unreadable data in XML files and a manage screen to assist
- in updating files to the current data format and/or removing unreadable data from plugins
- that are no longer active. "Manage Hudson" page will show a link if any old/unreadable
- data was detected.
-
- Added a mechanism to bundle init.groovy inside the war for OEM.
- (report)
-
- Added an extension point to annotate console output.
- (issue 2137)
+ Old copies of maven3-agent.jar on slaves were not being reliably updated, leading to errors.
+ (issue 19251)
+
+ Add option to disable "Remember me on this computer" checkbox in login screen.
+ (issue 15757)
+
+ Added postCheckout method for SCMs
+ (issue 19740)
+ Working around a GZip compression bug in jzlib affecting transfer of certain large, repetitive artifacts.
+ (issue 19473)
+
+ Lazy-loading bug: builds go missing.
+ (issue 19418)
- Fix javascript problem showing test failure detail for test name with a quote character.
- (issue 1544)
-
- Hudson can incorrectly configure labels for the master when bleeding edge EC2 plugin is used.
-
- Fixed the regression wrt the whitespace trimming caused by 1.346.
- (issue 5633)
-
- Under some circumstances, Hudson can incorrectly delete the temporary directory itself.
- (issue 5642)
-
- Newlines in MAVEN_OPTS environment variable can cause problems in other contexts.
- (issue 5651)
+ (re)create build number->id symlink if missing when updating permalink.
+ (issue 19034)
- Improved the form validation mechanism to support multiple controls.
- (issue 5610)
+ Display the full display name in title for jobs and views.
+ (pull request 884)
+
+ Added a new extension point to control where archived artifacts get stored.
+ (issue 17236)
- Added message to slave log when it has successfully come online.
- (issue 5630)
+ Use fine-grained permissions for node manipulation via REST API & CLI
+ (issue 18485)
+
+ Make the link to the aggregated test result from the project page work.
+ (issue 9637)
- Maven modules should not be buildable when the parent project is disabled.
- (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)
-
- Escape username in URLs in case it contains special characters such as "#".
- (issue 2610)
+ Deleting an external run did not immediately remove it from build list, leading to errors from log rotation.
+ (issue 19377)
- Fix sidepanel link for People to be visible and show view-specific info when appropriate.
- (issue 5443)
+ When copying a directory from master to slave fails due to an error on the slave, properly report it.
+ (issue 9540)
- Improved HTML rendering, not using closing tags that do not exist in HTML.
- (issue 5458)
+ Identify user agent for Internet Explorer 11.
+ (issue 19171)
- Show better error message for missing view type selection when creating a view.
- (issue 5469)
+ Since 1.518, fingerprint serialization broke when job or file names contained XML special characters like ampersands.
+ (issue 18337)
- 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)
+ Robustness against truncated fingerprint files.
+ (issue 19515)
- Use user selected icon size on People page.
- (issue 5447)
+ JavaScript error in the checkUrl computation shouldn't break the job configuration page.
+ (issue 19457)
- Speed/footprint improvement in the HTML rendering.
-
- Update center retrieval, "build now" link, and real-time console update was broken in 1.344.
- (issue 5536)
+ Annotate the Advanced section if some fields are already customized.
+ (issue 3107)
- Fixed the backward incompatibility introduced in JENKINS-5391 fix in 1.344.
- (issue 5391)
+ No events fired when project is enable/disable or the description is changed
+ (issue 17108)
- Removed the forced upper casing in parameterized builds.
- (issue 5391)
-
- Password parameter on the disk should be encrypted.
- (issue 5420)
-
- Duplicate entries on Upstream/Downstream project with "Build modules in parallel".
- (issue 5293)
+ Send Maven agent JARs to slaves on demand, not unconditionally upon connection.
+ (issue 16261)
- "Projects tied on" should be "Projects tied to".
- (issue 5451)
+ Occasional race condition during startup.
+ (issue 18775)
+
+ Robustness against startup error for users of Global Build Stats plugin.
+ (issue 17248)
+
+ 404s from Javadoc and HTML Publisher plugins.
+ (issue 19168)
- Fixed the bug that prevents update center metadata retrieval in Jetty.
- (issue 5210)
-
- Show which plugins have already been upgraded in Plugin Manager.
- (issue 2313)
-
- Show Hudson upgrade status on manage page instead of offering same upgrade again.
- (issue 3055)
-
- Make badges in build history line up.
- (report)
+ Build number symlinks and permalinks not updated for Maven module builds.
+ (issue 18846)
- Don't report a computer as idle if it running the parent job for a matrix project.
- (issue 5049)
-
- Improve error message for a name conflict when renaming a job.
- (issue 1916)
-
- Job description set via the remote API was not saved.
- (issue 5351)
-
- Work around a JVM bug on Windows that causes the "Access denied" error
- while creating a temp file.
- (issue 5313)
-
- Fixed a NPE in the update center with the container authentication mode.
- (issue 5382)
-
- Global MAVEN_OPTS for Maven projects wasn't getting loaded properly for configuration.
- (issue 5405)
-
- Fix for parameterized project with choice parameter value that has < or > character.
- (report)
-
- Build queue was showing some of the items in the wrong order — it should show new ones first and
- old ones later.
- Move form to adjust logging levels to its own page and include table of configured levels.
- (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)
-
- Introduced a new extension point for test result parsers.
- (discussion)
-
- Data loading is made more robust in the face of linkage failures.
- (issue 5383)
-
- Auto-detect if Hudson is run in Solaris SMF
- and provide restart capability.
- (report)
+ With Apache Maven 3.1 build, logging configuration from the Apache Maven distribution is not used.
- Formalized an extension point to control priority among builds in the queue.
- (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)
-
- Downstream jobs could fail to trigger when using per-project read permissions.
- (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)
-
- 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)
-
- Fixed a bug that induces a NPE during list view column construction.
- (issue 5061)
+ Avoid log duplication with Apache Maven 3.1 builds
- Fixed a bug that can cause Hudson to fail to encode non-ASCII characters in URL.
- (issue 5155)
+ Ungraceful handling of empty matrix project axes.
+ (issue 19135)
- Added "process-test-classes" phase to Maven intercepter.
- (issue 2226)
+ Updated Groovy to 1.8.9 to avoid GROOVY-4292.
+
+ CLI login command broken on Windows since 1.518.
+ (issue 19192)
- Fixed a regression in the remote API in 1.341.
- (report)
-
- Improved error diagnostics when failing to auto install Maven/Ant.
- (issue 5272)
-
- Infer the default e-mail address more smartly with user IDs like "DOMAIN\user" (often seen in Windows)
- (issue 5164)
-
- The hudson.model.Run.ArtifactList.treeCutoff property should not limit the number
- of artifacts shown by the API.
- (issue 5247)
+ A malformed JUnit result file should mark that test suite as a failure, but not interrupt archiving of other tests.
+ (issue 19186)
- Spanish translation made a great progress.
+ Build for $username now shows also build scheduled by user
+ (issue 16178)
+ Command line now supports "--sessionTimeout" option for controlling session timeout
- Non ASCII chars get mangled when a new user is created.
- (issue 2026)
+ Form validation methods weren't getting triggered when one of its dependency controls change.
+ (issue 19124)
- Fixed garbled mail text when default encoding is not UTF-8.
- (issue 1811)
+ When POST is required for some HTTP operation but GET was used, the response should have status code 405.
+ (issue 16918)
- Fixed a bug in the log rotation setting of RPM packages.
- (report)
-
- Added a new CLI command to obtain list of changes by specifying builds.
-
- Improved memory/swap monitoring on Solaris systems that doesn't have the 'top' command.
- (report)
-
- User IDs in Hudson are now case preserving but case insensitive.
- (issue 4354)
+ Correct help text of Label field in automatic installation of tools in global configuration.
+ (issue 19091)
- CVS support is separated into a plugin, although it's still bundled by default for compatibility.
- (issue 3101)
+ Use Guice from Google rather than a fork
+
+ Jenkins does not invoke ProcessKillers for Windows
+ (issue 19156)
- slave.jar incorrectly shipped with a version number indicating a private build.
- (issue 5138)
-
- Global MAVEN_OPTS weren't saving due to TopLevelItemDescriptors not being configured in global configuration.
- (issue 5142)
-
- Make maven project more resilient to exceptions from plugins.
- (issue 3279)
-
- Add the ability to configure low-disk space thresholds.
- (issue 2552)
-
- Allow BuildWrapper tearDown code to detect when the build has failed.
- (issue 2485)
-
- Add help regarding "Auto" repository browser selection and add support
- for this in Subversion plugin.
- (issue 2082)
+ Fixed NoSuchFieldError: triggers with older Maven plugin
+ (issue 18677)
- Introduced a mechanism so that writing XSS-free code is easier.
- (discussion)
-
- 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)
+ Improve search to locate items inside folders.
+ (pull request 848)
+ (pull request 893)
+
+ Windows path separators not correctly escaped in Maven properties configuration.
+ (issue 10539)
- Expose upstream cause details via remote API.
- (issue 5074)
+ Improved EnvironmentContributor to support project-level insertion.
+ (issue 19042)
- Exceptions in one publisher shouldn't block all other publishers from running.
- (issue 5023)
+ HudsonAuthenticationEntryPoint can break CLI support, because the port isn't exposed properly.
+ (issue 18634)
+
+ Report an user friendly error page if a deletion of a build fails.
+ (pull request 827)
- Fixed OutOfMemoryError in JNLP slaves that are running for too long.
- (issue 3406)
+ Maven build failure wasn't describing errors like Maven CLI does.
+ (issue 15025)
- Auto installer for Maven couldn't be configured after the fact.
+ MavenModuleSetBuild.getResult is expensive.
+ (issue 18895)
- Fixed a bug that the form field validation couldn't handle <select> box.
- (report)
+ Revisited fix to be compatible for plugins.
+ (issue 18119)
- Fixed a possible "XYZ is missing its descriptor" storm.
- (issue 5067)
-
- Group available plugins in Plugin Manager by category.
- (issue 1836)
+ Ensuring /log/all shows only INFO and above messages, even if custom loggers display FINE or below.
+ (issue 18959)
- Add sorting and link to directory browser for artifact list and tree display.
- (issue 4976)
+ Added a new monitor that detects and fixes out-of-order builds records.
+ (issue 18289)
- Make links in build history for a view stay under that view.
- (issue 5021)
+ Added CLI command create-node.
+ (issue 18282)
- Update or remove lastSuccessful/lastStable symlinks on filesystem as appropriate
- when a build is deleted.
- (issue 1986)
-
- In-demand strategy could not relaunch slave nodes since 1.302.
- (issue 3890)
-
- Actual cause for slave going offline was always masked by channel-terminated cause.
+ Can't build using maven 3.1.0
+ (issue 15935)
- Improved display of why a slave is offline (don't incorrectly say "failed to launch").
+ Fixed Winstone+mod_proxy_ajp+SSL combo issue.
+ (issue 5753)
- Improved the error diagnostics on the failure to establish connection with JNLP slaves early on.
+ JENKINS_DEBUG_LEVEL misinterpreted by Winstone, causing excessive logging.
+ (issue 18701)
+
+ Since 1.520, Jenkins requires Java 6 or later, breaking Maven builds set to use JDK 5. Now falls back to JVM of slave agent but sets compile/test flags to use defined JDK.
+ (issue 18403)
+
+ Since 1.517, Maven projects using Maven 2 could not build projects using extensions depending on Apache Commons Codec.
+ (issue 18178)
- Fix so configure-slave permission actually allows configuration of slaves.
+ Test harness was packing copies of Maven into plugin archives under some conditions.
+ (issue 18918)
- User pages showed add/edit description link to users without permission to edit,
- and guests were allowed to edit the user profile for anonymous user.
+ Provided maven settings.xml in maven builder is lost.
+ (issue 15976)
- Debian package now demands full JRE, not just a headless JRE.
- (issue 4879)
+ Exception when running polling with a Maven installation not defined on master.
+ (issue 18898)
- Avoid exception if a plugin provides null for a dynamic node label.
- (issue 4924)
+ Since 1.477 GET on /view/…/config.xml included a spurious wrapper element.
+ (issue 17302)
+
+ Clearer display of log messages: chronological order, and coloration of repeated vs. fresh metadata (date, log level, log source).
- If restart is not supported, explain why.
- (issue 4876)
+ Fixed a regression that broke some plugins' form validation
+ (issue 18776)
- Matrix configuration builds should continue even when Hudson is about to shut down.
- (issue 4873)
+ People View does Not Populate if JQuery plugin enabled.
+ (issue 18641)
+
- Send build status email to valid addresses rather than aborting for one invalid address.
- (issue 4927)
+ Clock Difference broken on Manage Nodes page
+ (issue 18671)
- Smart JDK/Maven/Ant auto installers aren't available for existing tool configurations.
+ Fixed another possible cause of an NPE from MatrixConfiguration.newBuild.
+ (issue 17728)
- Hudson can now run gracefully (albeit bit slowly) where JNA is not available.
- (issue 4820)
-
- Add ability to delete users from Hudson.
- (issue 1867)
-
- Gracefully detect the double loading of JNA instead of failing later with NoClassDefFoundError
- (detail)
-
- Introduced a structure around the initialization process for better reporting and etc.
+ NPE in MavenFingerprinter.getArtifactRepositoryMaven21.
+ (issue 18441)
- Debian packages creates Hudson user with /bin/bash to accomodate some tools that want a valid shell.
- (issue 4830)
+ More reliability improvement in remote slave reconnection.
+
+ Do not load disabled plugins as dependencies for other plugins.
+ (issue 18654)
- Space in axis value for matrix type project was lost on reconfiguration.
- (issue 2360)
-
- Remember me did not work with unix authentication.
- (issue 3057)
-
- Node variables not passed through to Maven jobs.
- (issue 4030)
+ Fixed: claiming of tests doesn't work in Maven jobs (claim-plugin)
+ (issue 14585)
+
- Fix handling of non-ASCII characters in external job submission.
- (issue 4877)
+ Fixed a regression in the config form with some plugins
+ (issue 18585)
- Job assigned to label that no longer has any nodes generates exception since 1.330.
- (issue 4878)
+ Fixed a dead lock in the Project class and improved the signature of the persisted XML form a bit.
+ (issue 18589)
- Custom workspace on Windows with just a drive letter or using forward slashes in path
- failed to build in 1.334.
- (issue 4894)
+ Improved memory efficiency in parsing test reports with large stdio output files.
+ (issue 15382)
+
+ Node monitoring now happens concurrently across all the slaves, so it'll be affected less by problematic slaves.
+ (issue 18438)
- Core version number in plugin manager warning message was missing in 1.334.
+ Deadlock during Maven builds Parsing POM step
+ (issue 15846)
- Matrix build wasn't showing their full name in the executor list on the left.
-
- Hudson's UDP broadcast/discovery now supports IP multicast.
+ If every node is restricted to tied jobs only, Matrix build jobs can never start.
- Fixed a possible exception in submitting forms and obtaining update center metadata with Winstone in 1.333.
- (issue 4804)
-
- Remoting layer was unable to kill remote processes. Prevented Mercurial plugin from implementing poll timeout on slaves.
- (issue 4611)
-
- Display of console output as plain text did not work in browsers since 1.323.
- (issue 4557)
-
- Show "Latest Test Results" link even when a new build is running.
- (issue 4580)
-
- Fix broken links for failed tests on build page for a matrix type project.
- (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)
-
- Project read permission was not enforced via /jobCaseInsensitive/jobname path.
+ Build with parameters returns empty web page
+ (issue 18425)
- Project configuration could be modified via POST to /job/jobname/config.xml with only
- "Extended Read" permission but not configure permission.
+ Access denied error results in ERR_CONTENT_DECODING_FAILED on most browsers, masking the root cause.
+ (issue 15437)
- Fixed the over zealous escaping in the inlined unit test failure report.
+ Fixed the master/slave handshake problem when a slave runs on non-ASCII compatible encoding (such as EBCDIC.)
+
+ Added a diagnosis for StreamCorruptedException problem
+ (issue 8856)
+
+ Matrix project's parent can be now tied to labels/slaves.
+ (issue 7825)
- Fixed OutOfMemoryError in Winp
- (issue 4058)
+ Clean up fingerprint records that correspond to the deleted build recods
+ (issue 18417)
- Write log message and ignore unrecognized permissions when loading XML.
- (issue 4573)
+ Fixed "Comparison method violates its general contract" error in BuildTrigger.execute
+ (issue 17247)
- Fix in stapler so we don't redirect to "." which causes problem in some containers.
- (issue 4787)
+ Edited description wasn't reflected when pressing the "Apply" button.
+ (issue 18436)
- List counts for duplicate cause entries for a build rather than listing many times.
- (issue 4831)
-
- Plugin manager now shows warning for upgrade/install of plugins into a Hudson that
- is older than the plugin was built for.
- (issue 541)
-
- CLI "build" command now supports passing parameters.
-
- Job should provide doDescription to allow easy manipulation over http
- (issue 4802)
-
- Improvement in the caching of the view templates.
+ Fixed a regression in remoting since 1.519 that caused FindBugs plugins to break.
+ (issue 18349,
+ issue 18405)
- Added new SaveableListener to be called when objects implementing Saveable are saved.
+ Revisited the extension point added in 1.519 that adds custom plexus components.
- Fixed a performance problem in the file upload with Winstone.
- (report)
+ Slave launch thread should have the background activity credential.
+ (issue 15578)
- Allow non-absolute URLs in sidebar links that do not end with slash character.
- (issue 4720)
+ “Build Now” link did not work for multijobs.
+ (issue 16974)
- Build other projects "even when unstable" option was not working with Maven projects.
- (issue 4739)
+ Unix vs. Windows mode not correctly retained for command launchers under some conditions.
+ (issue 18368)
- When renaming a log recorder, check new name uses valid characters, remove config file for
- old name and redirect to new name after save.
+ Edit views with non-ASCII names did not work since 1.500.
+ (issue 18373)
+
+ Fixed API incompatibility since 1.489.
+ (issue 18356)
- Fixed ArrayIndexOutOfBoundsException in my view.
- (report)
+ “Projects tied to slave” shows unrelated Maven module jobs.
+ (issue 17451)
- Fixed a race condition in interrupting pending remote calls.
+ Fixed file descriptor leak in fingerprint computation.
+ (issue 18351)
- Retry shouldn't kick in if the build is aborted during checkout.
+ Test history was not shown if suite name was part of the test name.
+ (issue 15380)
- Hudson now sends "Accept-Ranges" header where it's supported.
- (report)
+ Added a new extension point to monitor the flow of stuff in the queue.
+
+ Added a new extension point to monitor the provisioning of nodes from clouds.
+ (pull request 819)
- Hudson is internally capable of supporting multiple update sites.
+ Possible to create a custom AbstractDiskSpaceMonitor.
- Added a new "safe-restart" CLI command, also accessible at "/safeRestart", and used for post-upgrade/plugin install restart.
- (issue 4553)
+ Executors running the builds can be now a subject of access control.
+ (issue 18285)
+
+ Core started relying on Java 1.6 as per the agreement in the dev list.
+ If you have a serious objection against it, please let us know
+ before we really start relying on 1.6 features.
+
+ Some actions confirmed by dialog were not working when CSRF crumbs were enabled.
+ (issue 17977)
+ (issue 18032)
- Added "delete-builds" CLI command for bulk build record deletion.
+ CLI list-jobs command should list all nested jobs.
+ (pull request 793)
- Supported a relative path in the custom workspace directory, which resolves from FS root of the slave.
+ Provide a mechanism to differentiate between node properties that are applicable
+ to the master node only and node properties that can be applied to all nodes
+ (issue 18381)
+
+ Maven module links in the module list page are broken.
+ (issue 17713)
- Fixed another NotExportableException when making a remote API call on a project.
- Broke NetBeans integration and possibly others.
- (issue 4760)
+ 100% CPU pegging in Deflator.deflateBytes
+ (issue 14362)
- 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)
- Fixed NotExportableException when making a remote API call on a project.
- (report)
-
- Fixed IllegalArgumentException: name
- (report)
+ Log cluttered with irrelevant warnings about build timestamps when running on Windows on Java 6.
+ (issue 15587)
+
+ Fingerprint action deserialization problem fixed.
+ (issue 17125)
+
+ Updating the master computer's configuration from the slave list UI had no immediate effect.
+ (issue 17276)
+
+ Improved the tracking of queued jobs and their eventual builds in the REST API.
+
+ Configured log recorders can now pick up messages logged from slaves.
+ (issue 18274)
+
+ Added a new extension point to contribute custom plexus components into Maven for the maven project type.
+
+ Remoting classloader performance improvement upon reconnection to the same slave.
+ (issue 15120)
- Fixed a memory leak problem with the groovysh Hudson CLI command.
- (issue 4618)
+ NPE in DefaultMatrixExecutionStrategyImpl.waitForCompletion.
+ (issue 18024)
- CVS changelog reports were incorrect if built from tags.
- (issue 1816)
+ Optimizations in fingerprint recording.
+ (issue 16301)
- Upstream projects list was lost when saving matrix type project.
- (issue 3607)
+ Using JNR-POSIX rather than JNA-POSIX for better platform support.
+ (issue 14351)
+
+ Errors searching build records when builds were misordered.
+ (issue 15652)
+
+ Finding the last failed build for a job (e.g. from a view column) broke lazy loading.
+ (issue 16023)
- slave.jar now supports HTTP BASIC auth.
- (issue 4071)
+ Do not fail startup in case ListView.includeRegex was syntactically malformed.
- Fixed a problem where taglibs defined in plugins cannot be seen from other plugins.
- (report)
+ CSS stylesheets misrendered in Chrome due to caching.
+ (issue 17684)
- Improved the UI of taking a node offline.
+ User icon in People broken if Jenkins root URL unconfigured.
+ (issue 18118)
- Added improved logging for exceptions encountered when attempting to invoke Maven in Maven projects.
- (issue 3273)
-
- Automated tool downloads are made more robust by using HTTP download retries.
-
- SCM information is now exposed via the remote API.
-
- Added the "install-plugin" command to install plugins from CLI.
- (report)
+ Progress bar sometimes broken in People.
+ (issue 18119)
- Fixed NoSuchMethodError error during error recovery with Maven 2.1.
- (issue 2373)
-
- RemoteClassLoader does not persist retrieved classes with package structure
- (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)
+ Enable word breaking in potentially long strings like job names.
+ (issue 17030)
- When showing why a build is pending, Hudson now puts hyperlinks to node/label/project names.
+ Allow filtering of the Run parameter build list by result.
+ (issue 7280)
- Custom workspace is now subject to the variable expansion.
- (issue 3997)
-
- Fixed UI selector (hetero-list) to handle nested selectors (resolves conflict between
- Promoted Builds and Parameterized Trigger plugins).
- (issue 4414)
-
- Fixed incremental Maven build behavior to properly handle new modules without hitting NPE.
- (issue 4624)
-
- Added the "build" CLI command that can not only schedule a new build, but also wait until its completion.
+ Add support for scalatest-maven-plugin.
+ (issue 18086)
- Made visibility rules of test result remote API consistent with those for individual test cases.
+ When copying a folder, the display names of contained jobs were gratuitously cleared.
+ (issue 18074)
- Fixed a bug in the HTTP Range header handling.
- (report)
+ “Recurse in subfolders” option for list views produced exceptions when used with native Maven projects.
+ (issue 18025)
- Fixed a bug in .cvspass form field persistence.
- (issue 4456)
+ Using proper directory separator character for permalinks on Windows.
+ (issue 17681)
+
+ Use markup formater to display parameter description.
+ (issue 18427)
+
- Overview of all SCM polling activity was never showing any entries.
- (issue 4609)
+ NPE from Run.getDynamic.
+ (issue 17935)
+
+ Should be able to collect all log records at a given level using a blank logger name.
+ (issue 17983)
- Fixed a bogus IOException in the termination of CLI.
+ Reworked Upload Plugin gesture to work more like installation from an update center, and in particular to support dynamic load.
+ (issue 16652)
- Fixed a bug in the form submission logic of the SMTP authentation configuration.
- (report)
-
- Hudson shouldn't store SMTP auth password in a clear text.
- (report)
-
- Improved the form validation in global e-mail configurations.
- (report)
+ Errors in init.groovy halted startup; changed to just log a warning.
+ (issue 17933)
- Worked around a possible Windows slave hang on start up.
- (details)
-
- Inability to access hudson.dev.java.net shouldn't prevent Hudson from working.
- (issue 4590)
-
- Added a CLI command install-tool to invoke a tool auto-installation from Hudson CLI.
- (report)
-
- Added column on plugin updates page showing currently installed version.
-
- Build page now shows where the build was done.
- Job-enabling API should reject GET requests
- (issue 3721)
+ Windows services now auto-restart in case of abnormal process termination.
- Added an extension point for inserting actions across all projects without configuration.
- (report)
+ <f:dropdownDescriptorSelector> does not allow defaulting to specifig instance
+ (issue 17858)
- stdout, stderr, error details and the stack trace can be filtered out of the remote API
- representation of a test case with the depth parameter.
- (discussion)
-
- Self restart was not working on Solaris 64bit JVM.
-
- Fixed a possible NoSuchElementException in Hudson start up.
-
- "Redeploy Maven artifacts" GUI causes NPE.
-
- Permission check was missing for file mask validators.
-
- Fixed a problem regarding SCM plugin evolution and SCM browser settings, as observed in the Mercurial plugin.
- (issue 4514)
-
- Update center wasn't capable of updating bundled plugins, such as subversion.
-
- Fixed a problem in the up-to-date check of the plugin extraction.
- (issue 4353)
+ mark maven settings / global settings as default for new jobs
+ (issue 17723)
- Fixed a bug in the Debian package init script.
- (issue 4304)
+ Display Name is not shown.
+ (issue 17715)
+
+ Symlink handling problem with build permalinks on Windows.
+ (issue 17681)
- Fixed an NPE in MavenBuild$RunnerImpl.decideWorkspace
- (issue 4226)
+ List views missing a required field were unloadable.
+ (issue 15309)
- "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)
-
- 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)
-
- 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.
+ Maven module artifacts were not being deleted by the log rotator.
+ (issue 17508)
+
+ Properly find parent POMs when fingerprinting a Maven project.
+ (issue 17775)
- Javadoc location is now subject to the variable expansions.
- (issue 3942)
+ Allow the combination filter to accept parameter values.
+ (issue 7285)
- JNLP clients now report the reason when the connection is rejected by the master.
- (issue 3889)
+ Extension point to transform test names (for use with alternative JVM languages).
+ (issue 17478)
+ Added a new set-build-parameter command that can update a build variable from within a build.
+
+ Can use -Dhudson.udp=-1 to disable UDP broadcast without showing an ugly exception.
- 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)
-
- Default value for password parameter in a parameterized project was not saved.
- (issue 4333)
-
- Run sequentially option for Matrix project was not visible unless Axes was checked.
- (issue 4366)
-
- Fix launching Windows slave for slave name with space or other characters needed encoding.
- (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)
-
- Fixed links on age values in JUnit test reports.
- (issue 4402)
-
- Maven project POM parser now ignores empty modules or modules only containing whitespace,
- matching Maven's behavior.
- (issue 4442)
-
- Fixed setting of "blockBuildWhenUpstreamBuilding" for AbstractProject - wasn't being set at all, or displayed.
- (issue 4423)
-
- Handling of URLs with encoded character at end of a path component did not work in 1.323.
- (issue 4454)
-
- Fixed some field validators to work for values including + character.
+ Third-party license display for core was broken since 1.506.
+ (issue 17724)
+
+ Mitigation of exception from fingerprinting in a Maven project when a parent POM could not be located.
+ (issue 17775)
- Fixed the charset encoding handling when different encodings are involved between the master and slaves.
- (patch)
+ NPE from MatrixConfiguration.newBuild.
+ (issue 17728)
- Fixed a bug in the workspace archive support.
- (issue 4039)
+ Identify the short name of an uploaded plugin from the manifest, so it does not matter what the filename was.
+ (issue 4543)
+
- Added client-side validator for required fields and used this to replace some AJAX calls.
+ /about now links to license information for plugins as well.
- JNLP clients perform periodic ping to detect terminated connections and recover automatically.
- (report)
+ Updated bundled plugins.
+ Slave status monitor page shows when the data is last obtained
+
+ Delete button to highlight what it is going to delete.
- Creation of symlinks failed (or created in wrong location) since 1.320.
- (issue 4301)
-
- Fixed a NoClassDefFoundError problem that happens in remoting+maven+3rd plugin combo.
- report
-
- Raw console output was doing XML escaping for '&' and '<' but it shouldn't.
-
- Manually wiping out a workspace from GUI can cause NPE with some SCM plugins.
-
- Fixed ClassCastException in JavaMail with some application servers.
- (issue 1261)
-
- Fixed a bug in the tabular display of matrix projects.
- (issue 4245)
-
- Avoid division by zero error in swap space monitor.
- (issue 4284)
+ StringIndexOutOfBoundsException in PackageResult.findCorrespondingResult.
+ (issue 17721)
+
+ Breadcrumb is reworked to show descendants to provide additional navigational shortcuts.
+ (discussion)
- Avoid duplicate My Views links after Reload configuration from disk.
- (issue 4272)
+ hpi:run did not work for bundled plugins.
+ (issue 18352)
- Removed need for hack that lowered build health scores by one, so now 4/5 is reported as 80 instead of 79.
- (issue 4286)
+ Fixed CSRF vulnerabilities
+ (SECURITY-63,SECURITY-69)
- Fixed renaming a job to a name that includes a + character.
+ Fixed an XSS vulnerability via stylesheet
+ (SECURITY-67)
- Matrix project did not properly handle axis values with some special characters such as slash.
- (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)
-
- Maven incremental builds now rebuild failed/unstable modules from previous builds, even if they are unchanged.
- (issue 4152)
-
- Labels are listed in lexicographic order.
- (report)
-
- Labels for nodes are shown in a tag cloud style.
- (patch)
-
- Exposing load statistics to the remote API.
- (report)
+ Fixed an XSS vulnerability to copy arbitrary text into clipboard
+ (SECURITY-71/CVE-2013-1808)
+
- Make dynamic labelling of nodes clearer and easier to work with.
+ Views can now include jobs located within folders.
+ (pull 757)
- Plugins can mark themselves as incompatible with earlier versions to notify users during upgrade.
- (issue 4056)
+ Added confirmation dialog before reloading configuration from disk.
+ (issue 15340)
- Footer now includes a timestamp.
+ Switched confirmation before deleting jobs or wiping out workspace to a dialog.
- Advanced option now available for all project types to keep builds in queue while upstream projects are building. Off by default.
- (issue 1938)
+ Different text than “Build Now” for parameterized jobs.
+ (issue 10738)
- 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)
-
- Maven test failures will again properly mark a build as unstable,
- even if later task segments are successful.
- (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)
-
- Improved the debian package to set USER and HOME.
- (report)
-
- Failed to look up an e-mail address for LDAP users shouldn't cause a build to fail.
- (report)
-
- Fixed a possible NPE in Hudson.removeNode
- (report)
-
- 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)
-
- Form validation for the remote FS root of slaves was not functioning.
+ Check the view name with ajax.
- Privilege escalation on Solaris without username was not working.
+ “Build Now” context menu item broken for parameterized jobs.
+ (issue 17110)
- Hudson can make mistakes in binding plugins to their right /plugin/NAME/ URLs.
- (report)
+ Incorrect redirection after delete of job in folder in view.
+ (issue 17575)
- Hudson wasn't working on WebLogic on Windows.
- (report)
+ ”My Views" links leads to 404 Not Found.
+ (issue 17317)
- Fix New Job and Edit View links when default view is not "All" jobs.
- (issue 4212)
+ Quoting Issue with JDK Installer with Windows Installer.
+ (issue 5408)
- Revert logger settings when a log recorder is deleted.
- (issue 4201)
+ Restored compatibility in ArtifactArchiver signature; broken in 1.509 and could affect plugins.
+ (issue 17637)
- Add xml header on RSS/atom feeds and fix RSS URLs in header for non-default views.
- (issue 4080,
- issue 4081)
-
- 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)
-
- 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)
-
- Added BUILD_URL and JOB_URL to the exposed environment variables.
- (request)
-
- Added restart CLI command.
+ Fixed a bug in the logic that hides context menu anchor 'v'
+ (issue 13995)
- Fixed an encoding problem in CVS changelog calculation.
- (issue 3979)
-
- Environment variables are considered in test paths.
- (issue 3451)
-
- A failing test is recorded when JUnit XML is invalid
- (issue 3149)
-
- Fixed possible Unable to call getCredential. Invalid object ID race problem.
- (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)
-
- Fixed NPE in various Maven reporters caused by Hudson core problem.
- (issue 4192)
-
- Show warning if zero value entered for #builds/#days to save for discarding old builds
- (issue 4110)
-
- Added create-job CLI command.
+ JUnit result archiver should only fail builds if there are really no results - i.e. also no skipped tests.
+ (issue 7970)
+
+ NullPointerException related to lazy loading when loading some builds using fingerprinting.
+ (issue 16845)
- Hudson now tracks why a slave is put offline.
- (issue 2431)
-
- User-settable descriptions for tests.
-
- A history page with test count and duration charts.
- (issue 2228)
-
- A collapsible panel with test error details on the overview pages.
-
- Skipped tests counts are included in tables.
- (issue 1820)
-
- Improved the start up error handling with slave.jar -jnlpUrl option.
- (report)
+ UnsatisfiedLinkError on CreateSymbolicLinkw on Windows XP.
+ (issue 17343)
- Made hetero-list's include of descriptor config pages optional, so
- that descriptors without config.jelly files don't break page rendering.
- (See
- here for background.
+ Flyweight tasks should execute on the master if there's no static
+ executors available.
+ (issue 7291)
+
+ Download tool installations directly from the slave when possible, since this is much faster than going through the master.
+ (issue 17330)
- Moved Maven help files to maven-plugin.
- (issue 3527)
+ Improved UI for implicitly locked builds.
+ (issue 10197)
- Hudson shouldn't immediately launch a slave newly created via copy.
- report
-
- Added support for optional alternate Maven settings file for use
- in embedded Maven for POM parsing as well as actual Maven
- execution.
- (issue 2575)
-
- Added a test button to the PAM configuration to make sure Hudson has read access to
- /etc/shadow
- (report)
-
- Users can define their own views
-
- Added a /me url that points to the current user
-
- Added a new password parameter type to the parameterized builds.
- (report)
+ Incorrect URL computation broke context menu for computers with spaces in their names.
+ (issue 18236)
- Matrix projects can now run sequentially
- (issue 3028)
+ Promote the use of 'H' in cron.
+ (issue 17311)
- Hudson now allows builds of a single project to execute concurrently.
+ Context menu no longer automatically pops up
+ (issue 13995)
- Fixed a bug in inferring root DN in non-anonymous LDAP environment.
- (report)
-
- Fixed a MissingResourceException for "Ajp13Listener.ShortPacket"
- (issue 4053)
-
- Fixed 500 error when requesting the zip URL.
- (issue 4039)
-
- Debian package now has 750 permission on /var/run/hudson and /var/lib/hudson to make ssh work
- (issue 4047)
+ Better report file deletion failures.
+ (issue 17271)
- Fixed LinkageError in PAM authentication on Solaris.
- (issue 3546)
+ "Local to the workspace" repository locator does not work when building one module in isolation.
+ (issue 17331)
- Fixed a JDK path separator issue between Windows master and Unix slaves.
+ Master node mode not correctly displayed in /computer/(master)/configure.
+ (issue 17263)
+
+ Performance improvement in master/slave communication throughput
+ (issue 7813)
- Fixed a memory leak in the remoting layer.
- (issue 4045)
+ Quoted label expression can result into dead executors (throwing exception)
+ (issue 17128)
- Fixed Maven dependency build order logic.
- (issue 2736)
+ ChangeLog should produce some output even if some (plugin) annotator fails
+ (issue 17084)
- Renaming views and jobs, and deleting jobs should use POST instead of GET.
- (discussion)
-
- Improved the error diagnosis of "Processing failed due to a bug in the code"
-
- Slaves expose more information via the remote API now.
-
- Exported BUILD_ID to the remote API.
- (report)
-
- Don't let the slave TCP/IP connection port failure to prevent Hudson start up.
- (report)
-
- If the user sets up "Hudson's own" security realm, UI now asks the first admin user to be created.
-
- Windows service now does log rotation and handles whitespace in path correctly.
- (This is only applicable to newly installed services.)
- (report)
+ View name should not allow "..".
+ (issue 16608)
+ Show the reason for a skipped test if the test result contains one
+ (issue 8713)
+
- Matrix configuration should show a test trend.
- (issue 840)
-
- Fixed a possible NPE in installing Windows service.
- (report)
+ an in-progress build was dropped from JSON API when lazy-loading was introduced.
+ (issue 15583)
- Fixed a possible NPE in CrumbFilter.getCrumbIssuer.
- (issue 3986)
+ In-progress builds now survive the "reload from disk" administrator action.
+ (issue 3265)
- Login may result in 403 if the user realm is delegated to container.
- (issue 1235)
+ If artifact archiving failed with an I/O error, the build nonetheless was considered to be a success.
+ (issue 2058)
- The --logfile option stopped working on Windows.
- (issue 3272)
+ Fixed a bad interaction between Windows symlinks and build record lazy loading.
+ (issue 15587)
+
+ Remember the lastStable/Failed/Successful/etc builds to avoid eager loading builds.
+ (issue 16089)
- On-demand retention policy almost immediately turns off slaves.
- (issue 3972)
+ Wrong build result in post build steps after failed pre build step in maven projects.
+ (issue 17177)
+
+ Saving Global Jenkins Global Config wipes out the crumb issuer settings in the Global Security Config.
+ (issue 17087)
+
+ Made --httpKeepAliveTimeout option work (that was supposed to have been introduced in 1.503).
+ (issue 16474)
- Fixed "incomplete LifecycleExecutor" warning with Maven 2.2
- (issue 2373)
+ Preview function for textareas using Jenkins markup did not work when CSRF protection was enabled.
+ (issue 17085)
- Fixed a bug in Winstone that may result in "unable to create new native thread" error
- (report)
+ Permalinks created in the wrong place when using external build directories.
+ (issue 17137)
- Fixed a possible NPEs with the slavestatus plugin.
- (report)
+ External build directories not updated by job rename/delete.
+ (issue 17138)
- Fixed a possible StringIndexOutOfBoundsException with Windows process execution.
- (issue 4034)
+ JNA-related error from Windows slave monitoring thrown repeatedly.
+ (issue 15796)
- Fixed an NPE when environment variables are enabled but no variables are set.
- (issue 4032)
-
- Added ibm-web-bnd.xmi to simplify the automated deployment with WebSphere.
- (issue 3270)
-
- When deleting the workspace of a matrix project, do so for all configurations.
- (issue 3087)
-
- Enclose URLs in angle brackets when sending mail.
- (issue 3647)
-
- Plugins can now hide classes in the core so that they can ship their own versions.
- (report)
-
- The default view is now configurable.
+ New JSON library corrects problems such as form values starting with [.
+ (issue 14827)
- Tentatively added additional extension points to control queue behaviors.
- (patch)
+ Improved the request handling performance (where the file lookup is expensive, such as on NFS).
+ (issue 16606)
- Added support for proxy authentication on non-NTLM systems
- (issue 1920)
+ Windows symbolic support on Java5/6.
- Added MIME type mapping for several well-known file extensions so that it works everywhere.
- (issue 3803)
+ Improved the duration browsers cache static resources.
+ Exception in flyweight tasks when checking if an executor is interrupted.
+ (issue 17025)
+
+ JNA-related linkage errors on Windows not handled gracefully.
+ (issue 15466)
+
+ Builds disappear from build history after completion (revisited).
+ (issue 15156)
+
+ Added run display name as an environment variable when RunParameter is used
+ (pull 720)
- Hudson failed to notice a build result status change if aborted builds
- were in the middle.
- (report)
+ Fixed "Manage" sub-contextmenu for non-standalone deployments
+ (pull 721)
- TCP/IP hostname calculation of slaves may fail due in high latency network.
- (issue 3981)
+ Absolute URLs in console output
+ (issue 16368)
- Expose MAVEN_OPTS as env. var, in addition to set it to Maven JVM.
- (issue 3644)
+ Revert ampersand encoding which can cause backward incompatibility issue
+ (pull 683)
- Fixed winp.dll load problem on WebSphere
- (report)
+ Fix dependency graph computation when upstream build trigger is involved
+ (issue 13502)
- Subversion checkouts created files for symlinks
- (issue 3949)
-
- Hudson CLI now tries to connect to Hudson via plain TCP/IP, then fall back to tunneling over HTTP.
-
- Added ability to exclude by author and revision property with Subversion polling trigger.
-
- CLI slave agents show details of how it failed to connect.
- (report)
+ Disabled Authenticode verification for Windows services.
+ (issue 15596)
- Fixed a possible "Cannot create a file when that file already exists" error in managed Windows slave launcher.
- report
+ Not all log messages were being captured at /log/all.
+ (issue 16952)
- Made Hudson more robust in parsing CVS/Entries
- report
+ Incorrect or missing XML encoding declaration on some REST API pages.
+ (issue 16881)
- Fixed a regression in the groovy CLI command
- report
-
- Fixed regression in handling of usernames containing <, often used by Mercurial.
- (issue 3964)
-
- Allow Maven projects to have their own artifact archiver settings.
- (issue 3289)
-
+ Fixed: Human readable file size method returns ",00" for files with byte length 0
+ (issue 16630)
- Added copy-job, delete-job, enable-job, and disable-job command.
+ “Build” from job context menu produced a confusing warning page.
+ (issue 16844)
- Fixed a bug in the non-ASCII character handling in remote bulk file copy.
- (report)
+ Maven2 builds with non-standard test plugins failed.
+ (issue 16928)
- Supported self restart on all containers in Unix
- (report)
+ Started bundling XStream 1.4.4
+ (issue 12542)
- Added Retry Logic to SCM Checkout
-
- Fix bug in crumb validation when client is coming through a proxy.
- (issue 3854)
-
- Replaced "appears to be stuck" with an icon.
- (issue 3891)
-
- WebDAV deployment from Maven was failing with VerifyError.
-
- Subversion checkout/update gets in an infinite loop when a previously valid password goes invalid.
- (issue 2909)
-
- Gracefully handle IBM JVMs on PowerPC.
- (issue 3875)
+ ${ITEM_FULLNAME} variable was not working for Maven projects on Windows,
+ so introduced ${ITEM_FULL_NAME} instead.
+ (issue 12251)
- Fixed NPE with GlassFish v3 when CSRF protection is on.
- (issue 3878)
+ Lock contention issue in build history view.
+ (issue 16831)
- Fixed a bug in CLI where the state of command executions may interfere with each other.
+ Fixed the HTTP request thread saturation problem with Winstone.
+ (issue 16474)
- CLI should handle the situation gracefully if the server doesn't use crumb.
+ Script evaluation script error on IE.
+ (issue 16561)
- Fixed a performance problem in CLI execution.
+ surefire-reports not detected for android-maven-plugin
+ (issue 16776)
- Don't populate the default value of the Subversion local directory name.
- (report)
-
- Integrated SVNKit 1.3.0
-
- Implemented more intelligent polling assisted by commit-hook from SVN repository.
- (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)
-
- Set time out to avoid infinite hang when SMTP servers don't respond in time.
- (report)
-
+ maven-failsafe-plugin tests not recognized anymore
+ (issue 16696)
- Ant/Maven installers weren't setting the file permissions on Unix.
- (issue 3850)
+ UI waiting on a queue lock to display cause of queue blockage.
+ (issue 16833)
- Fixed cross-site scripting vulnerabilities, thanks to Steve Milner.
+ UpdateCenter REST API chokes if there was a plugin installation failure.
+ (issue 16836)
- Changing number of executors for master node required Hudson restart.
- (issue 3092)
+ Missing build title in /rssAll when build has no test result.
+ (issue 16770)
- Improved validation and help text regarding when number of executors setting may be zero.
- (issue 2110)
+ Changed the way matrix axis values are exposed as env variables
+ (issue 11577)
- NPE fix in the remote API if @xpath is used without @exclude.
- (patch)
-
- Expose the node name as 'NODE_NAME' environment varilable to build.
+ Maven 3 builds ignored quiet (-q) and debug (-X) options
+ (issue 16843)
- Added a CLI command to clear the job queue.
- (report)
+ JNLP slave installers can now work transparently with secured Jenkins.
+ (SECURITY-54 / despite the ticket marker, this is not a security vulnerability)
- Sundry improvements to automatic tool installation. You no longer need to configure an absolute tool home directory. Also some Unix-specific fixes.
-
- Generate nonce values to prevent cross site request forgeries. Extension point to customize the nonce generation algorithm.
-
- Reimplemented JDK auto installer to reduce Hudson footprint by 5MB. This also fix a failure to run on JBoss.
- (report)
+ "Discard old build records" behavior is now pluggable, allowing plugins to define custom logic.
+ Miscellaneous security vulnerability fixes. See the advisory for more details.
+ (SECURITY-13,16,46,47,54,55,59,60,61)
+
+ Builds disappear from build history after completion.
+ (issue 15156)
- Unit test trend graph was not displayed if there's no successful build.
- (report)
-
- init script ($HUDSON_HOME/init.groovy) is now run with uber-classloader.
-
- Maven2 projects may fail with "Cannot lookup required component".
- (issue 3706)
+ Plugin Manager’s Filter field did not work. Regression in 1.500.
+ (issue 16651)
- Toned down the form validation of JDK, Maven, Ant installations given that we can now auto-install them.
+ DISCOVER-able jobs break the build queue widget
+ (issue 16682)
- Ant can now be automatically installed from ant.apache.org.
+ Extension point to provide access to workspace even when node is offline
+ (issue 16454)
- Maven can now be automatically installed from maven.apache.org.
+ Extension point to listen BuildStep execution
- X-Hudson header was sent for all views, not just the top page.
- (report)
+ Plugin icons in the sidebar were not being properly cached.
+ (issue 16530)
+
+ Broadly as well as deeply nested build causes overwhelmed the UI after 1.482.
+ (issue 15747)
- Remote API served incorrect absolute URLs when Hudson is run behind a reverse proxy.
- (report)
+ API typo DependecyDeclarer corrected.
- Further improved the JUnit report parsing.
- (report)
+ Avoid eagerly loading builds in Changes in dependency or culprit list.
+ (pull 689)
- External job monitoring was ignoring the possible encoding difference between Hudson and the remote machine that executed the job.
- (report)
+ Run parameters do not support folders.
+ (issue 16462)
- Slave launch log was doing extra buffering that can prevent error logs (and so on) from showing up instantly.
- (report)
+ Fixed RememberMe cookie signature generation.
+ (issue 16278)
- Some failures in Windows batch files didn't cause Hudson to fail the build.
- (report)
+ Fixed NullPointerException when copying from existing Maven job
+ (issue 16499)
+
+ Display authorities at /user/* for convenience.
+ (pull 577)
- Fixed a bug that caused Hudson to delete slave workspaces too often.
- (issue 3653)
+ Slow rendering of view pages in large installations due to eager check whether the “People” link would show anything.
+ (issue 16244)
- If distributed build isn't enabled, slave selection drop-down shouldn't be displayed in the job config.
+ Reduced size of memory leak in render-on-demand functionality used e.g. in configuration pages.
+ (issue 16341)
- Added support for Svent 2.x SCM browsers.
- (issue 3357)
+ Improving responsiveness of People page.
+ (issue 16342)
+ (issue 16397)
- Fixed unexpanded rootURL in CLI.
- (report)
+ Exception printed to log while adding Build other projects post-build step.
+ (issue 16444)
- Trying to see the generated maven site results in 404.
- (issue 3497)
-
- Long lines in console output are now wrapped in most browsers.
-
- Hudson can now automatically install JDKs from java.sun.com
-
- The native m2 mode now works with Maven 2.1
- (issue 2373)
-
+ BindException when using --daemon with JMX.
+ (issue 14529)
- CLI didn't work with "java -jar hudson.war"
- (report)
+ Improved logging and error output from SSHD in Jenkins.
+
+ Linking to the /threadDump page from /systemInfo so it is discoverable.
+
+ Rekeying operation (from SECURITY-49 fix in 1.498) failed on Windows.
+ (issue 16319)
- Link to the jar file in the CLI usage page is made more robust.
- (issue 3621)
+ JNLP slave index page failed to explain how to pass -jnlpCredentials.
+ (issue 16273)
- "Build after other projects are built" wasn't loading the proper setting.
- (issue 3284)
-
- Hudson started as "java -jar hudson.war" can now restart itself on all Unix flavors.
-
- When run on GlassFish, Hudson configures GF correctly to handle URI encoding always in UTF-8
-
- Added a new extension point to contribute fragment to UDP-based auto discovery.
+ Links should preserve used protocol
+ (issue 16368)
- Rolled back changes for JENKINS-3580 - workspace is once again deleted on svn checkout.
- (issue 3580)
+ Don't report the same plugin twice in the update center if the filtering is in effect.
+
+ Accept any plugin with a 'test' goal as a test plugin in Maven jobs
+ (issue 8334)
+
+ Avoid unnecessary downloads if automatically installed tools are up-to-date
+ (issue 16215)
- Hudson now handles unexpected failures in trigger plugins more gracefully.
-
- Use 8K buffer to improve remote file copy performance.
- (issue 3524)
-
- Hudson now has a CLI
-
- Hudson's start up performance is improved by loading data concurrently.
+ The master key that was protecting all the sensitive data in $JENKINS_HOME was vulnerable.
+ (SECURITY-49)
- When a SCM plugin is uninstalled, projects using it should fall back to "No SCM".
-
- When polling SCMs, boolean parameter sets default value collectly.
-
- Sidebar build descriptions will not have "..." appended unless they have been truncated.
- (issue 3541)
-
- Workspace with symlink causes svn exception when updating externals.
- (issue 3532)
-
- Hudson now started bundling ssh-slaves plugin out of the box.
-
- Added an extension point to programmatically contribute a Subversion authentication credential.
- (report)
-
- 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)
-
- Preventive node monitoring like /tmp size check, swap space size check can be now disabled selectively.
- (issue 2596,
- issue 2552)
-
- Per-project read permission support.
- (issue 2324)
+ Delete the oldest build but it still come up on HistoryWidget
+ (issue 16194)
diff --git a/cli/pom.xml b/cli/pom.xml
index a5cbe8ccc93c4d1391f2be28d01ffbc72afa3056..f3a0e84999b0be23454770731e1a813573189737 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -5,7 +5,7 @@
pomorg.jenkins-ci.main
- 1.535-SNAPSHOT
+ 1.574-SNAPSHOTcli
@@ -13,6 +13,16 @@
Jenkins CLI
+
+ org.powermock
+ powermock-module-junit4
+ test
+
+
+ org.powermock
+ powermock-api-mockito
+ test
+ commons-codeccommons-codec
diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java
index 30d5fbc4c985216486a1240b4dd54fd774681e6c..bf75736560e51a82e6b81ebda5369c5c43f8b020 100644
--- a/cli/src/main/java/hudson/cli/CLI.java
+++ b/cli/src/main/java/hudson/cli/CLI.java
@@ -23,16 +23,14 @@
*/
package hudson.cli;
-import com.trilead.ssh2.crypto.PEMDecoder;
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.SocketInputStream;
+import hudson.remoting.SocketChannelStream;
import hudson.remoting.SocketOutputStream;
-import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
@@ -50,7 +48,6 @@ import java.io.Closeable;
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;
@@ -62,13 +59,10 @@ import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -79,8 +73,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
-import java.io.Console;
-
import static java.util.logging.Level.*;
/**
@@ -209,7 +201,7 @@ public class CLI {
} else {
s = new Socket();
s.connect(clip.endpoint,3000);
- out = new SocketOutputStream(s);
+ out = SocketChannelStream.out(s);
}
closables.add(new Closeable() {
@@ -218,7 +210,7 @@ public class CLI {
}
});
- Connection c = new Connection(new SocketInputStream(s),out);
+ Connection c = new Connection(SocketChannelStream.in(s),out);
switch (clip.version) {
case 1:
@@ -262,7 +254,7 @@ public class CLI {
/**
* If the server advertises CLI endpoint, returns its location.
*/
- private CliPort getCliTcpPort(String url) throws IOException {
+ protected CliPort getCliTcpPort(String url) throws IOException {
URL _url = new URL(url);
if (_url.getHost()==null || _url.getHost().length()==0) {
throw new IOException("Invalid URL: "+url);
@@ -284,6 +276,10 @@ public class CLI {
flushURLConnection(head);
if (p1==null && p2==null) {
+ // we aren't finding headers we are expecting. Is this even running Jenkins?
+ if (head.getHeaderField("X-Hudson")==null && head.getHeaderField("X-Jenkins")==null)
+ throw new IOException("There's no Jenkins running at "+url);
+
throw new IOException("No X-Jenkins-CLI2-Port among " + head.getHeaderFields().keySet());
}
@@ -307,10 +303,12 @@ public class CLI {
} catch (IOException e) {
try {
InputStream es = ((HttpURLConnection)conn).getErrorStream();
- while (es.read(buf) >= 0) {
- // Ignore
+ if (es!=null) {
+ while (es.read(buf) >= 0) {
+ // Ignore
+ }
+ es.close();
}
- es.close();
} catch (IOException ex) {
// Ignore
}
@@ -386,7 +384,7 @@ public class CLI {
public static int _main(String[] _args) throws Exception {
List args = Arrays.asList(_args);
- List candidateKeys = new ArrayList();
+ PrivateKeyProvider provider = new PrivateKeyProvider();
boolean sshAuthRequestedExplicitly = false;
String httpProxy=null;
@@ -426,17 +424,9 @@ public class CLI {
printUsage(Messages.CLI_NoSuchFileExists(f));
return -1;
}
- KeyPair kp;
- try {
- kp = loadKey(f);
- } catch (IOException e) {
- //if the PEM file is encrypted, IOException is thrown
- kp = tryEncryptedFile(f);
- } catch (GeneralSecurityException e) {
- throw new Exception("Failed to load key: "+f,e);
- }
- if(kp != null)
- candidateKeys.add(kp);
+
+ provider.readFrom(f);
+
args = args.subList(2,args.size());
sshAuthRequestedExplicitly = true;
continue;
@@ -457,8 +447,8 @@ public class CLI {
if(args.isEmpty())
args = Arrays.asList("help"); // default to help
- if (candidateKeys.isEmpty())
- addDefaultPrivateKeyLocations(candidateKeys);
+ if (!provider.hasKeys())
+ provider.readFromDefaultLocations();
CLIConnectionFactory factory = new CLIConnectionFactory().url(url).httpsProxyTunnel(httpProxy);
String userInfo = new URL(url).getUserInfo();
@@ -468,10 +458,10 @@ public class CLI {
CLI cli = factory.connect();
try {
- if (!candidateKeys.isEmpty()) {
+ if (provider.hasKeys()) {
try {
// TODO: server verification
- cli.authenticate(candidateKeys);
+ cli.authenticate(provider.getKeys());
} catch (IllegalStateException e) {
if (sshAuthRequestedExplicitly) {
System.err.println("The server doesn't support public key authentication");
@@ -523,103 +513,22 @@ public class CLI {
* Loads RSA/DSA private key in a PEM format into {@link KeyPair}.
*/
public static KeyPair loadKey(File f, String passwd) throws IOException, GeneralSecurityException {
- return loadKey(readPemFile(f), passwd);
+ return PrivateKeyProvider.loadKey(f, passwd);
}
public static KeyPair loadKey(File f) throws IOException, GeneralSecurityException {
- return loadKey(f, null);
- }
-
- private static String readPemFile(File f) throws IOException{
- FileInputStream is = new FileInputStream(f);
- try {
- DataInputStream dis = new DataInputStream(is);
- byte[] bytes = new byte[(int) f.length()];
- dis.readFully(bytes);
- dis.close();
- return new String(bytes);
- } finally {
- is.close();
- }
+ return loadKey(f, null);
}
-
+
/**
* Loads RSA/DSA private key in a PEM format into {@link KeyPair}.
*/
public static KeyPair loadKey(String pemString, String passwd) throws IOException, GeneralSecurityException {
- Object key = PEMDecoder.decode(pemString.toCharArray(), passwd);
- 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);
+ return PrivateKeyProvider.loadKey(pemString, passwd);
}
public static KeyPair loadKey(String pemString) throws IOException, GeneralSecurityException {
- return loadKey(pemString, null);
- }
-
- private static KeyPair tryEncryptedFile(File f) throws IOException, GeneralSecurityException{
- KeyPair kp = null;
- if(isPemEncrypted(f)){
- String passwd = askForPasswd(f.getCanonicalPath());
- kp = loadKey(f,passwd);
- }
- return kp;
- }
-
- private static boolean isPemEncrypted(File f) throws IOException{
- String pemString = readPemFile(f);
- //simple check if the file is encrypted
- return pemString.contains("4,ENCRYPTED");
- }
-
- @SuppressWarnings("Since15")
- @IgnoreJRERequirement
- private static String askForPasswd(String filePath){
- try {
- Console cons = System.console();
- String passwd = null;
- if (cons != null){
- char[] p = cons.readPassword("%s", "Enter passphrase for "+filePath+":");
- passwd = String.valueOf(p);
- }
- return passwd;
- } catch (LinkageError e) {
- throw new Error("Your private key is encrypted, but we need Java6 to ask you password safely",e);
- }
- }
-
- /**
- * 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);
- }
- }
- }
+ return loadKey(pemString, null);
}
/**
diff --git a/cli/src/main/java/hudson/cli/Connection.java b/cli/src/main/java/hudson/cli/Connection.java
index 232970869d95ae3b3206c60cfb3859eb5fd99a87..165a6deb7e6af83adf0ccda8cbe86a474414a65e 100644
--- a/cli/src/main/java/hudson/cli/Connection.java
+++ b/cli/src/main/java/hudson/cli/Connection.java
@@ -23,8 +23,7 @@
*/
package hudson.cli;
-import hudson.remoting.SocketInputStream;
-import hudson.remoting.SocketOutputStream;
+import hudson.remoting.SocketChannelStream;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
@@ -35,7 +34,6 @@ import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
@@ -64,7 +62,7 @@ public class Connection {
public final DataOutputStream dout;
public Connection(Socket socket) throws IOException {
- this(new SocketInputStream(socket),new SocketOutputStream(socket));
+ this(SocketChannelStream.in(socket),SocketChannelStream.out(socket));
}
public Connection(InputStream in, OutputStream out) {
@@ -128,7 +126,11 @@ public class Connection {
}
public byte[] readByteArray() throws IOException {
- byte[] buf = new byte[din.readInt()];
+ int bufSize = din.readInt();
+ if (bufSize < 0) {
+ throw new IOException("DataInputStream unexpectedly returned negative integer");
+ }
+ byte[] buf = new byte[bufSize];
din.readFully(buf);
return buf;
}
diff --git a/cli/src/main/java/hudson/cli/PrivateKeyProvider.java b/cli/src/main/java/hudson/cli/PrivateKeyProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..834bb7234250d7d7bac2bd424aba5aa670be0fda
--- /dev/null
+++ b/cli/src/main/java/hudson/cli/PrivateKeyProvider.java
@@ -0,0 +1,162 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2014 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson.cli;
+
+import static java.util.logging.Level.FINE;
+
+import java.io.Console;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Logger;
+
+import com.trilead.ssh2.crypto.PEMDecoder;
+
+/**
+ * Read DSA or RSA key from file(s) asking for password interactively.
+ *
+ * @author ogondza
+ * @since 1.556
+ */
+public class PrivateKeyProvider {
+
+ private List privateKeys = new ArrayList();
+
+ /**
+ * Get keys read so far.
+ *
+ * @return Possibly empty list. Never null.
+ */
+ public List getKeys() {
+ return Collections.unmodifiableList(privateKeys);
+ }
+
+ public boolean hasKeys() {
+ return !privateKeys.isEmpty();
+ }
+
+ /**
+ * Read keys from default keyFiles
+ *
+ * .ssh/id_rsa, .ssh/id_dsa and .ssh/identity.
+ *
+ * @return true if some key was read successfully.
+ */
+ public boolean readFromDefaultLocations() {
+ final File home = new File(System.getProperty("user.home"));
+
+ boolean read = false;
+ for (String path : new String[] {".ssh/id_rsa", ".ssh/id_dsa", ".ssh/identity"}) {
+ final File key = new File(home, path);
+ if (!key.exists()) continue;
+
+ try {
+
+ readFrom(key);
+ read = true;
+ } catch (IOException e) {
+
+ LOGGER.log(FINE, "Failed to load " + key, e);
+ } catch (GeneralSecurityException e) {
+
+ LOGGER.log(FINE, "Failed to load " + key, e);
+ }
+ }
+
+ return read;
+ }
+
+ /**
+ * Read key from keyFile.
+ */
+ public void readFrom(File keyFile) throws IOException, GeneralSecurityException {
+ final String password = isPemEncrypted(keyFile)
+ ? askForPasswd(keyFile.getCanonicalPath())
+ : null
+ ;
+ privateKeys.add(loadKey(keyFile, password));
+ }
+
+ private static boolean isPemEncrypted(File f) throws IOException{
+ //simple check if the file is encrypted
+ return readPemFile(f).contains("4,ENCRYPTED");
+ }
+
+ private static String askForPasswd(String filePath){
+ Console cons = System.console();
+ String passwd = null;
+ if (cons != null){
+ char[] p = cons.readPassword("%s", "Enter passphrase for " + filePath + ":");
+ passwd = String.valueOf(p);
+ }
+ return passwd;
+ }
+
+ public static KeyPair loadKey(File f, String passwd) throws IOException, GeneralSecurityException {
+ return loadKey(readPemFile(f), passwd);
+ }
+
+ private static String readPemFile(File f) throws IOException{
+ FileInputStream is = new FileInputStream(f);
+ try {
+ DataInputStream dis = new DataInputStream(is);
+ byte[] bytes = new byte[(int) f.length()];
+ dis.readFully(bytes);
+ dis.close();
+ return new String(bytes);
+ } finally {
+ is.close();
+ }
+ }
+
+ public static KeyPair loadKey(String pemString, String passwd) throws IOException, GeneralSecurityException {
+ Object key = PEMDecoder.decode(pemString.toCharArray(), passwd);
+ if (key instanceof com.trilead.ssh2.signature.RSAPrivateKey) {
+ com.trilead.ssh2.signature.RSAPrivateKey x = (com.trilead.ssh2.signature.RSAPrivateKey)key;
+
+ 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");
+
+ 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);
+ }
+
+ private static final Logger LOGGER = Logger.getLogger(PrivateKeyProvider.class.getName());
+}
diff --git a/cli/src/main/resources/hudson/cli/client/Messages_pt_BR.properties b/cli/src/main/resources/hudson/cli/client/Messages_pt_BR.properties
index 1131ec8d17d04bacd323a99eaa3669896088d60a..106ba7ce84decb70f0b7332eb3a0777a861905e3 100644
--- a/cli/src/main/resources/hudson/cli/client/Messages_pt_BR.properties
+++ b/cli/src/main/resources/hudson/cli/client/Messages_pt_BR.properties
@@ -1,6 +1,6 @@
# The MIT License
#
-# Copyright (c) 2004-2010, Sun Microsystems, Inc., Reginaldo L. Russinholi, Cleiber Silva
+# Copyright (c) 2004-2010, Sun Microsystems, Inc., Reginaldo L. Russinholi, Cleiber Silva, Fernando Boaglio
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -21,21 +21,21 @@
# THE SOFTWARE.
# 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 jenkins-cli.jar [-s URL] command [opts...] args...\n\
-# Options:\n\
+CLI.VersionMismatch=A vers\u00e3o n\u00e3o coincide. Esta CLI n\u00e3o pode funcionar com este servidor Hudson
+# Hudson 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\
+# \n\
+# The available commands depend on the server. Run the 'help' command to\n\
# see the list.
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 JENKINS_URL \ufffd usada)\n\
+ Uso: java -jar jenkins-cli.jar [-s URL] comando [op\u00e7\u00f5es...] par\u00e2metros...\n\
+ Op\u00e7\u00f5es:\n\
+ \ -s URL : a URL do servidor (por padr\u00e3o a vari\u00e1vel de ambiente JENKINS_URL \u00e9 usada)\n\
\n\
- Os comandos dispon\ufffdveis dependem do servidor. Execute o comando 'help' para\n\
+ Os comandos dispon\u00edveis dependem do servidor. Execute o comando 'help' para\n\
ver a lista.
# 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
+CLI.NoURL=N\u00e3o foi especificado nem '-s' e nem a vari\u00e1vel de ambiente JENKINS_URL
diff --git a/cli/src/test/java/hudson/cli/ConnectionMockTest.java b/cli/src/test/java/hudson/cli/ConnectionMockTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..77b319a4d249ee6856bdc60b66e5bd217c3829af
--- /dev/null
+++ b/cli/src/test/java/hudson/cli/ConnectionMockTest.java
@@ -0,0 +1,56 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2013 Ericsson
+ *
+ * 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.FastPipedInputStream;
+import hudson.remoting.FastPipedOutputStream;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.powermock.api.mockito.PowerMockito.*;
+
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+/**
+ * @author marco.miller@ericsson.com
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({Connection.class})
+public class ConnectionMockTest {
+
+ @Test
+ public void shouldTolerateEmptyByteArrayUponStreamZeroValue() throws IOException {
+ DataInputStream din = mock(DataInputStream.class);
+ //when(din.readInt()).thenReturn(0) does not work; mock always return 0 for some TBD reason
+ Connection c = new Connection(din, new FastPipedOutputStream(new FastPipedInputStream()));
+ assertTrue(c.readByteArray().length == 0);
+ }
+}
diff --git a/cli/src/test/java/hudson/cli/PrivateKeyProviderTest.java b/cli/src/test/java/hudson/cli/PrivateKeyProviderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..376b1fa815f3f62988f741c93a63f2f2d090563a
--- /dev/null
+++ b/cli/src/test/java/hudson/cli/PrivateKeyProviderTest.java
@@ -0,0 +1,140 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2014 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson.cli;
+
+import static org.mockito.Mockito.verify;
+import static org.powermock.api.mockito.PowerMockito.doReturn;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+import static org.powermock.api.mockito.PowerMockito.whenNew;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.util.Arrays;
+
+import org.hamcrest.Description;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(CLI.class) // When mocking new operator caller has to be @PreparedForTest, not class itself
+public class PrivateKeyProviderTest {
+
+ @Test
+ public void specifyKeysExplicitly() throws Exception {
+ final CLI cli = fakeCLI();
+
+ final File dsaKey = keyFile(".ssh/id_dsa");
+ final File rsaKey = keyFile(".ssh/id_rsa");
+
+ run("-i", dsaKey.getAbsolutePath(), "-i", rsaKey.getAbsolutePath(), "-s", "http://example.com");
+
+ verify(cli).authenticate(withKeyPairs(
+ keyPair(dsaKey),
+ keyPair(rsaKey)
+ ));
+ }
+
+ @Test
+ public void useDefaultKeyLocations() throws Exception {
+ final CLI cli = fakeCLI();
+
+ final File rsaKey = keyFile(".ssh/id_rsa");
+ final File dsaKey = keyFile(".ssh/id_dsa");
+
+ fakeHome();
+ run("-s", "http://example.com");
+
+ verify(cli).authenticate(withKeyPairs(
+ keyPair(rsaKey),
+ keyPair(dsaKey)
+ ));
+ }
+
+ private CLI fakeCLI() throws Exception {
+ final CLI cli = mock(CLI.class);
+
+ final CLIConnectionFactory factory = mock(CLIConnectionFactory.class, Mockito.CALLS_REAL_METHODS);
+ factory.jenkins = new URL("http://example.com");
+ doReturn(cli).when(factory).connect();
+
+ mockStatic(CLIConnectionFactory.class);
+ whenNew(CLIConnectionFactory.class).withNoArguments().thenReturn(factory);
+
+ return cli;
+ }
+
+ private void fakeHome() throws URISyntaxException {
+ final File home = new File(this.getClass().getResource(".ssh").toURI()).getParentFile();
+ System.setProperty("user.home", home.getAbsolutePath());
+ }
+
+ private int run(String... args) throws Exception {
+ return CLI._main(args);
+ }
+
+ private File keyFile(String name) throws URISyntaxException {
+ return new File(this.getClass().getResource(name).toURI());
+ }
+
+ private KeyPair keyPair(File file) throws IOException, GeneralSecurityException {
+ return PrivateKeyProvider.loadKey(file, null);
+ }
+
+ private Iterable withKeyPairs(final KeyPair... expected) {
+ return Mockito.argThat(new ArgumentMatcher>() {
+ @Override public void describeTo(Description description) {
+ description.appendText(Arrays.asList(expected).toString());
+ }
+
+ @Override public boolean matches(Object argument) {
+ if (!(argument instanceof Iterable)) throw new IllegalArgumentException("Not an instance of Iterrable");
+
+ @SuppressWarnings("unchecked")
+ final Iterable actual = (Iterable) argument;
+ int i = 0;
+ for (KeyPair akp: actual) {
+ if (!eq(expected[i].getPublic(), akp.getPublic())) return false;
+ if (!eq(expected[i].getPrivate(), akp.getPrivate())) return false;
+ i++;
+ }
+
+ return i == expected.length;
+ }
+
+ private boolean eq(final Key expected, final Key actual) {
+ return Arrays.equals(expected.getEncoded(), actual.getEncoded());
+ }
+ });
+ }
+}
diff --git a/cli/src/test/resources/hudson/cli/.ssh/id_dsa b/cli/src/test/resources/hudson/cli/.ssh/id_dsa
new file mode 100644
index 0000000000000000000000000000000000000000..be556daa6d02984101686a02939b6777c780db86
--- /dev/null
+++ b/cli/src/test/resources/hudson/cli/.ssh/id_dsa
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBugIBAAKBgQCA9mMzB1O52hpObIyaJXgFJQUmc1HV0NEJXsFFGh8U2l0Tkgv4
+fp3MWadiAMmc5H1ot4KQLXl7SwU7dHCCFcGcfQiOjeD5rWeZuHoPAJSDMilcJGE3
+Xo2C+wlescTByEgRRA16vdSlNaDJXKVxq9Wr59G8P4JC6/5EvpeypgYdTQIVAMTf
+aC0O2EGLnJrNBsUdc1s+iUp9AoGAZA7pZYPMJHJWTanJb2DlWHn/QM63jfh38N6W
+ERzmQQks6QdS7UkFlg9cbVGUtn0Yz2SfX3VKiMXNMkAdGD8loBcJS5w6oMMU7rcj
+lldRQ63+fMgdVZYMF5bchC6RhQeGZQ8Imf2iFF28SsE4bi+K12HYgIO5bFxPFUTH
+WSWsMLcCgYBgHJ90ZLU400axB5P0qw/0s4arPD0g53Vzi/Y2h5TJr3KPF2sEIbAc
+2gpFEzUNY0hvH6REKJ+VPPUvlH6ieaXomW8pSGjv4SdxZhJRrDe+Ac/xQse1QdYx
+uWJzpVm3cIGfqLxmQnrklnutI/1F62VZQlq9vjiZL7ir/00vdUTYHwIUUkttGGgl
+a0rWLzPTPF4X4lZfFhk=
+-----END DSA PRIVATE KEY-----
diff --git a/cli/src/test/resources/hudson/cli/.ssh/id_dsa.pub b/cli/src/test/resources/hudson/cli/.ssh/id_dsa.pub
new file mode 100644
index 0000000000000000000000000000000000000000..4a42a845727297abcfa1c1204d0a270392c5a9a6
--- /dev/null
+++ b/cli/src/test/resources/hudson/cli/.ssh/id_dsa.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAID2YzMHU7naGk5sjJoleAUlBSZzUdXQ0QlewUUaHxTaXROSC/h+ncxZp2IAyZzkfWi3gpAteXtLBTt0cIIVwZx9CI6N4PmtZ5m4eg8AlIMyKVwkYTdejYL7CV6xxMHISBFEDXq91KU1oMlcpXGr1avn0bw/gkLr/kS+l7KmBh1NAAAAFQDE32gtDthBi5yazQbFHXNbPolKfQAAAIBkDullg8wkclZNqclvYOVYef9AzreN+Hfw3pYRHOZBCSzpB1LtSQWWD1xtUZS2fRjPZJ9fdUqIxc0yQB0YPyWgFwlLnDqgwxTutyOWV1FDrf58yB1VlgwXltyELpGFB4ZlDwiZ/aIUXbxKwThuL4rXYdiAg7lsXE8VRMdZJawwtwAAAIBgHJ90ZLU400axB5P0qw/0s4arPD0g53Vzi/Y2h5TJr3KPF2sEIbAc2gpFEzUNY0hvH6REKJ+VPPUvlH6ieaXomW8pSGjv4SdxZhJRrDe+Ac/xQse1QdYxuWJzpVm3cIGfqLxmQnrklnutI/1F62VZQlq9vjiZL7ir/00vdUTYHw== ogondza@localhost.localdomain
diff --git a/cli/src/test/resources/hudson/cli/.ssh/id_rsa b/cli/src/test/resources/hudson/cli/.ssh/id_rsa
new file mode 100644
index 0000000000000000000000000000000000000000..ee2ad6b569a3b5ebbea0d5d1bfcf6ed2bd0dbe27
--- /dev/null
+++ b/cli/src/test/resources/hudson/cli/.ssh/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAyTqwFqp5Ww2Tr/52D7hhdOwgzYGBUqxrOFopa+kjNEL1Yqwb
++mApUWZ+D3zN9PurhUcVUfeYVXiYWFJ0kG72HIJawL/0BR5oYxRJfumK8Z/sAzAL
+xdhc5O5twETrr9gU3cxtvF5oJNP0I9HickAOeC+ZNpiDIIblrhvxXl/QwqrR+/Gv
+Nb8TApj+rxXEfNp+N69iGnnxzWn1FeKeOAWpwoBAxZNoqBQAFacF7xfQnoygyekC
+xk+ts2O5Zzv8iJ10sVf+x2Q79rxAtsc0xOGhZbBAzbmFTz0PE4iWuo/Vo1c6mM7u
+/dam+FxB2NqPNw7W+4eiCnEVkiQZlrxmuGvK7wIDAQABAoIBACml1+QZDFzoBnUa
+eVzvkFwesvtVnmp5/QcAwinvarXaVedCL9g2JtcOG3EhJ49YtzsyZxs7329xMja1
+eiKalJ157UaPc/XLQVegT0XRGEzCCJrwSr979F39awGsQgt28XqmYN/nui5FH/Z5
+7iAvWc9OKqu+DQWiZc8PQXmC4zYmvhGQ8vKx44RSqlWCjd9IqBVhpE5gxpI/SmCx
+umUNNtoH0hBWr+MsVHzr6UUrC3a99+7bB4We8XMXXFLzbTUSgiYFmK+NxPs/Fux/
+IAyXAMbDw2HeqZ7g4kTaf4cvmVOwhh4zlvB4p7j301LdO1jmvs9z0fn/QJcTpVM7
+ISMKwAECgYEA/uKVdmOKTk3dKzKRFXtWJjqypOXakoX+25lUcVv2PXYRr8Sln9jC
+A13fbhvwq+FqbdnNlB23ag5niCVLfUpB1DYYP5jd4lU8D6HZQiHlmokB6nLT9NIW
+iTcG88E58Bta/l1Ue5Yn+LqluBC4i289wFbH1kZyxQ565s5dJEv9uAECgYEAyhwF
+ZOqTK2lZe5uuN4owVLQaYFj9fsdFHULzlK/UAtkG1gCJhjBmwSEpZFFMH6WgwHk5
+SHJEom0uB4qRv8gQcxl9OSiDsp56ymr0NBhlPVXWr6IzLotLy5XBC1muqvYYlj7E
+kHgSet/h8RUM/FeEiwOFHDU2DkMb8Qx1hfMdAu8CgYBSEsYL9CuB4WK5WTQMlcV8
+0+PYY0dJbSpOrgXZ5sHYsp8pWQn3+cUnbl/WxdpujkxGCR9AdX0tAmxmE5RGSNX/
+rleKiv/PtKB9bCFYQS/83ecnBkioCcpF7tknPm4YmcZoJ8dfcE94sSlRpti11WEu
+AQOiRNcKCwqaLZMib/HIAQKBgQCdiOffeERMYypfgcJzAiCX9WZV0SeOCS7jFwub
+ys17hsSgS/zl/pYpVXrY+dFXHZfGTvcKdB7xaB6nvCfND9lajfSgd+bndEYLvwAo
+Fxfajizv64LvdZ4XytuUyEuwcHBLtBMs9Jqa8iU/8AOWMXVbkdvQV92RkleWNPrp
+9MyZOwKBgQD9x8MnX5LVBfQKuL9qX6l9Da06EyMkzfz3obKn9AAJ3Xj9+45TNPJu
+HnZyvJWesl1vDjXQTm+PVkdyE0WQgoiVX+wxno0hsoly5Uqb5EYHtTUrZzRpkyLK
+1VmtDxT5D8gorUgn6crzk4PKaxRkPfAimZdlkQm6iOtuR3kqn5BtIQ==
+-----END RSA PRIVATE KEY-----
diff --git a/cli/src/test/resources/hudson/cli/.ssh/id_rsa.pub b/cli/src/test/resources/hudson/cli/.ssh/id_rsa.pub
new file mode 100644
index 0000000000000000000000000000000000000000..91f8ff7180e5f05f989b9aec2104d102372e88a4
--- /dev/null
+++ b/cli/src/test/resources/hudson/cli/.ssh/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJOrAWqnlbDZOv/nYPuGF07CDNgYFSrGs4Wilr6SM0QvVirBv6YClRZn4PfM30+6uFRxVR95hVeJhYUnSQbvYcglrAv/QFHmhjFEl+6Yrxn+wDMAvF2Fzk7m3AROuv2BTdzG28Xmgk0/Qj0eJyQA54L5k2mIMghuWuG/FeX9DCqtH78a81vxMCmP6vFcR82n43r2IaefHNafUV4p44BanCgEDFk2ioFAAVpwXvF9CejKDJ6QLGT62zY7lnO/yInXSxV/7HZDv2vEC2xzTE4aFlsEDNuYVPPQ8TiJa6j9WjVzqYzu791qb4XEHY2o83Dtb7h6IKcRWSJBmWvGa4a8rv your_email@example.com
diff --git a/core/pom.xml b/core/pom.xml
index 1efb9691848e6bdd42272dccbafe0c95b4edbc4b..d2c68f4b9decfc86e63e53cc57a6e6e6ef297321 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -29,7 +29,7 @@ THE SOFTWARE.
org.jenkins-ci.mainpom
- 1.535-SNAPSHOT
+ 1.574-SNAPSHOT../pom.xml
@@ -42,7 +42,7 @@ THE SOFTWARE.
true
- 1.218
+ 1.2272.5.6.SEC031.8.9
@@ -112,7 +112,7 @@ THE SOFTWARE.
org.jenkins-citrilead-ssh2
- build214-jenkins-3
+ build217-jenkins-5org.kohsuke.stapler
@@ -161,7 +161,7 @@ THE SOFTWARE.
org.kohsuke.staplerstapler-adjunct-codemirror
- 1.2
+ 1.3org.kohsuke.stapler
@@ -174,7 +174,7 @@ THE SOFTWARE.
com.infradna.toolbridge-method-annotation
- 1.8
+ 1.9
@@ -194,17 +194,17 @@ THE SOFTWARE.
org.jenkins-ciannotation-indexer
- 1.4
+ 1.7org.jenkins-cibytecode-compatibility-transformer
- 1.3
+ 1.5
- org.jvnet.hudson
+ org.jenkins-citask-reactor
- 1.2
+ 1.4org.jvnet.localizer
@@ -219,7 +219,17 @@ THE SOFTWARE.
org.jvnet.hudsonxstream
- 1.4.4-jenkins-4
+ 1.4.7-jenkins-1
+
+
+ xmlpull
+ xmlpull
+
+
+ xpp3
+ xpp3_min
+
+ jfree
@@ -469,12 +479,12 @@ THE SOFTWARE.
org.jvnet.winpwinp
- 1.16
+ 1.20org.jenkins-cimemory-monitor
- 1.7
+ 1.8org.codehaus.woodstox
@@ -517,14 +527,9 @@ THE SOFTWARE.
1.9
- org.jenkins-ci
- jinterop-wmi
- 1.1
-
-
- org.jenkins-ci
- windows-remote-command
- 1.4
+ org.kohsuke.jinterop
+ j-interop
+ 2.0.6-kohsuke-1org.kohsuke.metainf-services
@@ -558,12 +563,6 @@ THE SOFTWARE.
provided
-
- org.kohsuke
- owasp-html-sanitizer
- r88
-
-
org.mindrotjbcrypt
@@ -583,6 +582,12 @@ THE SOFTWARE.
com.google.guavaguava
+
+
+ com.jcraft
+ jzlib
+ 1.1.3-kohsuke-1
+
@@ -634,7 +639,6 @@ THE SOFTWARE.
com.infradna.toolbridge-method-injector
- 1.8
@@ -722,7 +726,7 @@ THE SOFTWARE.
com.sun.winswwinsw
- 1.13
+ 1.16binexe${project.build.outputDirectory}/windows-service
@@ -781,7 +785,7 @@ THE SOFTWARE.
org.kohsuke.staplermaven-stapler-plugin
- 1.16
+ ${maven-stapler-plugin.version}/lib/.*
diff --git a/core/src/main/groovy/hudson/util/LoadMonitor.groovy b/core/src/main/groovy/hudson/util/LoadMonitor.groovy
index 804094d2a4560aacbb2f186f42b42bd3aa309bc1..97d701816199c011e67c6fcc39e539563442f225 100644
--- a/core/src/main/groovy/hudson/util/LoadMonitor.groovy
+++ b/core/src/main/groovy/hudson/util/LoadMonitor.groovy
@@ -24,6 +24,7 @@
package hudson.util
import hudson.model.Computer
+import jenkins.util.Timer
import jenkins.model.Jenkins
import hudson.model.Label
import hudson.model.Queue.BlockedItem
@@ -31,7 +32,7 @@ import hudson.model.Queue.BuildableItem
import hudson.model.Queue.WaitingItem
import hudson.triggers.SafeTimerTask
import java.text.DateFormat
-import hudson.triggers.Trigger;
+import java.util.concurrent.TimeUnit
/**
* Spits out the load information.
@@ -51,7 +52,7 @@ public class LoadMonitorImpl extends SafeTimerTask {
this.dataFile = dataFile;
labels = Jenkins.getInstance().labels*.name;
printHeaders();
- Trigger.timer.scheduleAtFixedRate(this,0,10*1000);
+ Timer.get().scheduleAtFixedRate(this,0,10*1000, TimeUnit.MILLISECONDS);
}
private String quote(Object s) { "\"${s}\""; }
diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java
index e4d158128cf731d42d0bfd220e0fafdeb6e4ed0c..ba123fc95b09f870bdc3a2ba148d48627a3bb008 100644
--- a/core/src/main/java/hudson/ClassicPluginStrategy.java
+++ b/core/src/main/java/hudson/ClassicPluginStrategy.java
@@ -27,16 +27,15 @@ import com.google.common.collect.Lists;
import hudson.Plugin.DummyImpl;
import hudson.PluginWrapper.Dependency;
import hudson.model.Hudson;
+import jenkins.util.AntClassLoader;
import hudson.util.CyclicGraphDetector;
import hudson.util.CyclicGraphDetector.CycleDetectedException;
-import hudson.util.IOException2;
import hudson.util.IOUtils;
import hudson.util.MaskingClassLoader;
import hudson.util.VersionNumber;
import jenkins.ClassLoaderReflectionToolkit;
import jenkins.ExtensionFilter;
import org.apache.commons.io.output.NullOutputStream;
-import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Expand;
@@ -57,7 +56,6 @@ import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
@@ -68,12 +66,13 @@ import java.util.HashSet;
import java.util.List;
import java.util.Vector;
import java.util.jar.Attributes;
+import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
+import org.jenkinsci.bytecode.Transformer;
public class ClassicPluginStrategy implements PluginStrategy {
- private final ClassLoaderReflectionToolkit clt = new ClassLoaderReflectionToolkit();
/**
* Filter for jar files.
@@ -95,15 +94,26 @@ public class ClassicPluginStrategy implements PluginStrategy {
this.pluginManager = pluginManager;
}
- public PluginWrapper createPluginWrapper(File archive) throws IOException {
- final Manifest manifest;
- URL baseResourceURL;
+ @Override public String getShortName(File archive) throws IOException {
+ Manifest manifest;
+ if (isLinked(archive)) {
+ manifest = loadLinkedManifest(archive);
+ } else {
+ JarFile jf = new JarFile(archive, false);
+ try {
+ manifest = jf.getManifest();
+ } finally {
+ jf.close();
+ }
+ }
+ return PluginWrapper.computeShortName(manifest, archive);
+ }
- File expandDir = null;
- // if .hpi, this is the directory where war is expanded
+ private static boolean isLinked(File archive) {
+ return archive.getName().endsWith(".hpl") || archive.getName().endsWith(".jpl");
+ }
- boolean isLinked = archive.getName().endsWith(".hpl") || archive.getName().endsWith(".jpl");
- if (isLinked) {
+ private static Manifest loadLinkedManifest(File archive) throws IOException {
// resolve the .hpl file to the location of the manifest file
final String firstLine = IOUtils.readFirstLine(new FileInputStream(archive), "UTF-8");
if (firstLine.startsWith("Manifest-Version:")) {
@@ -115,12 +125,24 @@ public class ClassicPluginStrategy implements PluginStrategy {
// then parse manifest
FileInputStream in = new FileInputStream(archive);
try {
- manifest = new Manifest(in);
+ return new Manifest(in);
} catch (IOException e) {
- throw new IOException2("Failed to load " + archive, e);
+ throw new IOException("Failed to load " + archive, e);
} finally {
in.close();
}
+ }
+
+ @Override public PluginWrapper createPluginWrapper(File archive) throws IOException {
+ final Manifest manifest;
+ URL baseResourceURL;
+
+ File expandDir = null;
+ // if .hpi, this is the directory where war is expanded
+
+ boolean isLinked = isLinked(archive);
+ if (isLinked) {
+ manifest = loadLinkedManifest(archive);
} else {
if (archive.isDirectory()) {// already expanded
expandDir = archive;
@@ -272,7 +294,11 @@ public class ClassicPluginStrategy implements PluginStrategy {
new DetachedPlugin("external-monitor-job","1.467.*","1.0"),
new DetachedPlugin("ldap","1.467.*","1.0"),
new DetachedPlugin("pam-auth","1.467.*","1.0"),
- new DetachedPlugin("mailer","1.493.*","1.2")
+ new DetachedPlugin("mailer","1.493.*","1.2"),
+ new DetachedPlugin("matrix-auth","1.535.*","1.0.2"),
+ new DetachedPlugin("windows-slaves","1.547.*","1.0"),
+ new DetachedPlugin("antisamy-markup-formatter","1.553.*","1.0"),
+ new DetachedPlugin("matrix-project","1.561.*","1.0")
);
/**
@@ -349,13 +375,13 @@ public class ClassicPluginStrategy implements PluginStrategy {
}
wrapper.setPlugin((Plugin) o);
} catch (LinkageError e) {
- throw new IOException2("Unable to load " + className + " from " + wrapper.getShortName(),e);
+ throw new IOException("Unable to load " + className + " from " + wrapper.getShortName(),e);
} catch (ClassNotFoundException e) {
- throw new IOException2("Unable to load " + className + " from " + wrapper.getShortName(),e);
+ throw new IOException("Unable to load " + className + " from " + wrapper.getShortName(),e);
} catch (IllegalAccessException e) {
- throw new IOException2("Unable to create instance of " + className + " from " + wrapper.getShortName(),e);
+ throw new IOException("Unable to create instance of " + className + " from " + wrapper.getShortName(),e);
} catch (InstantiationException e) {
- throw new IOException2("Unable to create instance of " + className + " from " + wrapper.getShortName(),e);
+ throw new IOException("Unable to create instance of " + className + " from " + wrapper.getShortName(),e);
}
}
@@ -366,7 +392,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
startPlugin(wrapper);
} catch(Throwable t) {
// gracefully handle any error in plugin.
- throw new IOException2("Failed to initialize",t);
+ throw new IOException("Failed to initialize",t);
}
} finally {
Thread.currentThread().setContextClassLoader(old);
@@ -377,6 +403,36 @@ public class ClassicPluginStrategy implements PluginStrategy {
plugin.getPlugin().start();
}
+ @Override
+ public void updateDependency(PluginWrapper depender, PluginWrapper dependee) {
+ DependencyClassLoader classLoader = findAncestorDependencyClassLoader(depender.classLoader);
+ if (classLoader != null) {
+ classLoader.updateTransientDependencies();
+ LOGGER.log(Level.INFO, "Updated dependency of {0}", depender.getShortName());
+ }
+ }
+
+ private DependencyClassLoader findAncestorDependencyClassLoader(ClassLoader classLoader)
+ {
+ for (; classLoader != null; classLoader = classLoader.getParent()) {
+ if (classLoader instanceof DependencyClassLoader) {
+ return (DependencyClassLoader)classLoader;
+ }
+
+ if (classLoader instanceof AntClassLoader) {
+ // AntClassLoaders hold parents not only as AntClassLoader#getParent()
+ // but also as AntClassLoader#getConfiguredParent()
+ DependencyClassLoader ret = findAncestorDependencyClassLoader(
+ ((AntClassLoader)classLoader).getConfiguredParent()
+ );
+ if (ret != null) {
+ return ret;
+ }
+ }
+ }
+ return null;
+ }
+
private static File resolve(File base, String relative) {
File rel = new File(relative);
if(rel.isAbsolute())
@@ -426,7 +482,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
unzipExceptClasses(archive, destDir, prj);
createClassJarFromWebInfClasses(archive, destDir, prj);
} catch (BuildException x) {
- throw new IOException2("Failed to expand " + archive,x);
+ throw new IOException("Failed to expand " + archive,x);
}
try {
@@ -440,7 +496,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
* Repackage classes directory into a jar file to make it remoting friendly.
* The remoting layer can cache jar files but not class files.
*/
- private static void createClassJarFromWebInfClasses(File archive, File destDir, Project prj) {
+ private static void createClassJarFromWebInfClasses(File archive, File destDir, Project prj) throws IOException {
File classesJar = new File(destDir, "WEB-INF/lib/classes.jar");
ZipFileSet zfs = new ZipFileSet();
@@ -457,31 +513,36 @@ public class ClassicPluginStrategy implements PluginStrategy {
mapper.add(gm);
final long dirTime = archive.lastModified();
- Zip z = new Zip() {
- /**
- * Forces the fixed timestamp for directories to make sure
- * classes.jar always get a consistent checksum.
- */
- protected void zipDir(Resource dir, ZipOutputStream zOut, String vPath,
- int mode, ZipExtraField[] extra)
- throws IOException {
-
- ZipOutputStream wrapped = new ZipOutputStream(new NullOutputStream()) {
- @Override
- public void putNextEntry(ZipEntry ze) throws IOException {
- ze.setTime(dirTime+1999); // roundup
- super.putNextEntry(ze);
- }
- };
- super.zipDir(dir,wrapped,vPath,mode,extra);
+ // this ZipOutputStream is reused and not created for each directory
+ final ZipOutputStream wrappedZOut = new ZipOutputStream(new NullOutputStream()) {
+ @Override
+ public void putNextEntry(ZipEntry ze) throws IOException {
+ ze.setTime(dirTime+1999); // roundup
+ super.putNextEntry(ze);
}
};
- z.setProject(prj);
- z.setTaskType("zip");
- classesJar.getParentFile().mkdirs();
- z.setDestFile(classesJar);
- z.add(mapper);
- z.execute();
+ try {
+ Zip z = new Zip() {
+ /**
+ * Forces the fixed timestamp for directories to make sure
+ * classes.jar always get a consistent checksum.
+ */
+ protected void zipDir(Resource dir, ZipOutputStream zOut, String vPath,
+ int mode, ZipExtraField[] extra)
+ throws IOException {
+ // use wrappedZOut instead of zOut
+ super.zipDir(dir,wrappedZOut,vPath,mode,extra);
+ }
+ };
+ z.setProject(prj);
+ z.setTaskType("zip");
+ classesJar.getParentFile().mkdirs();
+ z.setDestFile(classesJar);
+ z.add(mapper);
+ z.execute();
+ } finally {
+ wrappedZOut.close();
+ }
}
private static void unzipExceptClasses(File archive, File destDir, Project prj) {
@@ -518,6 +579,11 @@ public class ClassicPluginStrategy implements PluginStrategy {
this.dependencies = dependencies;
}
+ private void updateTransientDependencies() {
+ // This will be recalculated at the next time.
+ transientDependencies = null;
+ }
+
private List getTransitiveDependencies() {
if (transientDependencies==null) {
CyclicGraphDetector cgd = new CyclicGraphDetector() {
@@ -562,10 +628,10 @@ public class ClassicPluginStrategy implements PluginStrategy {
if (PluginManager.FAST_LOOKUP) {
for (PluginWrapper pw : getTransitiveDependencies()) {
try {
- Class c = clt.findLoadedClass(pw.classLoader,name);
+ Class> c = ClassLoaderReflectionToolkit._findLoadedClass(pw.classLoader, name);
if (c!=null) return c;
- return clt.findClass(pw.classLoader,name);
- } catch (InvocationTargetException e) {
+ return ClassLoaderReflectionToolkit._findClass(pw.classLoader, name);
+ } catch (ClassNotFoundException e) {
//not found. try next
}
}
@@ -589,15 +655,11 @@ public class ClassicPluginStrategy implements PluginStrategy {
HashSet result = new HashSet();
if (PluginManager.FAST_LOOKUP) {
- try {
for (PluginWrapper pw : getTransitiveDependencies()) {
- Enumeration urls = clt.findResources(pw.classLoader, name);
+ Enumeration urls = ClassLoaderReflectionToolkit._findResources(pw.classLoader, name);
while (urls != null && urls.hasMoreElements())
result.add(urls.nextElement());
}
- } catch (InvocationTargetException e) {
- throw new Error(e);
- }
} else {
for (Dependency dep : dependencies) {
PluginWrapper p = pluginManager.getPlugin(dep.shortName);
@@ -615,14 +677,10 @@ public class ClassicPluginStrategy implements PluginStrategy {
@Override
protected URL findResource(String name) {
if (PluginManager.FAST_LOOKUP) {
- try {
for (PluginWrapper pw : getTransitiveDependencies()) {
- URL url = clt.findResource(pw.classLoader,name);
+ URL url = ClassLoaderReflectionToolkit._findResource(pw.classLoader, name);
if (url!=null) return url;
}
- } catch (InvocationTargetException e) {
- throw new Error(e);
- }
} else {
for (Dependency dep : dependencies) {
PluginWrapper p = pluginManager.getPlugin(dep.shortName);
@@ -639,8 +697,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
}
/**
- * {@link AntClassLoader} with a few methods exposed and {@link Closeable} support.
- * Deprecated as of Java 7, retained only for Java 5/6.
+ * {@link AntClassLoader} with a few methods exposed, {@link Closeable} support, and {@link Transformer} support.
*/
private final class AntClassLoader2 extends AntClassLoader implements Closeable {
private final Vector pathComponents;
@@ -693,10 +750,13 @@ public class ClassicPluginStrategy implements PluginStrategy {
@Override
protected Class defineClassFromData(File container, byte[] classData, String classname) throws IOException {
- return super.defineClassFromData(container, pluginManager.getCompatibilityTransformer().transform(classname,classData), classname);
+ if (!DISABLE_TRANSFORMER)
+ classData = pluginManager.getCompatibilityTransformer().transform(classname, classData);
+ return super.defineClassFromData(container, classData, classname);
}
}
public static boolean useAntClassLoader = Boolean.getBoolean(ClassicPluginStrategy.class.getName()+".useAntClassLoader");
private static final Logger LOGGER = Logger.getLogger(ClassicPluginStrategy.class.getName());
+ public static boolean DISABLE_TRANSFORMER = Boolean.getBoolean(ClassicPluginStrategy.class.getName()+".noBytecodeTransformer");
}
diff --git a/core/src/main/java/hudson/DescriptorExtensionList.java b/core/src/main/java/hudson/DescriptorExtensionList.java
index 08c814e557db52702a2412b6ec71dd0324d244e0..a3917b2e84b02cd1f9c997f966b9d485977040bc 100644
--- a/core/src/main/java/hudson/DescriptorExtensionList.java
+++ b/core/src/main/java/hudson/DescriptorExtensionList.java
@@ -35,7 +35,6 @@ import hudson.util.Memoizer;
import hudson.util.Iterators.FlattenIterator;
import hudson.slaves.NodeDescriptor;
import hudson.tasks.Publisher;
-import hudson.tasks.Publisher.DescriptorExtensionListImpl;
import java.util.Collection;
import java.util.List;
@@ -44,6 +43,7 @@ import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.concurrent.CopyOnWriteArrayList;
+import javax.annotation.CheckForNull;
import org.kohsuke.stapler.Stapler;
import net.sf.json.JSONObject;
@@ -72,7 +72,7 @@ public class DescriptorExtensionList, D extends Descrip
public static ,D extends Descriptor>
DescriptorExtensionList createDescriptorList(Jenkins jenkins, Class describableType) {
if (describableType == (Class) Publisher.class) {
- return (DescriptorExtensionList) new DescriptorExtensionListImpl(jenkins);
+ return (DescriptorExtensionList) new Publisher.DescriptorExtensionListImpl(jenkins);
}
return new DescriptorExtensionList(jenkins,describableType);
}
@@ -146,7 +146,7 @@ public class DescriptorExtensionList, D extends Descrip
*
* If none is found, null is returned.
*/
- public D findByName(String id) {
+ public @CheckForNull D findByName(String id) {
for (D d : this)
if(d.getId().equals(id))
return d;
diff --git a/core/src/main/java/hudson/EnvVars.java b/core/src/main/java/hudson/EnvVars.java
index 0ad75f775d2b339ec5384433dddc4640c5d2b0b3..af8362eeb9a136a6cd877ce44f22feeb1db1c61a 100644
--- a/core/src/main/java/hudson/EnvVars.java
+++ b/core/src/main/java/hudson/EnvVars.java
@@ -26,13 +26,23 @@ package hudson;
import hudson.remoting.Callable;
import hudson.remoting.VirtualChannel;
import hudson.util.CaseInsensitiveComparator;
+import hudson.util.CyclicGraphDetector;
+import hudson.util.CyclicGraphDetector.CycleDetectedException;
+import hudson.util.VariableResolver;
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeMap;
import java.util.Arrays;
+import java.util.TreeSet;
import java.util.UUID;
+import java.util.logging.Logger;
/**
* Environment variables.
@@ -62,6 +72,7 @@ import java.util.UUID;
* @author Kohsuke Kawaguchi
*/
public class EnvVars extends TreeMap {
+ private static Logger LOGGER = Logger.getLogger(EnvVars.class.getName());
/**
* 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
@@ -148,6 +159,179 @@ public class EnvVars extends TreeMap {
return this;
}
+ /**
+ * Calculates the order to override variables.
+ *
+ * Sort variables with topological sort with their reference graph.
+ *
+ * This is package accessible for testing purpose.
+ */
+ static class OverrideOrderCalculator {
+ /**
+ * Extract variables referred directly from a variable.
+ */
+ private static class TraceResolver implements VariableResolver {
+ private final Comparator super String> comparator;
+ public Set referredVariables;
+
+ public TraceResolver(Comparator super String> comparator) {
+ this.comparator = comparator;
+ clear();
+ }
+
+ public void clear() {
+ referredVariables = new TreeSet(comparator);
+ }
+
+ public String resolve(String name) {
+ referredVariables.add(name);
+ return "";
+ }
+ }
+
+ private static class VariableReferenceSorter extends CyclicGraphDetector {
+ // map from a variable to a set of variables that variable refers.
+ private final Map> refereeSetMap;
+
+ public VariableReferenceSorter(Map> refereeSetMap) {
+ this.refereeSetMap = refereeSetMap;
+ }
+
+ @Override
+ protected Iterable extends String> getEdges(String n) {
+ // return variables referred from the variable.
+ if (!refereeSetMap.containsKey(n)) {
+ // there is a case a non-existing variable is referred...
+ return Collections.emptySet();
+ }
+ return refereeSetMap.get(n);
+ }
+ };
+
+ private final Comparator super String> comparator;
+
+ private final EnvVars target;
+ private final Map overrides;
+
+ private Map> refereeSetMap;
+ private List orderedVariableNames;
+
+ public OverrideOrderCalculator(EnvVars target, Map overrides) {
+ comparator = target.comparator();
+ this.target = target;
+ this.overrides = overrides;
+ scan();
+ }
+
+ public List getOrderedVariableNames() {
+ return orderedVariableNames;
+ }
+
+ // Cut the reference to the variable in a cycle.
+ private void cutCycleAt(String referee, List cycle) {
+ // cycle contains variables in referrer-to-referee order.
+ // This should not be negative, for the first and last one is same.
+ int refererIndex = cycle.lastIndexOf(referee) - 1;
+
+ assert(refererIndex >= 0);
+ String referrer = cycle.get(refererIndex);
+ boolean removed = refereeSetMap.get(referrer).remove(referee);
+ assert(removed);
+ LOGGER.warning(String.format("Cyclic reference detected: %s", Util.join(cycle," -> ")));
+ LOGGER.warning(String.format("Cut the reference %s -> %s", referrer, referee));
+ }
+
+ // Cut the variable reference in a cycle.
+ private void cutCycle(List cycle) {
+ // if an existing variable is contained in that cycle,
+ // cut the cycle with that variable:
+ // existing:
+ // PATH=/usr/bin
+ // overriding:
+ // PATH1=/usr/local/bin:${PATH}
+ // PATH=/opt/something/bin:${PATH1}
+ // then consider reference PATH1 -> PATH can be ignored.
+ for (String referee: cycle) {
+ if (target.containsKey(referee)) {
+ cutCycleAt(referee, cycle);
+ return;
+ }
+ }
+
+ // if not, cut the reference to the first one.
+ cutCycleAt(cycle.get(0), cycle);
+ }
+
+ /**
+ * Scan all variables and list all referring variables.
+ */
+ public void scan() {
+ refereeSetMap = new TreeMap>(comparator);
+ List extendingVariableNames = new ArrayList();
+
+ TraceResolver resolver = new TraceResolver(comparator);
+
+ for (Map.Entry entry: overrides.entrySet()) {
+ if (entry.getKey().indexOf('+') > 0) {
+ // XYZ+AAA variables should be always processed in last.
+ extendingVariableNames.add(entry.getKey());
+ continue;
+ }
+ resolver.clear();
+ Util.replaceMacro(entry.getValue(), resolver);
+
+ // Variables directly referred from the current scanning variable.
+ Set refereeSet = resolver.referredVariables;
+ // Ignore self reference.
+ refereeSet.remove(entry.getKey());
+ refereeSetMap.put(entry.getKey(), refereeSet);
+ }
+
+ VariableReferenceSorter sorter;
+ while(true) {
+ sorter = new VariableReferenceSorter(refereeSetMap);
+ try {
+ sorter.run(refereeSetMap.keySet());
+ } catch(CycleDetectedException e) {
+ // cyclic reference found.
+ // cut the cycle and retry.
+ @SuppressWarnings("unchecked")
+ List cycle = e.cycle;
+ cutCycle(cycle);
+ continue;
+ }
+ break;
+ }
+
+ // When A refers B, the last appearance of B always comes after
+ // the last appearance of A.
+ List reversedDuplicatedOrder = new ArrayList(sorter.getSorted());
+ Collections.reverse(reversedDuplicatedOrder);
+
+ orderedVariableNames = new ArrayList(overrides.size());
+ for(String key: reversedDuplicatedOrder) {
+ if(overrides.containsKey(key) && !orderedVariableNames.contains(key)) {
+ orderedVariableNames.add(key);
+ }
+ }
+ Collections.reverse(orderedVariableNames);
+ orderedVariableNames.addAll(extendingVariableNames);
+ }
+ }
+
+
+ /**
+ * Overrides all values in the map by the given map. Expressions in values will be expanded.
+ * See {@link #override(String, String)}.
+ * @return this
+ */
+ public EnvVars overrideExpandingAll(Map all) {
+ for (String key : new OverrideOrderCalculator(this, all).getOrderedVariableNames()) {
+ override(key, expand(all.get(key)));
+ }
+ return this;
+ }
+
/**
* Resolves environment variables against each other.
*/
@@ -172,6 +356,14 @@ public class EnvVars extends TreeMap {
if (value==null) throw new IllegalArgumentException("Null value not allowed as an environment variable: "+key);
return super.put(key,value);
}
+
+ /**
+ * Add a key/value but only if the value is not-null. Otherwise no-op.
+ */
+ public void putIfNotNull(String key, String value) {
+ if (value!=null)
+ put(key,value);
+ }
/**
* Takes a string that looks like "a=b" and adds that to this map.
diff --git a/core/src/main/java/hudson/ExtensionList.java b/core/src/main/java/hudson/ExtensionList.java
index e1a5ca9682e3e0cc9d910fb9acc9464b19f4d8ba..7861de26c2a5b39bc006b922b92476d36cef46c0 100644
--- a/core/src/main/java/hudson/ExtensionList.java
+++ b/core/src/main/java/hudson/ExtensionList.java
@@ -44,6 +44,8 @@ import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
/**
* Retains the known extension instances for the given type 'T'.
@@ -72,7 +74,7 @@ public class ExtensionList extends AbstractList {
* Use {@link #jenkins}
*/
public final Hudson hudson;
- public final Jenkins jenkins;
+ public final @CheckForNull Jenkins jenkins;
public final Class extensionType;
/**
@@ -119,6 +121,9 @@ public class ExtensionList extends AbstractList {
this.jenkins = jenkins;
this.extensionType = extensionType;
this.legacyInstances = legacyStore;
+ if (jenkins == null) {
+ extensions = Collections.emptyList();
+ }
}
/**
@@ -176,23 +181,23 @@ public class ExtensionList extends AbstractList {
@Override
public synchronized boolean remove(Object o) {
- removeComponent(legacyInstances,o);
+ boolean removed = removeComponent(legacyInstances, o);
if(extensions!=null) {
List> r = new ArrayList>(extensions);
- removeComponent(r,o);
+ removed |= removeComponent(r,o);
extensions = sort(r);
}
- return true;
+ return removed;
}
- private void removeComponent(Collection> collection, Object t) {
+ private boolean removeComponent(Collection> collection, Object t) {
for (Iterator> itr = collection.iterator(); itr.hasNext();) {
ExtensionComponent c = itr.next();
if (c.getInstance().equals(t)) {
- collection.remove(c);
- return;
+ return collection.remove(c);
}
}
+ return false;
}
@Override
@@ -240,7 +245,7 @@ public class ExtensionList extends AbstractList {
private List> ensureLoaded() {
if(extensions!=null)
return extensions; // already loaded
- if(Jenkins.getInstance().getInitLevel().compareTo(InitMilestone.PLUGINS_PREPARED)<0)
+ if (jenkins.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
synchronized (getLoadLock()) {
@@ -332,6 +337,20 @@ public class ExtensionList extends AbstractList {
}
}
+ /**
+ * Gets the extension list for a given type.
+ * Normally calls {@link Jenkins#getExtensionList(Class)} but falls back to an empty list
+ * in case {@link Jenkins#getInstance} is null.
+ * Thus it is useful to call from {@code all()} methods which need to behave gracefully during startup or shutdown.
+ * @param type the extension point type
+ * @return some list
+ * @since 1.572
+ */
+ public static @Nonnull ExtensionList lookup(Class type) {
+ Jenkins j = Jenkins.getInstance();
+ return j == null ? create((Jenkins) null, type) : j.getExtensionList(type);
+ }
+
/**
* Places to store static-scope legacy instances.
*/
diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java
index fe7d95c286940933ca2dda7f1671858d5541efc0..75a9ae1c64c25f7bdfe38f1a0a197c4de8b28809 100644
--- a/core/src/main/java/hudson/FilePath.java
+++ b/core/src/main/java/hudson/FilePath.java
@@ -25,42 +25,52 @@
*/
package hudson;
+import com.jcraft.jzlib.GZIPInputStream;
+import com.jcraft.jzlib.GZIPOutputStream;
import hudson.Launcher.LocalLauncher;
import hudson.Launcher.RemoteLauncher;
-import jenkins.model.Jenkins;
-import hudson.model.TaskListener;
import hudson.model.AbstractProject;
+import hudson.model.Computer;
import hudson.model.Item;
+import hudson.model.TaskListener;
+import hudson.org.apache.tools.tar.TarInputStream;
+import hudson.os.PosixAPI;
+import hudson.os.PosixException;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.DelegatingCallable;
import hudson.remoting.Future;
+import hudson.remoting.LocalChannel;
import hudson.remoting.Pipe;
+import hudson.remoting.RemoteInputStream;
import hudson.remoting.RemoteOutputStream;
import hudson.remoting.VirtualChannel;
-import hudson.remoting.RemoteInputStream;
import hudson.remoting.Which;
import hudson.security.AccessControlled;
+import hudson.util.DaemonThreadFactory;
import hudson.util.DirScanner;
-import hudson.util.IOException2;
-import hudson.util.HeadBufferingStream;
+import hudson.util.ExceptionCatchingThreadFactory;
+import hudson.util.FileVisitor;
import hudson.util.FormValidation;
+import hudson.util.HeadBufferingStream;
import hudson.util.IOUtils;
-
-import static hudson.Util.*;
-import static hudson.util.jna.GNUCLibrary.LIBC;
-import static hudson.FilePath.TarCompression.GZIP;
-import hudson.org.apache.tools.tar.TarInputStream;
+import hudson.util.NamingThreadFactory;
import hudson.util.io.Archiver;
import hudson.util.io.ArchiverFactory;
+import jenkins.model.Jenkins;
+import jenkins.util.ContextResettingExecutorService;
import jenkins.util.VirtualFile;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.io.input.CountingInputStream;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.tar.TarEntry;
-import org.apache.commons.io.input.CountingInputStream;
-import org.apache.commons.fileupload.FileItem;
+import org.apache.tools.zip.ZipEntry;
+import org.apache.tools.zip.ZipFile;
import org.kohsuke.stapler.Stapler;
+
+import javax.annotation.CheckForNull;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
@@ -73,39 +83,34 @@ import java.io.InterruptedIOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
+import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.io.Writer;
-import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
-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.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import com.jcraft.jzlib.GZIPInputStream;
-import com.jcraft.jzlib.GZIPOutputStream;
-
-import com.sun.jna.Native;
-import hudson.os.PosixException;
-import hudson.util.FileVisitor;
-import java.util.Enumeration;
-import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
import java.util.logging.Logger;
-import org.apache.tools.ant.taskdefs.Chmod;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
-import org.apache.tools.zip.ZipFile;
-import org.apache.tools.zip.ZipEntry;
+import static hudson.FilePath.TarCompression.*;
+import static hudson.Util.*;
/**
* {@link File} like object with remoting support.
@@ -199,7 +204,7 @@ public final class FilePath implements Serializable {
* that's connected to that machine. If null, that means the local file path.
*/
public FilePath(VirtualChannel channel, String remote) {
- this.channel = channel == Jenkins.MasterComputer.localChannel ? null : channel;
+ this.channel = channel instanceof LocalChannel ? null : channel;
this.remote = normalize(remote);
}
@@ -392,7 +397,7 @@ public final class FilePath implements Serializable {
*
* @param glob
* Ant style glob, like "**/*.xml". If empty or null, this method
- * works like {@link #createZipArchive(OutputStream)}
+ * works like {@link #createZipArchive(OutputStream)}, inserting a top-level directory into the ZIP.
*
* @since 1.315
*/
@@ -530,7 +535,12 @@ public final class FilePath implements Serializable {
if (p != null) {
p.mkdirs();
}
- IOUtils.copy(zip.getInputStream(e), f);
+ InputStream input = zip.getInputStream(e);
+ try {
+ IOUtils.copy(input, f);
+ } finally {
+ input.close();
+ }
try {
FilePath target = new FilePath(f);
int mode = e.getUnixMode();
@@ -631,16 +641,11 @@ public final class FilePath implements Serializable {
} catch (IOException e) {
// various people reported "java.io.IOException: Not in GZIP format" here, so diagnose this problem better
in.fillSide();
- throw new IOException2(e.getMessage()+"\nstream="+Util.toHexString(in.getSideBuffer()),e);
+ throw new IOException(e.getMessage()+"\nstream="+Util.toHexString(in.getSideBuffer()),e);
}
}
public OutputStream compress(OutputStream out) throws IOException {
- return new GZIPOutputStream(new BufferedOutputStream(out),
- // TODO JENKINS-19473 workaround; replace when jzlib fixed
- new com.jcraft.jzlib.Deflater(6, 15+16, 9), // use 9 for memLevel
- 512,
- true
- );
+ return new GZIPOutputStream(new BufferedOutputStream(out));
}
};
@@ -668,7 +673,7 @@ public final class FilePath implements Serializable {
private static final long serialVersionUID = 1L;
});
} finally {
- IOUtils.closeQuietly(_in);
+ org.apache.commons.io.IOUtils.closeQuietly(_in);
}
}
@@ -760,13 +765,13 @@ public final class FilePath implements Serializable {
else
untarFrom(cis,GZIP);
} catch (IOException e) {
- throw new IOException2(String.format("Failed to unpack %s (%d bytes read of total %d)",
+ throw new IOException(String.format("Failed to unpack %s (%d bytes read of total %d)",
archive,cis.getByteCount(),con.getContentLength()),e);
}
timestamp.touch(sourceTimestamp);
return true;
} catch (IOException e) {
- throw new IOException2("Failed to install "+archive+" to "+remote,e);
+ throw new IOException("Failed to install "+archive+" to "+remote,e);
}
}
@@ -786,7 +791,7 @@ public final class FilePath implements Serializable {
readFromTar("input stream", dir, GZIP.extract(cis));
}
} catch (IOException x) {
- throw new IOException2(String.format("Failed to unpack %s (%d bytes read)", archive, cis.getByteCount()), x);
+ throw new IOException(String.format("Failed to unpack %s (%d bytes read)", archive, cis.getByteCount()), x);
}
} finally {
in.close();
@@ -818,7 +823,7 @@ public final class FilePath implements Serializable {
public void copyFrom(InputStream in) throws IOException, InterruptedException {
OutputStream os = write();
try {
- IOUtils.copy(in, os);
+ org.apache.commons.io.IOUtils.copy(in, os);
} finally {
os.close();
}
@@ -843,13 +848,13 @@ public final class FilePath implements Serializable {
} catch (IOException e) {
throw e;
} catch (Exception e) {
- throw new IOException2(e);
+ throw new IOException(e);
}
} else {
InputStream i = file.getInputStream();
OutputStream o = write();
try {
- IOUtils.copy(i,o);
+ org.apache.commons.io.IOUtils.copy(i,o);
} finally {
try {
o.close();
@@ -910,11 +915,11 @@ public final class FilePath implements Serializable {
throw e; // pass through so that the caller can catch it as AbortException
} catch (IOException e) {
// wrap it into a new IOException so that we get the caller's stack trace as well.
- throw new IOException2("remote file operation failed: "+remote+" at "+channel,e);
+ throw new IOException("remote file operation failed: "+remote+" at "+channel,e);
}
} else {
// the file is on the local machine.
- return callable.invoke(new File(remote), Jenkins.MasterComputer.localChannel);
+ return callable.invoke(new File(remote), localChannel);
}
}
@@ -988,11 +993,11 @@ public final class FilePath implements Serializable {
}
}
- return (channel!=null ? channel : Jenkins.MasterComputer.localChannel)
+ return (channel!=null ? channel : localChannel)
.callAsync(wrapper);
} catch (IOException e) {
// wrap it into a new IOException so that we get the caller's stack trace as well.
- throw new IOException2("remote file operation failed",e);
+ throw new IOException("remote file operation failed",e);
}
}
@@ -1053,6 +1058,22 @@ public final class FilePath implements Serializable {
return VirtualFile.forFilePath(this);
}
+ /**
+ * If this {@link FilePath} represents a file on a particular {@link Computer}, return it.
+ * Otherwise null.
+ */
+ public @CheckForNull Computer toComputer() {
+ Jenkins j = Jenkins.getInstance();
+ if (j != null) {
+ for (Computer c : j.getComputers()) {
+ if (getChannel()==c.getChannel()) {
+ return c;
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Creates this directory.
*/
@@ -1191,7 +1212,7 @@ public final class FilePath implements Serializable {
}
}));
} catch (IOException e) {
- throw new IOException2("Failed to create a temp file on "+remote,e);
+ throw new IOException("Failed to create a temp file on "+remote,e);
}
}
@@ -1251,7 +1272,7 @@ public final class FilePath implements Serializable {
try {
f = File.createTempFile(prefix, suffix, dir);
} catch (IOException e) {
- throw new IOException2("Failed to create a temporary directory in "+dir,e);
+ throw new IOException("Failed to create a temporary directory in "+dir,e);
}
Writer w = new FileWriter(f);
@@ -1265,7 +1286,7 @@ public final class FilePath implements Serializable {
}
}));
} catch (IOException e) {
- throw new IOException2("Failed to create a temp file on "+remote,e);
+ throw new IOException("Failed to create a temp file on "+remote,e);
}
}
@@ -1295,7 +1316,7 @@ public final class FilePath implements Serializable {
}
}));
} catch (IOException e) {
- throw new IOException2("Failed to create a temp directory on "+remote,e);
+ throw new IOException("Failed to create a temp directory on "+remote,e);
}
}
@@ -1409,6 +1430,45 @@ public final class FilePath implements Serializable {
});
}
+ /**
+ * Returns the number of unallocated bytes in the partition of that file.
+ * @since 1.542
+ */
+ public long getFreeDiskSpace() throws IOException, InterruptedException {
+ return act(new FileCallable() {
+ private static final long serialVersionUID = 1L;
+ @Override public Long invoke(File f, VirtualChannel channel) throws IOException {
+ return f.getFreeSpace();
+ }
+ });
+ }
+
+ /**
+ * Returns the total number of bytes in the partition of that file.
+ * @since 1.542
+ */
+ public long getTotalDiskSpace() throws IOException, InterruptedException {
+ return act(new FileCallable() {
+ private static final long serialVersionUID = 1L;
+ @Override public Long invoke(File f, VirtualChannel channel) throws IOException {
+ return f.getTotalSpace();
+ }
+ });
+ }
+
+ /**
+ * Returns the number of usable bytes in the partition of that file.
+ * @since 1.542
+ */
+ public long getUsableDiskSpace() throws IOException, InterruptedException {
+ return act(new FileCallable() {
+ private static final long serialVersionUID = 1L;
+ @Override public Long invoke(File f, VirtualChannel channel) throws IOException {
+ return f.getUsableSpace();
+ }
+ });
+ }
+
/**
* Sets the file permission.
*
@@ -1437,32 +1497,12 @@ public final class FilePath implements Serializable {
}
/**
- * Run chmod via libc if we can, otherwise fall back to Ant.
+ * Run chmod via jnr-posix
*/
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();
+ PosixAPI.jnr().chmod(f.getAbsolutePath(),mask);
}
private static boolean CHMOD_WARNED = false;
@@ -1624,8 +1664,8 @@ public final class FilePath implements Serializable {
Util.copyStream(fis,p.getOut());
return null;
} finally {
- IOUtils.closeQuietly(fis);
- IOUtils.closeQuietly(p.getOut());
+ org.apache.commons.io.IOUtils.closeQuietly(fis);
+ org.apache.commons.io.IOUtils.closeQuietly(p.getOut());
}
}
});
@@ -1639,7 +1679,7 @@ public final class FilePath implements Serializable {
public String readToString() throws IOException {
InputStream in = read();
try {
- return IOUtils.toString(in);
+ return org.apache.commons.io.IOUtils.toString(in);
} finally {
in.close();
}
@@ -1744,14 +1784,20 @@ public final class FilePath implements Serializable {
act(new FileCallable() {
private static final long serialVersionUID = 1L;
public Void invoke(File f, VirtualChannel channel) throws IOException {
+ // JENKINS-16846: if f.getName() is the same as one of the files/directories in f,
+ // then the rename op will fail
+ File tmp = new File(f.getAbsolutePath()+".__rename");
+ if (!f.renameTo(tmp))
+ throw new IOException("Failed to rename "+f+" to "+tmp);
+
File t = new File(target.getRemote());
- for(File child : f.listFiles()) {
+ for(File child : tmp.listFiles()) {
File target = new File(t, child.getName());
if(!child.renameTo(target))
throw new IOException("Failed to rename "+child+" to "+target);
}
- f.delete();
+ tmp.delete();
return null;
}
});
@@ -1769,7 +1815,7 @@ public final class FilePath implements Serializable {
out.close();
}
} catch (IOException e) {
- throw new IOException2("Failed to copy "+this+" to "+target,e);
+ throw new IOException("Failed to copy "+this+" to "+target,e);
}
}
@@ -1799,8 +1845,8 @@ public final class FilePath implements Serializable {
Util.copyStream(fis,out);
return null;
} finally {
- IOUtils.closeQuietly(fis);
- IOUtils.closeQuietly(out);
+ org.apache.commons.io.IOUtils.closeQuietly(fis);
+ org.apache.commons.io.IOUtils.closeQuietly(out);
}
}
});
@@ -1917,7 +1963,7 @@ public final class FilePath implements Serializable {
@Override public void visit(File f, String relativePath) throws IOException {
if (f.isFile()) {
File target = new File(dest, relativePath);
- target.getParentFile().mkdirs();
+ IOUtils.mkdirs(target.getParentFile());
Util.copyFile(f, target);
count.incrementAndGet();
}
@@ -1927,6 +1973,7 @@ public final class FilePath implements Serializable {
}
@Override public void visitSymlink(File link, String target, String relativePath) throws IOException {
try {
+ IOUtils.mkdirs(new File(dest, relativePath).getParentFile());
Util.createSymlink(dest, target, relativePath, TaskListener.NULL);
} catch (InterruptedException x) {
throw (IOException) new IOException(x.toString()).initCause(x);
@@ -1964,7 +2011,7 @@ public final class FilePath implements Serializable {
future.get();
return future2.get();
} catch (ExecutionException e) {
- throw new IOException2(e);
+ throw new IOException(e);
}
} else {
// remote -> local copy
@@ -1988,7 +2035,7 @@ public final class FilePath implements Serializable {
throw e; // the remote side completed successfully, so the error must be local
} catch (ExecutionException x) {
// report both errors
- throw new IOException2(Functions.printThrowable(e),x);
+ throw new IOException(Functions.printThrowable(e),x);
} catch (TimeoutException _) {
// remote is hanging
throw e;
@@ -1997,7 +2044,7 @@ public final class FilePath implements Serializable {
try {
return future.get();
} catch (ExecutionException e) {
- throw new IOException2(e);
+ throw new IOException(e);
}
}
}
@@ -2069,12 +2116,12 @@ public final class FilePath implements Serializable {
}
}
} catch(IOException e) {
- throw new IOException2("Failed to extract "+name,e);
+ throw new IOException("Failed to extract "+name,e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // process this later
- throw new IOException2("Failed to extract "+name,e);
+ throw new IOException("Failed to extract "+name,e);
} catch (IllegalAccessException e) {
- throw new IOException2("Failed to extract "+name,e);
+ throw new IOException("Failed to extract "+name,e);
} finally {
t.close();
}
@@ -2385,7 +2432,7 @@ public final class FilePath implements Serializable {
public VirtualChannel getChannel() {
if(channel!=null) return channel;
- else return Jenkins.MasterComputer.localChannel;
+ else return localChannel;
}
/**
@@ -2458,7 +2505,7 @@ public final class FilePath implements Serializable {
/**
* Used to tunnel {@link InterruptedException} over a Java signature that only allows {@link IOException}
*/
- private static class TunneledInterruptedException extends IOException2 {
+ private static class TunneledInterruptedException extends IOException {
private TunneledInterruptedException(InterruptedException cause) {
super(cause);
}
@@ -2527,4 +2574,11 @@ public final class FilePath implements Serializable {
}
+ private static final ExecutorService threadPoolForRemoting = new ContextResettingExecutorService(
+ Executors.newCachedThreadPool(
+ new ExceptionCatchingThreadFactory(
+ new NamingThreadFactory(new DaemonThreadFactory(), "FilePath.localPool"))
+ ));
+
+ public static final LocalChannel localChannel = new LocalChannel(threadPoolForRemoting);
}
diff --git a/core/src/main/java/hudson/FileSystemProvisioner.java b/core/src/main/java/hudson/FileSystemProvisioner.java
index 0ab7e44a1eb791749d64ffaa9fe57d98790e0c76..58680eb88847035b1d00790d2c453e5edb82537e 100644
--- a/core/src/main/java/hudson/FileSystemProvisioner.java
+++ b/core/src/main/java/hudson/FileSystemProvisioner.java
@@ -24,14 +24,12 @@
package hudson;
import hudson.FilePath.TarCompression;
-import hudson.matrix.MatrixBuild;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Computer;
import hudson.model.Describable;
import hudson.model.Job;
import hudson.model.TaskListener;
-import hudson.util.DirScanner.Glob;
import hudson.util.io.ArchiverFactory;
import jenkins.model.Jenkins;
import hudson.model.listeners.RunListener;
@@ -158,7 +156,7 @@ public abstract class FileSystemProvisioner implements ExtensionPoint, Describab
*
* The state of the build when this method is invoked depends on
* the project type. Most would call this at the end of the build,
- * but for example {@link MatrixBuild} would call this after
+ * but for example {@code MatrixBuild} would call this after
* SCM check out so that the state of the fresh workspace
* can be then propagated to elsewhere.
*
diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java
index 190143f55de5f9a6ff1f1be575b00f4a31af7bda..b375fad3f3c64c54a0fdb17fe67980cf46e63527 100644
--- a/core/src/main/java/hudson/Functions.java
+++ b/core/src/main/java/hudson/Functions.java
@@ -28,7 +28,6 @@ package hudson;
import hudson.cli.CLICommand;
import hudson.console.ConsoleAnnotationDescriptor;
import hudson.console.ConsoleAnnotatorFactory;
-import hudson.matrix.MatrixProject;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.Describable;
@@ -44,9 +43,9 @@ import hudson.model.JobPropertyDescriptor;
import hudson.model.ModelObject;
import hudson.model.Node;
import hudson.model.PageDecorator;
+import hudson.model.PaneStatusProperties;
import hudson.model.ParameterDefinition;
import hudson.model.ParameterDefinition.ParameterDescriptor;
-import hudson.model.Project;
import hudson.model.Run;
import hudson.model.TopLevelItem;
import hudson.model.User;
@@ -140,7 +139,6 @@ import org.apache.commons.jelly.XMLOutput;
import org.apache.commons.jexl.parser.ASTSizeFunction;
import org.apache.commons.jexl.util.Introspector;
import org.apache.commons.lang.StringUtils;
-import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
import org.jvnet.tiger_types.Types;
import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.Stapler;
@@ -150,8 +148,10 @@ import org.kohsuke.stapler.jelly.InternationalizedStringExpression.RawHtmlArgume
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
+import hudson.util.RunList;
+import java.util.concurrent.atomic.AtomicLong;
import org.kohsuke.accmod.Restricted;
-import org.kohsuke.accmod.restrictions.DoNotUse;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
* Utility functions used in views.
@@ -164,21 +164,16 @@ import org.kohsuke.accmod.restrictions.DoNotUse;
*/
@SuppressWarnings("rawtypes")
public class Functions {
- private static volatile int globalIota = 0;
- private int iota;
+ private static final AtomicLong iota = new AtomicLong();
public Functions() {
- iota = globalIota;
- // concurrent requests can use the same ID --- we are just trying to
- // prevent the same user from seeing the same ID repeatedly.
- globalIota+=1000;
}
/**
* Generates an unique ID.
*/
public String generateId() {
- return "id"+iota++;
+ return "id" + iota.getAndIncrement();
}
public static boolean isModel(Object o) {
@@ -193,11 +188,9 @@ public class Functions {
return o instanceof ModelObjectWithChildren;
}
- /**
- * @since 1.524
- */
+ @Deprecated
public static boolean isMatrixProject(Object o) {
- return o instanceof MatrixProject;
+ return o != null && o.getClass().getName().equals("hudson.matrix.MatrixProject");
}
public static String xsDate(Calendar cal) {
@@ -209,7 +202,8 @@ public class Functions {
}
public static void initPageVariables(JellyContext context) {
- String rootURL = Stapler.getCurrentRequest().getContextPath();
+ StaplerRequest currentRequest = Stapler.getCurrentRequest();
+ String rootURL = currentRequest.getContextPath();
Functions h = new Functions();
context.setVariable("h", h);
@@ -229,6 +223,8 @@ public class Functions {
*/
context.setVariable("resURL",rootURL+getResourcePath());
context.setVariable("imagesURL",rootURL+getResourcePath()+"/images");
+
+ context.setVariable("userAgent", currentRequest.getHeader("User-Agent"));
}
/**
@@ -261,7 +257,7 @@ public class Functions {
* like "-5", "+/-0", "+3".
*/
public static String getDiffString(int i) {
- if(i==0) return "\u00B10"; // +/-0
+ if(i==0) return "±0";
String s = Integer.toString(i);
if(i>0) return "+"+s;
else return s;
@@ -412,7 +408,7 @@ public class Functions {
return Node.Mode.values();
}
- public static String getProjectListString(List projects) {
+ public static String getProjectListString(List projects) {
return Items.toNameList(projects);
}
@@ -449,13 +445,14 @@ public class Functions {
return formatter.format(r);
}
- @Restricted(DoNotUse.class)
+ @Restricted(NoExternalUse.class)
public static String[] printLogRecordHtml(LogRecord r, LogRecord prior) {
String[] oldParts = prior == null ? new String[4] : logRecordPreformat(prior);
String[] newParts = logRecordPreformat(r);
for (int i = 0; i < /* not 4 */3; i++) {
newParts[i] = "" + newParts[i] + "";
}
+ newParts[3] = Util.xmlEscape(newParts[3]);
return newParts;
}
/**
@@ -512,6 +509,15 @@ public class Functions {
return c.getValue();
}
+ private static final Pattern ICON_SIZE = Pattern.compile("\\d+x\\d+");
+ @Restricted(NoExternalUse.class)
+ public static String validateIconSize(String iconSize) throws SecurityException {
+ if (!ICON_SIZE.matcher(iconSize).matches()) {
+ throw new SecurityException("invalid iconSize");
+ }
+ return iconSize;
+ }
+
/**
* Gets the suffix to use for YUI JavaScript.
*/
@@ -584,6 +590,10 @@ public class Functions {
return false;
}
+ public static boolean isCollapsed(String paneId) {
+ return PaneStatusProperties.forCurrentUser().isCollapsed(paneId);
+ }
+
/**
* Finds the given object in the ancestor list and returns its URL.
* This is used to determine the "current" URL assigned to the given object,
@@ -1003,20 +1013,15 @@ public class Functions {
Item i=p;
String url = "";
- Collection viewItems;
- if (view != null) {
- viewItems = view.getItems();
- } else {
- viewItems = Collections.emptyList();
- }
while(true) {
ItemGroup ig = i.getParent();
url = i.getShortUrl()+url;
if(ig== Jenkins.getInstance() || (view != null && ig == view.getOwnerItemGroup())) {
assert i instanceof TopLevelItem;
- if(viewItems.contains((TopLevelItem)i)) {
- // if p and the current page belongs to the same view, then return a relative path
+ if (view != null) {
+ // assume p and the current page belong to the same view, so return a relative path
+ // (even if they did not, View.getItem does not by default verify ownership)
return normalizeURI(ancestors.get(view)+'/'+url);
} else {
// otherwise return a path from the root Hudson
@@ -1103,7 +1108,7 @@ public class Functions {
* @param p the Item we want the relative display name
* @param g the ItemGroup used as point of reference for the item
* @return
- * String like "foo » bar"
+ * String like "foo/bar"
*/
public static String getRelativeNameFrom(Item p, ItemGroup g) {
return getRelativeNameFrom(p, g, false);
@@ -1117,7 +1122,7 @@ public class Functions {
* @param p the Item we want the relative display name
* @param g the ItemGroup used as point of reference for the item
* @return
- * String like "foo » bar"
+ * String like "Foo » Bar"
*/
public static String getRelativeDisplayNameFrom(Item p, ItemGroup g) {
return getRelativeNameFrom(p, g, true);
@@ -1129,7 +1134,6 @@ public class Functions {
return sorted;
}
- @IgnoreJRERequirement
public static ThreadInfo[] getThreadInfos() {
ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
return mbean.dumpAllThreads(mbean.isObjectMonitorUsageSupported(),mbean.isSynchronizerUsageSupported());
@@ -1150,8 +1154,10 @@ public class Functions {
while (tg.getParent() != null) tg = tg.getParent();
Thread[] threads = new Thread[tg.activeCount()*2];
int threadsLen = tg.enumerate(threads, true);
- for (int i = 0; i < threadsLen; i++)
- map.put(threads[i].getId(), threads[i].getThreadGroup().getName());
+ for (int i = 0; i < threadsLen; i++) {
+ ThreadGroup group = threads[i].getThreadGroup();
+ map.put(threads[i].getId(), group != null ? group.getName() : null);
+ }
}
protected int compare(long idA, long idB) {
@@ -1191,20 +1197,14 @@ public class Functions {
}
/**
- * Are we running on JRE6 or above?
+ * @deprecated Now always true.
*/
- @IgnoreJRERequirement
+ @Deprecated
public static boolean isMustangOrAbove() {
- try {
- System.console();
- return true;
- } catch(LinkageError e) {
- return false;
- }
+ return true;
}
// ThreadInfo.toString() truncates the stack trace by first 8, so needed my own version
- @IgnoreJRERequirement
public static String dumpThreadInfo(ThreadInfo ti, ThreadGroupMap map) {
String grp = map.getThreadGroup(ti);
StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"" +
@@ -1435,7 +1435,8 @@ public class Functions {
}
/**
- * Returns a sub-list if the given list is bigger than the specified 'maxSize'
+ * Returns a sub-list if the given list is bigger than the specified {@code maxSize}.
+ * Warning: do not call this with a {@link RunList}, or you will break lazy loading!
*/
public static List subList(List base, int maxSize) {
if(maxSize$1")
- .replaceAll("(\\w{10})(?=\\w{3})", "$1")
+ if (plain == null) {
+ return null;
+ }
+ return plain.replaceAll("([\\p{Punct}&&[^;]]+\\w)", "$1")
+ .replaceAll("([^\\p{Punct}\\s-]{20})(?=[^\\p{Punct}\\s-]{10})", "$1")
;
}
@@ -1839,17 +1845,28 @@ public class Functions {
*/
public static void advertiseHeaders(HttpServletResponse rsp) {
Jenkins j = Jenkins.getInstance();
+ if (j!=null) {
+ rsp.setHeader("X-Hudson","1.395");
+ rsp.setHeader("X-Jenkins", Jenkins.VERSION);
+ rsp.setHeader("X-Jenkins-Session", Jenkins.SESSION_HASH);
+
+ TcpSlaveAgentListener tal = j.tcpSlaveAgentListener;
+ if (tal !=null) {
+ rsp.setIntHeader("X-Hudson-CLI-Port", tal.getPort());
+ rsp.setIntHeader("X-Jenkins-CLI-Port", tal.getPort());
+ rsp.setIntHeader("X-Jenkins-CLI2-Port", tal.getPort());
+ rsp.setHeader("X-Jenkins-CLI-Host", TcpSlaveAgentListener.CLI_HOST_NAME);
+ }
+ }
+ }
- rsp.setHeader("X-Hudson","1.395");
- rsp.setHeader("X-Jenkins", Jenkins.VERSION);
- rsp.setHeader("X-Jenkins-Session", Jenkins.SESSION_HASH);
-
- TcpSlaveAgentListener tal = j.tcpSlaveAgentListener;
- if (tal !=null) {
- rsp.setIntHeader("X-Hudson-CLI-Port", tal.getPort());
- rsp.setIntHeader("X-Jenkins-CLI-Port", tal.getPort());
- rsp.setIntHeader("X-Jenkins-CLI2-Port", tal.getPort());
- rsp.setHeader("X-Jenkins-CLI-Host", TcpSlaveAgentListener.CLI_HOST_NAME);
+ @Restricted(NoExternalUse.class) // for actions.jelly and ContextMenu.add
+ public static boolean isContextMenuVisible(Action a) {
+ if (a instanceof ModelObjectWithContextMenu.ContextMenuVisibility) {
+ return ((ModelObjectWithContextMenu.ContextMenuVisibility) a).isVisible();
+ } else {
+ return true;
}
}
+
}
diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java
index 382e850bbd43d26f8ed0f4e762a0b3e24e4ee568..b08dfaf0f139163abb056326c640c50cfbd839b5 100644
--- a/core/src/main/java/hudson/Launcher.java
+++ b/core/src/main/java/hudson/Launcher.java
@@ -39,6 +39,8 @@ import hudson.util.StreamCopyThread;
import hudson.util.ArgumentListBuilder;
import hudson.util.ProcessTree;
import org.apache.commons.io.input.NullInputStream;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -298,8 +300,13 @@ public abstract class Launcher {
return this;
}
+ /**
+ * Gets a list of environment variables to be set.
+ * Returns an empty array if envs field has not been initialized.
+ * @return If initialized, returns a copy of internal envs array. Otherwise - a new empty array.
+ */
public String[] envs() {
- return envs.clone();
+ return envs != null ? envs.clone() : new String[0];
}
/**
@@ -752,7 +759,7 @@ public abstract class Launcher {
*/
public static class LocalLauncher extends Launcher {
public LocalLauncher(TaskListener listener) {
- this(listener, Jenkins.MasterComputer.localChannel);
+ this(listener, FilePath.localChannel);
}
public LocalLauncher(TaskListener listener, VirtualChannel channel) {
@@ -816,7 +823,7 @@ public abstract class Launcher {
* Kill the process when the channel is severed.
*/
@Override
- protected synchronized void terminate(IOException e) {
+ public synchronized void terminate(IOException e) {
super.terminate(e);
ProcessTree pt = ProcessTree.get();
try {
@@ -841,6 +848,30 @@ public abstract class Launcher {
}
}
+ @Restricted(NoExternalUse.class)
+ public static class DummyLauncher extends Launcher {
+
+ public DummyLauncher(TaskListener listener) {
+ super(listener, null);
+ }
+
+ @Override
+ public Proc launch(ProcStarter starter) throws IOException {
+ throw new IOException("Can not call launch on a dummy launcher.");
+ }
+
+ @Override
+ public Channel launchChannel(String[] cmd, OutputStream out, FilePath workDir, Map envVars) throws IOException, InterruptedException {
+ throw new IOException("Can not call launchChannel on a dummy launcher.");
+ }
+
+ @Override
+ public void kill(Map modelEnvVars) throws IOException, InterruptedException {
+ // Kill method should do nothing.
+ }
+ }
+
+
/**
* Launches processes remotely by using the given channel.
*/
@@ -946,6 +977,88 @@ public abstract class Launcher {
}
}
}
+
+ /**
+ * A launcher which delegates to a provided inner launcher.
+ * Allows subclasses to only implement methods they want to override.
+ * Originally, this launcher has been implemented in
+ *
+ * Custom Tools Plugin.
+ *
+ * @author rcampbell
+ * @author Oleg Nenashev, Synopsys Inc.
+ * @since TODO: define version
+ */
+ public static class DecoratedLauncher extends Launcher {
+
+ private Launcher inner = null;
+
+ public DecoratedLauncher(Launcher inner) {
+ super(inner);
+ this.inner = inner;
+ }
+
+ @Override
+ public Proc launch(ProcStarter starter) throws IOException {
+ return inner.launch(starter);
+ }
+
+ @Override
+ public Channel launchChannel(String[] cmd, OutputStream out,
+ FilePath workDir, Map envVars) throws IOException,
+ InterruptedException {
+ return inner.launchChannel(cmd, out, workDir, envVars);
+ }
+
+ @Override
+ public void kill(Map modelEnvVars) throws IOException,
+ InterruptedException {
+ inner.kill(modelEnvVars);
+ }
+
+ @Override
+ public boolean isUnix() {
+ return inner.isUnix();
+ }
+
+ @Override
+ public Proc launch(String[] cmd, boolean[] mask, String[] env, InputStream in, OutputStream out, FilePath workDir) throws IOException {
+ return inner.launch(cmd, mask, env, in, out, workDir);
+ }
+
+ @Override
+ public Computer getComputer() {
+ return inner.getComputer();
+ }
+
+ @Override
+ public TaskListener getListener() {
+ return inner.getListener();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "; decorates " + inner.toString();
+ }
+
+ @Override
+ public VirtualChannel getChannel() {
+ return inner.getChannel();
+ }
+
+ @Override
+ public Proc launch(String[] cmd, String[] env, InputStream in, OutputStream out, FilePath workDir) throws IOException {
+ return inner.launch(cmd, env, in, out, workDir);
+ }
+
+ /**
+ * Gets nested launcher.
+ * @return Inner launcher
+ */
+ public Launcher getInner() {
+ return inner;
+ }
+ }
public static class IOTriplet implements Serializable {
InputStream stdout,stderr;
@@ -1087,11 +1200,7 @@ public abstract class Launcher {
*/
private static EnvVars inherit(Map overrides) {
EnvVars m = new EnvVars(EnvVars.masterEnvVars);
- // first add all values and then eventually expand them as values can refer other newly added values (see JENKINS-19488)
- for (Map.Entry o : overrides.entrySet())
- m.override(o.getKey(),o.getValue());
- for (Map.Entry o : overrides.entrySet())
- m.override(o.getKey(),m.expand(o.getValue()));
+ m.overrideExpandingAll(overrides);
return m;
}
diff --git a/core/src/main/java/hudson/Main.java b/core/src/main/java/hudson/Main.java
index 45067f932baf92499c5e3b1018fe381c19e8326a..cf8a168ba115549ba3e6be45cbdf70423198311d 100644
--- a/core/src/main/java/hudson/Main.java
+++ b/core/src/main/java/hudson/Main.java
@@ -37,7 +37,6 @@ import java.io.Writer;
import java.net.HttpRetryException;
import java.net.HttpURLConnection;
import java.net.URL;
-import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.nio.charset.Charset;
@@ -104,14 +103,14 @@ public class Main {
}
}
- String projectNameEnc = URLEncoder.encode(projectName,"UTF-8").replaceAll("\\+","%20");
+ URL jobURL = new URL(home + "job/" + Util.encode(projectName).replace("/", "/job/") + "/");
{// check if the job name is correct
- HttpURLConnection con = open(new URL(home+"job/"+projectNameEnc+"/acceptBuildResult"));
+ HttpURLConnection con = open(new URL(jobURL, "acceptBuildResult"));
if (auth != null) con.setRequestProperty("Authorization", auth);
con.connect();
if(con.getResponseCode()!=200) {
- System.err.println(projectName+" is not a valid job name on "+home+" ("+con.getResponseMessage()+")");
+ System.err.println(jobURL + " is not a valid external job (" + con.getResponseCode() + " " + con.getResponseMessage() + ")");
return -1;
}
}
@@ -138,29 +137,33 @@ public class Main {
FileOutputStream os = new FileOutputStream(tmpFile);
Writer w = new OutputStreamWriter(os,"UTF-8");
- w.write("");
- w.write("");
- w.flush();
-
- // run the command
- long start = System.currentTimeMillis();
-
- List cmd = new ArrayList();
- for( int i=1; i"+ret+""+(System.currentTimeMillis()-start)+"");
- w.close();
+ int ret;
+ try {
+ w.write("");
+ w.write("");
+ w.flush();
+
+ // run the command
+ long start = System.currentTimeMillis();
+
+ List cmd = new ArrayList();
+ for( int i=1; i"+ret+""+(System.currentTimeMillis()-start)+"");
+ } finally {
+ IOUtils.closeQuietly(w);
+ }
- String location = home+"job/"+projectNameEnc+"/postBuildResult";
+ URL location = new URL(jobURL, "postBuildResult");
while(true) {
try {
// start a remote connection
- HttpURLConnection con = open(new URL(location));
+ HttpURLConnection con = open(location);
if (auth != null) con.setRequestProperty("Authorization", auth);
if (crumbField != null && crumbValue != null) {
con.setRequestProperty(crumbField, crumbValue);
@@ -171,8 +174,11 @@ public class Main {
con.connect();
// send the data
FileInputStream in = new FileInputStream(tmpFile);
- Util.copyStream(in,con.getOutputStream());
- in.close();
+ try {
+ Util.copyStream(in,con.getOutputStream());
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
if(con.getResponseCode()!=200) {
Util.copyStream(con.getErrorStream(),System.err);
@@ -182,7 +188,7 @@ public class Main {
} catch (HttpRetryException e) {
if(e.getLocation()!=null) {
// retry with the new location
- location = e.getLocation();
+ location = new URL(e.getLocation());
continue;
}
// otherwise failed for reasons beyond us.
diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java
index 7ce9dea6d3456fbbab2306b7ad951265ae0215c8..4867ba8faeb82082d6e2c57df668d9f88edfc685 100644
--- a/core/src/main/java/hudson/PluginManager.java
+++ b/core/src/main/java/hudson/PluginManager.java
@@ -40,7 +40,6 @@ import hudson.security.Permission;
import hudson.security.PermissionScope;
import hudson.util.CyclicGraphDetector;
import hudson.util.CyclicGraphDetector.CycleDetectedException;
-import hudson.util.IOException2;
import hudson.util.PersistedList;
import hudson.util.Service;
import hudson.util.VersionNumber;
@@ -83,7 +82,6 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
@@ -110,7 +108,11 @@ import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import static hudson.init.InitMilestone.*;
+import hudson.model.DownloadService;
+import hudson.util.FormValidation;
import static java.util.logging.Level.WARNING;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
* Manages {@link PluginWrapper}s.
@@ -301,7 +303,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
cgd.run(getPlugins());
// obtain topologically sorted list and overwrite the list
- ListIterator litr = plugins.listIterator();
+ ListIterator litr = getPlugins().listIterator();
for (PluginWrapper p : cgd.getSorted()) {
litr.next();
litr.set(p);
@@ -410,11 +412,21 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
*/
public void dynamicLoad(File arc) throws IOException, InterruptedException, RestartRequiredException {
LOGGER.info("Attempting to dynamic load "+arc);
- final PluginWrapper p = strategy.createPluginWrapper(arc);
- String sn = p.getShortName();
+ PluginWrapper p = null;
+ String sn;
+ try {
+ sn = strategy.getShortName(arc);
+ } catch (AbstractMethodError x) {
+ LOGGER.log(WARNING, "JENKINS-12753 fix not active: {0}", x.getMessage());
+ p = strategy.createPluginWrapper(arc);
+ sn = p.getShortName();
+ }
if (getPlugin(sn)!=null)
throw new RestartRequiredException(Messages._PluginManager_PluginIsAlreadyInstalled_RestartRequired(sn));
+ if (p == null) {
+ p = strategy.createPluginWrapper(arc);
+ }
if (p.supportsDynamicLoad()== YesNoMaybe.NO)
throw new RestartRequiredException(Messages._PluginManager_PluginDoesntSupportDynamicLoad_RestartRequired(sn));
@@ -435,22 +447,44 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
failedPlugins.add(new FailedPlugin(sn, e));
activePlugins.remove(p);
plugins.remove(p);
- throw new IOException2("Failed to install "+ sn +" plugin",e);
+ throw new IOException("Failed to install "+ sn +" plugin",e);
}
// run initializers in the added plugin
Reactor r = new Reactor(InitMilestone.ordering());
- r.addAll(new InitializerFinder(p.classLoader) {
+ final ClassLoader loader = p.classLoader;
+ r.addAll(new InitializerFinder(loader) {
@Override
protected boolean filter(Method e) {
- return e.getDeclaringClass().getClassLoader()!=p.classLoader || super.filter(e);
+ return e.getDeclaringClass().getClassLoader() != loader || super.filter(e);
}
}.discoverTasks(r));
try {
new InitReactorRunner().run(r);
} catch (ReactorException e) {
- throw new IOException2("Failed to initialize "+ sn +" plugin",e);
+ throw new IOException("Failed to initialize "+ sn +" plugin",e);
}
+
+ // recalculate dependencies of plugins optionally depending the newly deployed one.
+ for (PluginWrapper depender: plugins) {
+ if (depender.equals(p)) {
+ // skip itself.
+ continue;
+ }
+ for (Dependency d: depender.getOptionalDependencies()) {
+ if (d.shortName.equals(p.getShortName())) {
+ // this plugin depends on the newly loaded one!
+ // recalculate dependencies!
+ try {
+ getPluginStrategy().updateDependency(depender, p);
+ } catch (AbstractMethodError x) {
+ LOGGER.log(WARNING, "{0} does not yet implement updateDependency", getPluginStrategy().getClass());
+ }
+ break;
+ }
+ }
+ }
+
LOGGER.info("Plugin " + sn + " dynamically installed");
}
@@ -553,7 +587,9 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
*/
@Exported
public List getPlugins() {
- return plugins;
+ List out = new ArrayList(plugins.size());
+ out.addAll(plugins);
+ return out;
}
public List getFailedPlugins() {
@@ -566,7 +602,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
* @return The plugin singleton or null if a plugin with the given short name does not exist.
*/
public PluginWrapper getPlugin(String shortName) {
- for (PluginWrapper p : plugins) {
+ for (PluginWrapper p : getPlugins()) {
if(p.getShortName().equals(shortName))
return p;
}
@@ -580,7 +616,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
* @return The plugin singleton or null if for some reason the plugin is not loaded.
*/
public PluginWrapper getPlugin(Class extends Plugin> pluginClazz) {
- for (PluginWrapper p : plugins) {
+ for (PluginWrapper p : getPlugins()) {
if(pluginClazz.isInstance(p.getPlugin()))
return p;
}
@@ -595,7 +631,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
*/
public List getPlugins(Class extends Plugin> pluginSuperclass) {
List result = new ArrayList();
- for (PluginWrapper p : plugins) {
+ for (PluginWrapper p : getPlugins()) {
if(pluginSuperclass.isInstance(p.getPlugin()))
result.add(p);
}
@@ -686,13 +722,33 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
String n = en.nextElement();
if(n.startsWith("plugin.")) {
n = n.substring(7);
- if (n.indexOf(".") > 0) {
- String[] pluginInfo = n.split("\\.");
- UpdateSite.Plugin p = Jenkins.getInstance().getUpdateCenter().getById(pluginInfo[1]).getPlugin(pluginInfo[0]);
- if(p==null)
- throw new Failure("No such plugin: "+n);
- p.deploy(dynamicLoad);
+ // JENKINS-22080 plugin names can contain '.' as could (according to rumour) update sites
+ int index = n.indexOf('.');
+ UpdateSite.Plugin p = null;
+ while (index != -1) {
+ if (index + 1 >= n.length()) {
+ break;
+ }
+ String pluginName = n.substring(0, index);
+ String siteName = n.substring(index + 1);
+ UpdateSite updateSite = Jenkins.getInstance().getUpdateCenter().getById(siteName);
+ if (updateSite == null) {
+ throw new Failure("No such update center: " + siteName);
+ } else {
+ UpdateSite.Plugin plugin = updateSite.getPlugin(pluginName);
+ if (plugin != null) {
+ if (p != null) {
+ throw new Failure("Ambiguous plugin: " + n);
+ }
+ p = plugin;
+ }
+ }
+ index = n.indexOf('.', index + 1);
}
+ if (p == null) {
+ throw new Failure("No such plugin: " + n);
+ }
+ p.deploy(dynamicLoad);
}
}
rsp.sendRedirect("../updateCenter/");
@@ -777,6 +833,24 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
}
+ @Restricted(NoExternalUse.class)
+ @RequirePOST public HttpResponse doCheckUpdatesServer() throws IOException {
+ for (UpdateSite site : Jenkins.getInstance().getUpdateCenter().getSites()) {
+ FormValidation v = site.updateDirectlyNow(DownloadService.signatureCheck);
+ if (v.kind != FormValidation.Kind.OK) {
+ // TODO crude but enough for now
+ return v;
+ }
+ }
+ for (DownloadService.Downloadable d : DownloadService.Downloadable.all()) {
+ FormValidation v = d.updateNow();
+ if (v.kind != FormValidation.Kind.OK) {
+ return v;
+ }
+ }
+ return HttpResponses.forwardToPreviousPage();
+ }
+
protected String identifyPluginShortName(File t) {
try {
JarFile j = new JarFile(t);
@@ -925,7 +999,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
});
} catch (SAXException x) {
- throw new IOException2("Failed to parse XML",x);
+ throw new IOException("Failed to parse XML",x);
} catch (ParserConfigurationException e) {
throw new AssertionError(e); // impossible since we don't tweak XMLParser
}
@@ -942,8 +1016,6 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
*/
private ConcurrentMap> generatedClasses = new ConcurrentHashMap>();
- private ClassLoaderReflectionToolkit clt = new ClassLoaderReflectionToolkit();
-
public UberClassLoader() {
super(PluginManager.class.getClassLoader());
}
@@ -964,11 +1036,11 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
if (FAST_LOOKUP) {
for (PluginWrapper p : activePlugins) {
try {
- Class c = clt.findLoadedClass(p.classLoader,name);
+ Class> c = ClassLoaderReflectionToolkit._findLoadedClass(p.classLoader, name);
if (c!=null) return c;
// calling findClass twice appears to cause LinkageError: duplicate class def
- return clt.findClass(p.classLoader,name);
- } catch (InvocationTargetException e) {
+ return ClassLoaderReflectionToolkit._findClass(p.classLoader, name);
+ } catch (ClassNotFoundException e) {
//not found. try next
}
}
@@ -988,15 +1060,11 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
@Override
protected URL findResource(String name) {
if (FAST_LOOKUP) {
- try {
for (PluginWrapper p : activePlugins) {
- URL url = clt.findResource(p.classLoader,name);
+ URL url = ClassLoaderReflectionToolkit._findResource(p.classLoader, name);
if(url!=null)
return url;
}
- } catch (InvocationTargetException e) {
- throw new Error(e);
- }
} else {
for (PluginWrapper p : activePlugins) {
URL url = p.classLoader.getResource(name);
@@ -1011,13 +1079,9 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
protected Enumeration findResources(String name) throws IOException {
List resources = new ArrayList();
if (FAST_LOOKUP) {
- try {
for (PluginWrapper p : activePlugins) {
- resources.addAll(Collections.list(clt.findResources(p.classLoader, name)));
+ resources.addAll(Collections.list(ClassLoaderReflectionToolkit._findResources(p.classLoader, name)));
}
- } catch (InvocationTargetException e) {
- throw new Error(e);
- }
} else {
for (PluginWrapper p : activePlugins) {
resources.addAll(Collections.list(p.classLoader.getResources(name)));
diff --git a/core/src/main/java/hudson/PluginStrategy.java b/core/src/main/java/hudson/PluginStrategy.java
index 6024c3571f4cb382b5cebeedc2e3e4acce028095..2acb1f706555ac5fed9f4ed2d2c0bd2c4ffef26a 100644
--- a/core/src/main/java/hudson/PluginStrategy.java
+++ b/core/src/main/java/hudson/PluginStrategy.java
@@ -24,11 +24,10 @@
package hudson;
import hudson.model.Hudson;
-import jenkins.model.Jenkins;
-
import java.io.File;
import java.io.IOException;
import java.util.List;
+import javax.annotation.Nonnull;
/**
* Pluggability point for how to create {@link PluginWrapper}.
@@ -50,6 +49,13 @@ public interface PluginStrategy extends ExtensionPoint {
PluginWrapper createPluginWrapper(File archive)
throws IOException;
+ /**
+ * Finds the plugin name without actually unpacking anything {@link #createPluginWrapper} would.
+ * Needed by {@link PluginManager#dynamicLoad} to decide whether such a plugin is already installed.
+ * @return the {@link PluginWrapper#getShortName}
+ */
+ @Nonnull String getShortName(File archive) throws IOException;
+
/**
* Loads the plugin and starts it.
*
@@ -77,4 +83,14 @@ public interface PluginStrategy extends ExtensionPoint {
* @since 1.400
*/
List> findComponents(Class type, Hudson hudson);
+
+ /**
+ * Called when a plugin is installed, but there was already a plugin installed which optionally depended on that plugin.
+ * The class loader of the existing depending plugin should be updated
+ * to load classes from the newly installed plugin.
+ * @param depender plugin depending on dependee.
+ * @param dependee newly loaded plugin.
+ * @since 1.557
+ */
+ void updateDependency(PluginWrapper depender, PluginWrapper dependee);
}
diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java
index dc8fd6659db1d6e2678b09a01cce4d36b5c1aa13..b7364499a4b6ac4717b68b7b3870a81864020177 100644
--- a/core/src/main/java/hudson/PluginWrapper.java
+++ b/core/src/main/java/hudson/PluginWrapper.java
@@ -45,6 +45,7 @@ import java.util.jar.Manifest;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
+import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.LogFactory;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
@@ -54,6 +55,8 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
import java.util.Enumeration;
import java.util.jar.JarFile;
+import java.util.logging.Level;
+import javax.annotation.CheckForNull;
/**
* Represents a Jenkins plug-in and associated control information
@@ -211,7 +214,7 @@ public class PluginWrapper implements Comparable, ModelObject {
}
public String getDisplayName() {
- return getLongName();
+ return StringUtils.removeStart(getLongName(), "Jenkins ");
}
public Api getApi() {
@@ -235,7 +238,7 @@ public class PluginWrapper implements Comparable, ModelObject {
return idx != null && idx.toString().contains(shortName) ? idx : null;
}
- private String computeShortName(Manifest manifest, File archive) {
+ static String computeShortName(Manifest manifest, File archive) {
// use the name captured in the manifest, as often plugins
// depend on the specific short name in its URLs.
String n = manifest.getMainAttributes().getValue("Short-Name");
@@ -283,8 +286,9 @@ public class PluginWrapper implements Comparable, ModelObject {
/**
* Gets the instance of {@link Plugin} contributed by this plugin.
*/
- public Plugin getPlugin() {
- return Jenkins.lookup(PluginInstanceStore.class).store.get(this);
+ public @CheckForNull Plugin getPlugin() {
+ PluginInstanceStore pis = Jenkins.lookup(PluginInstanceStore.class);
+ return pis != null ? pis.store.get(this) : null;
}
/**
@@ -372,11 +376,16 @@ public class PluginWrapper implements Comparable, ModelObject {
* Terminates the plugin.
*/
public void stop() {
- LOGGER.info("Stopping "+shortName);
- try {
- getPlugin().stop();
- } catch(Throwable t) {
- LOGGER.log(WARNING, "Failed to shut down "+shortName, t);
+ Plugin plugin = getPlugin();
+ if (plugin != null) {
+ try {
+ LOGGER.log(Level.FINE, "Stopping {0}", shortName);
+ plugin.stop();
+ } catch (Throwable t) {
+ LOGGER.log(WARNING, "Failed to shut down " + shortName, t);
+ }
+ } else {
+ LOGGER.log(Level.FINE, "Could not find Plugin instance to stop for {0}", shortName);
}
// Work around a bug in commons-logging.
// See http://www.szegedi.org/articles/memleak.html
@@ -573,7 +582,7 @@ public class PluginWrapper implements Comparable, ModelObject {
backupPlugin.close();
}
} catch (IOException e) {
- LOGGER.log(WARNING, "Failed to get backup version ", e);
+ LOGGER.log(WARNING, "Failed to get backup version from " + backup, e);
return null;
}
} else {
diff --git a/core/src/main/java/hudson/Proc.java b/core/src/main/java/hudson/Proc.java
index ca1f4a6767d26fedc768acb33f866f0bf0ece38b..5114cbd2c445a9e490efe2477f7d396932296ad3 100644
--- a/core/src/main/java/hudson/Proc.java
+++ b/core/src/main/java/hudson/Proc.java
@@ -28,7 +28,7 @@ import hudson.model.TaskListener;
import hudson.remoting.Channel;
import hudson.util.DaemonThreadFactory;
import hudson.util.ExceptionCatchingThreadFactory;
-import hudson.util.IOException2;
+import hudson.util.NamingThreadFactory;
import hudson.util.NullStream;
import hudson.util.StreamCopyThread;
import hudson.util.ProcessTree;
@@ -132,7 +132,7 @@ public abstract class Proc {
*/
public abstract OutputStream getStdin();
- private static final ExecutorService executor = Executors.newCachedThreadPool(new ExceptionCatchingThreadFactory(new DaemonThreadFactory()));
+ private static final ExecutorService executor = Executors.newCachedThreadPool(new ExceptionCatchingThreadFactory(new NamingThreadFactory(new DaemonThreadFactory(), "Proc.executor")));
/**
* Like {@link #join} but can be given a maximum time to wait.
@@ -454,7 +454,7 @@ public abstract class Proc {
} catch (ExecutionException e) {
if(e.getCause() instanceof IOException)
throw (IOException)e.getCause();
- throw new IOException2("Failed to join the process",e);
+ throw new IOException("Failed to join the process",e);
} catch (CancellationException x) {
return -1;
}
diff --git a/core/src/main/java/hudson/ProxyConfiguration.java b/core/src/main/java/hudson/ProxyConfiguration.java
index d4e10f65c4010a9ae214ad0cc74c8e569f560406..0e36a4cd9d946d9bfa1f0e97a55bca3852deedf9 100644
--- a/core/src/main/java/hudson/ProxyConfiguration.java
+++ b/core/src/main/java/hudson/ProxyConfiguration.java
@@ -24,32 +24,35 @@
package hudson;
import com.google.common.collect.Lists;
+import com.thoughtworks.xstream.XStream;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
-import hudson.util.FormValidation;
-import jenkins.model.Jenkins;
import hudson.model.Saveable;
import hudson.model.listeners.SaveableListener;
+import hudson.util.FormValidation;
import hudson.util.Scrambler;
import hudson.util.Secret;
import hudson.util.XStream2;
-
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.Serializable;
import java.net.Authenticator;
+import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
-
-import com.thoughtworks.xstream.XStream;
-import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
-
+import jenkins.model.Jenkins;
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.UsernamePasswordCredentials;
+import org.apache.commons.httpclient.auth.AuthScope;
+import org.apache.commons.httpclient.methods.GetMethod;
import org.jvnet.robust_http_client.RetryableHttpStream;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
@@ -65,7 +68,7 @@ import org.kohsuke.stapler.QueryParameter;
* (as described in the Java 6 tech note
*
* Http Authentication).
- *
+ *
* @see jenkins.model.Jenkins#proxy
*/
public final class ProxyConfiguration extends AbstractDescribableImpl implements Saveable, Serializable {
@@ -80,7 +83,7 @@ public final class ProxyConfiguration extends AbstractDescribableImpl List filter( Iterable> base, Class type ) {
+ @Nonnull
+ public static List filter( @Nonnull Iterable> base, @Nonnull Class type ) {
List r = new ArrayList();
for (Object i : base) {
if(type.isInstance(i))
@@ -107,7 +110,8 @@ public class Util {
/**
* Creates a filtered sublist.
*/
- public static List filter( List> base, Class type ) {
+ @Nonnull
+ public static List filter( @Nonnull List> base, @Nonnull Class type ) {
return filter((Iterable)base,type);
}
@@ -123,7 +127,8 @@ public class Util {
* Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.)
*
*/
- public static String replaceMacro(String s, Map properties) {
+ @Nullable
+ public static String replaceMacro( @CheckForNull String s, @Nonnull Map properties) {
return replaceMacro(s,new VariableResolver.ByMap(properties));
}
@@ -133,7 +138,8 @@ public class Util {
*
* Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.)
*/
- public static String replaceMacro(String s, VariableResolver resolver) {
+ @Nullable
+ public static String replaceMacro(@CheckForNull String s, @Nonnull VariableResolver resolver) {
if (s == null) {
return null;
}
@@ -166,11 +172,13 @@ public class Util {
/**
* Loads the contents of a file into a string.
*/
- public static String loadFile(File logfile) throws IOException {
+ @Nonnull
+ public static String loadFile(@Nonnull File logfile) throws IOException {
return loadFile(logfile, Charset.defaultCharset());
}
- public static String loadFile(File logfile,Charset charset) throws IOException {
+ @Nonnull
+ public static String loadFile(@Nonnull File logfile, @Nonnull Charset charset) throws IOException {
if(!logfile.exists())
return "";
@@ -196,7 +204,7 @@ public class Util {
* @throws IOException
* if the operation fails.
*/
- public static void deleteContentsRecursive(File file) throws IOException {
+ public static void deleteContentsRecursive(@Nonnull File file) throws IOException {
File[] files = file.listFiles();
if(files==null)
return; // the directory didn't exist in the first place
@@ -209,7 +217,7 @@ public class Util {
* @param f a file to delete
* @throws IOException if it exists but could not be successfully deleted
*/
- public static void deleteFile(File f) throws IOException {
+ public static void deleteFile(@Nonnull File f) throws IOException {
if (!f.delete()) {
if(!f.exists())
// we are trying to delete a file that no longer exists, so this is not an error
@@ -260,16 +268,11 @@ public class Util {
/**
* Makes the given file writable by any means possible.
*/
- @IgnoreJRERequirement
- private static void makeWritable(File f) {
- // try JDK6-way of doing it.
- try {
- if (f.setWritable(true)) {
- return;
- }
- } catch (NoSuchMethodError e) {
- // not JDK6
+ private static void makeWritable(@Nonnull File f) {
+ if (f.setWritable(true)) {
+ return;
}
+ // TODO do we still need to try anything else?
// try chmod. this becomes no-op if this is not Unix.
try {
@@ -293,7 +296,7 @@ public class Util {
}
- public static void deleteRecursive(File dir) throws IOException {
+ public static void deleteRecursive(@Nonnull File dir) throws IOException {
if(!isSymlink(dir))
deleteContentsRecursive(dir);
try {
@@ -327,7 +330,7 @@ public class Util {
* Checks if the given file represents a symlink.
*/
//Taken from http://svn.apache.org/viewvc/maven/shared/trunk/file-management/src/main/java/org/apache/maven/shared/model/fileset/util/FileSetManager.java?view=markup
- public static boolean isSymlink(File file) throws IOException {
+ public static boolean isSymlink(@Nonnull File file) throws IOException {
Boolean r = isSymlinkJava7(file);
if (r != null) {
return r;
@@ -356,12 +359,12 @@ public class Util {
}
@SuppressWarnings("NP_BOOLEAN_RETURN_NULL")
- private static Boolean isSymlinkJava7(File file) throws IOException {
+ private static Boolean isSymlinkJava7(@Nonnull File file) throws IOException {
try {
Object path = File.class.getMethod("toPath").invoke(file);
return (Boolean) Class.forName("java.nio.file.Files").getMethod("isSymbolicLink", Class.forName("java.nio.file.Path")).invoke(null, path);
} catch (NoSuchMethodException x) {
- return null; // fine, Java 5/6
+ return null; // fine, Java 6
} catch (Exception x) {
throw (IOException) new IOException(x.toString()).initCause(x);
}
@@ -385,13 +388,14 @@ public class Util {
* On Windows, error messages for IOException aren't very helpful.
* This method generates additional user-friendly error message to the listener
*/
- public static void displayIOException( IOException e, TaskListener listener ) {
+ public static void displayIOException(@Nonnull IOException e, @Nonnull TaskListener listener ) {
String msg = getWin32ErrorMessage(e);
if(msg!=null)
listener.getLogger().println(msg);
}
- public static String getWin32ErrorMessage(IOException e) {
+ @CheckForNull
+ public static String getWin32ErrorMessage(@Nonnull IOException e) {
return getWin32ErrorMessage((Throwable)e);
}
@@ -401,6 +405,7 @@ public class Util {
* @return
* null if there seems to be no error code or if the platform is not Win32.
*/
+ @CheckForNull
public static String getWin32ErrorMessage(Throwable e) {
String msg = e.getMessage();
if(msg!=null) {
@@ -426,6 +431,7 @@ public class Util {
* @return
* null if no such message is available.
*/
+ @CheckForNull
public static String getWin32ErrorMessage(int n) {
try {
ResourceBundle rb = ResourceBundle.getBundle("/hudson/win32errors");
@@ -439,6 +445,7 @@ public class Util {
/**
* Guesses the current host name.
*/
+ @Nonnull
public static String getHostName() {
try {
return InetAddress.getLocalHost().getHostName();
@@ -447,21 +454,21 @@ public class Util {
}
}
- public static void copyStream(InputStream in,OutputStream out) throws IOException {
+ public static void copyStream(@Nonnull InputStream in,@Nonnull OutputStream out) throws IOException {
byte[] buf = new byte[8192];
int len;
while((len=in.read(buf))>=0)
out.write(buf,0,len);
}
- public static void copyStream(Reader in, Writer out) throws IOException {
+ public static void copyStream(@Nonnull Reader in, @Nonnull Writer out) throws IOException {
char[] buf = new char[8192];
int len;
while((len=in.read(buf))>0)
out.write(buf,0,len);
}
- public static void copyStreamAndClose(InputStream in,OutputStream out) throws IOException {
+ public static void copyStreamAndClose(@Nonnull InputStream in, @Nonnull OutputStream out) throws IOException {
try {
copyStream(in,out);
} finally {
@@ -470,7 +477,7 @@ public class Util {
}
}
- public static void copyStreamAndClose(Reader in,Writer out) throws IOException {
+ public static void copyStreamAndClose(@Nonnull Reader in, @Nonnull Writer out) throws IOException {
try {
copyStream(in,out);
} finally {
@@ -489,18 +496,21 @@ public class Util {
* @since 1.145
* @see QuotedStringTokenizer
*/
- public static String[] tokenize(String s,String delimiter) {
+ @Nonnull
+ public static String[] tokenize(@Nonnull String s, @CheckForNull String delimiter) {
return QuotedStringTokenizer.tokenize(s,delimiter);
}
- public static String[] tokenize(String s) {
+ @Nonnull
+ public static String[] tokenize(@Nonnull String s) {
return tokenize(s," \t\n\r\f");
}
/**
* Converts the map format of the environment variables to the K=V format in the array.
*/
- public static String[] mapToEnv(Map m) {
+ @Nonnull
+ public static String[] mapToEnv(@Nonnull Map m) {
String[] r = new String[m.size()];
int idx=0;
@@ -510,7 +520,7 @@ public class Util {
return r;
}
- public static int min(int x, int... values) {
+ public static int min(int x, @Nonnull int... values) {
for (int i : values) {
if(i List createSubList( Collection> source, Class type ) {
+ @Nonnull
+ public static List createSubList(@Nonnull Collection> source, @Nonnull Class type ) {
List r = new ArrayList();
for (Object item : source) {
if(type.isInstance(item))
@@ -755,7 +780,8 @@ public class Util {
* {@link #rawEncode(String)} should generally be used instead, though be careful to pass only
* a single path component to that method (it will encode /, but this method does not).
*/
- public static String encode(String s) {
+ @Nonnull
+ public static String encode(@Nonnull String s) {
try {
boolean escaped = false;
@@ -812,7 +838,8 @@ public class Util {
* single path component used in constructing a URL.
* Method name inspired by PHP's rawurlencode.
*/
- public static String rawEncode(String s) {
+ @Nonnull
+ public static String rawEncode(@Nonnull String s) {
boolean escaped = false;
StringBuilder out = null;
CharsetEncoder enc = null;
@@ -861,7 +888,8 @@ public class Util {
/**
* Escapes HTML unsafe characters like <, & to the respective character entities.
*/
- public static String escape(String text) {
+ @Nonnull
+ public static String escape(@Nonnull String text) {
if (text==null) return null;
StringBuilder buf = new StringBuilder(text.length()+64);
for( int i=0; i List fixNull(List l) {
+ @Nonnull
+ public static List fixNull(@CheckForNull List l) {
return l!=null ? l : Collections.emptyList();
}
- public static Set fixNull(Set l) {
+ @Nonnull
+ public static Set fixNull(@CheckForNull Set l) {
return l!=null ? l : Collections.emptySet();
}
- public static Collection fixNull(Collection l) {
+ @Nonnull
+ public static Collection fixNull(@CheckForNull Collection l) {
return l!=null ? l : Collections.emptySet();
}
- public static Iterable fixNull(Iterable l) {
+ @Nonnull
+ public static Iterable fixNull(@CheckForNull Iterable l) {
return l!=null ? l : Collections.emptySet();
}
/**
* Cuts all the leading path portion and get just the file name.
*/
- public static String getFileName(String filePath) {
+ @Nonnull
+ public static String getFileName(@Nonnull String filePath) {
int idx = filePath.lastIndexOf('\\');
if(idx>=0)
return getFileName(filePath.substring(idx+1));
@@ -986,7 +1023,8 @@ public class Util {
/**
* Concatenate multiple strings by inserting a separator.
*/
- public static String join(Collection> strings, String separator) {
+ @Nonnull
+ public static String join(@Nonnull Collection> strings, @Nonnull String separator) {
StringBuilder buf = new StringBuilder();
boolean first=true;
for (Object s : strings) {
@@ -1000,7 +1038,8 @@ public class Util {
/**
* Combines all the given collections into a single list.
*/
- public static List join(Collection extends T>... items) {
+ @Nonnull
+ public static List join(@Nonnull Collection extends T>... items) {
int size = 0;
for (Collection extends T> item : items)
size += item.size();
@@ -1027,7 +1066,8 @@ public class Util {
* Can be null.
* @since 1.172
*/
- public static FileSet createFileSet(File baseDir, String includes, String excludes) {
+ @Nonnull
+ public static FileSet createFileSet(@Nonnull File baseDir, @Nonnull String includes, @CheckForNull String excludes) {
FileSet fs = new FileSet();
fs.setDir(baseDir);
fs.setProject(new Project());
@@ -1049,7 +1089,8 @@ public class Util {
return fs;
}
- public static FileSet createFileSet(File baseDir, String includes) {
+ @Nonnull
+ public static FileSet createFileSet(@Nonnull File baseDir, @Nonnull String includes) {
return createFileSet(baseDir,includes,null);
}
@@ -1065,7 +1106,8 @@ public class Util {
* @param symlinkPath
* Where to create a symlink in (relative to {@code baseDir})
*/
- public static void createSymlink(File baseDir, String targetPath, String symlinkPath, TaskListener listener) throws InterruptedException {
+ public static void createSymlink(@Nonnull File baseDir, @Nonnull String targetPath,
+ @Nonnull String symlinkPath, @Nonnull TaskListener listener) throws InterruptedException {
try {
if (createSymlinkJava7(baseDir, targetPath, symlinkPath)) {
return;
@@ -1137,18 +1179,37 @@ public class Util {
}
}
- private static boolean createSymlinkJava7(File baseDir, String targetPath, String symlinkPath) throws IOException {
+ private static boolean createSymlinkJava7(@Nonnull File baseDir, @Nonnull String targetPath, @Nonnull String symlinkPath) throws IOException {
try {
Object path = File.class.getMethod("toPath").invoke(new File(baseDir, symlinkPath));
Object target = Class.forName("java.nio.file.Paths").getMethod("get", String.class, String[].class).invoke(null, targetPath, new String[0]);
Class> filesC = Class.forName("java.nio.file.Files");
Class> pathC = Class.forName("java.nio.file.Path");
- filesC.getMethod("deleteIfExists", pathC).invoke(null, path);
+ Class> fileAlreadyExistsExceptionC = Class.forName("java.nio.file.FileAlreadyExistsException");
+
Object noAttrs = Array.newInstance(Class.forName("java.nio.file.attribute.FileAttribute"), 0);
- filesC.getMethod("createSymbolicLink", pathC, pathC, noAttrs.getClass()).invoke(null, path, target, noAttrs);
+ final int maxNumberOfTries = 4;
+ final int timeInMillis = 100;
+ for (int tryNumber = 1; tryNumber <= maxNumberOfTries; tryNumber++) {
+ filesC.getMethod("deleteIfExists", pathC).invoke(null, path);
+ try {
+ filesC.getMethod("createSymbolicLink", pathC, pathC, noAttrs.getClass()).invoke(null, path, target, noAttrs);
+ break;
+ }
+ catch (Exception x) {
+ if (fileAlreadyExistsExceptionC.isInstance(x)) {
+ if(tryNumber < maxNumberOfTries) {
+ TimeUnit.MILLISECONDS.sleep(timeInMillis); //trying to defeat likely ongoing race condition
+ continue;
+ }
+ LOGGER.warning("symlink FileAlreadyExistsException thrown "+maxNumberOfTries+" times => cannot createSymbolicLink");
+ }
+ throw x;
+ }
+ }
return true;
} catch (NoSuchMethodException x) {
- return false; // fine, Java 5/6
+ return false; // fine, Java 6
} catch (InvocationTargetException x) {
Throwable x2 = x.getCause();
if (x2 instanceof UnsupportedOperationException) {
@@ -1188,7 +1249,8 @@ public class Util {
* @return null
* if the specified file is not a symlink.
*/
- public static File resolveSymlinkToFile(File link) throws InterruptedException, IOException {
+ @CheckForNull
+ public static File resolveSymlinkToFile(@Nonnull File link) throws InterruptedException, IOException {
String target = resolveSymlink(link);
if (target==null) return null;
@@ -1208,12 +1270,13 @@ public class Util {
* If the symlink is relative, the returned string is that relative representation.
* The relative path is meant to be resolved from the location of the symlink.
*/
- public static String resolveSymlink(File link) throws InterruptedException, IOException {
+ @CheckForNull
+ public static String resolveSymlink(@Nonnull File link) throws InterruptedException, IOException {
try { // Java 7
Object path = File.class.getMethod("toPath").invoke(link);
return Class.forName("java.nio.file.Files").getMethod("readSymbolicLink", Class.forName("java.nio.file.Path")).invoke(null, path).toString();
} catch (NoSuchMethodException x) {
- // fine, Java 5/6; fall through
+ // fine, Java 6; fall through
} catch (InvocationTargetException x) {
Throwable x2 = x.getCause();
if (x2 instanceof UnsupportedOperationException) {
@@ -1293,7 +1356,8 @@ public class Util {
* Wraps with the error icon and the CSS class to render error message.
* @since 1.173
*/
- public static String wrapToErrorSpan(String s) {
+ @Nonnull
+ public static String wrapToErrorSpan(@Nonnull String s) {
s = ""+s+"";
return s;
}
@@ -1306,7 +1370,8 @@ public class Util {
* @param defaultNumber number to return if the string can not be parsed
* @return returns the parsed string; otherwise the default number
*/
- public static Number tryParseNumber(String numberStr, Number defaultNumber) {
+ @CheckForNull
+ public static Number tryParseNumber(@CheckForNull String numberStr, @CheckForNull Number defaultNumber) {
if ((numberStr == null) || (numberStr.length() == 0)) {
return defaultNumber;
}
@@ -1321,7 +1386,7 @@ public class Util {
* Checks if the public method defined on the base type with the given arguments
* are overridden in the given derived type.
*/
- public static boolean isOverridden(Class base, Class derived, String methodName, Class... types) {
+ public static boolean isOverridden(@Nonnull Class base, @Nonnull Class derived, @Nonnull String methodName, @Nonnull Class... types) {
try {
return !base.getMethod(methodName, types).equals(
derived.getMethod(methodName,types));
@@ -1336,7 +1401,8 @@ public class Util {
* @param ext
* For example, ".zip"
*/
- public static File changeExtension(File dst, String ext) {
+ @Nonnull
+ public static File changeExtension(@Nonnull File dst, @Nonnull String ext) {
String p = dst.getPath();
int pos = p.lastIndexOf('.');
if (pos<0) return new File(p+ext);
@@ -1345,8 +1411,10 @@ public class Util {
/**
* Null-safe String intern method.
+ * @return A canonical representation for the string object. Null for null input strings
*/
- public static String intern(String s) {
+ @Nullable
+ public static String intern(@CheckForNull String s) {
return s==null ? s : s.intern();
}
@@ -1357,7 +1425,7 @@ public class Util {
* implementing this by ourselves allow it to be more lenient about
* escaping of URI.
*/
- public static boolean isAbsoluteUri(String uri) {
+ public static boolean isAbsoluteUri(@Nonnull String uri) {
int idx = uri.indexOf(':');
if (idx<0) return false; // no ':'. can't be absolute
@@ -1369,7 +1437,7 @@ public class Util {
* Works like {@link String#indexOf(int)} but 'not found' is returned as s.length(), not -1.
* This enables more straight-forward comparison.
*/
- private static int _indexOf(String s, char ch) {
+ private static int _indexOf(@Nonnull String s, char ch) {
int idx = s.indexOf(ch);
if (idx<0) return s.length();
return idx;
@@ -1379,17 +1447,10 @@ public class Util {
* Loads a key/value pair string as {@link Properties}
* @since 1.392
*/
- @IgnoreJRERequirement
- public static Properties loadProperties(String properties) throws IOException {
+ @Nonnull
+ public static Properties loadProperties(@Nonnull String properties) throws IOException {
Properties p = new Properties();
- try {
- p.load(new StringReader(properties));
- } catch (NoSuchMethodError e) {
- // load(Reader) method is only available on JDK6.
- // this fall back version doesn't work correctly with non-ASCII characters,
- // but there's no other easy ways out it seems.
- p.load(new ByteArrayInputStream(properties.getBytes()));
- }
+ p.load(new StringReader(properties));
return p;
}
diff --git a/core/src/main/java/hudson/WebAppMain.java b/core/src/main/java/hudson/WebAppMain.java
index f6cf979bf80de2edf8e446706d6e480bb064de0f..1f332e97513444f678216d599a2ddee8a3903965 100644
--- a/core/src/main/java/hudson/WebAppMain.java
+++ b/core/src/main/java/hudson/WebAppMain.java
@@ -25,7 +25,9 @@ package hudson;
import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
import com.thoughtworks.xstream.core.JVM;
+import com.trilead.ssh2.util.IOUtils;
import hudson.model.Hudson;
+import hudson.util.BootFailure;
import jenkins.model.Jenkins;
import hudson.util.HudsonIsLoading;
import hudson.util.IncompatibleServletVersionDetected;
@@ -52,21 +54,25 @@ import javax.servlet.ServletResponse;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
+import java.util.Date;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.security.Security;
import java.util.logging.LogRecord;
+import static java.util.logging.Level.*;
+
/**
* Entry point when Hudson is used as a webapp.
*
* @author Kohsuke Kawaguchi
*/
-public final class WebAppMain implements ServletContextListener {
+public class WebAppMain implements ServletContextListener {
private final RingBufferLogHandler handler = new RingBufferLogHandler() {
@Override public synchronized void publish(LogRecord record) {
if (record.getLevel().intValue() >= Level.INFO.intValue()) {
@@ -82,8 +88,9 @@ public final class WebAppMain implements ServletContextListener {
* Creates the sole instance of {@link jenkins.model.Jenkins} and register it to the {@link ServletContext}.
*/
public void contextInitialized(ServletContextEvent event) {
+ final ServletContext context = event.getServletContext();
+ File home=null;
try {
- final ServletContext context = event.getServletContext();
// use the current request to determine the language
LocaleProvider.setProvider(new LocaleProvider() {
@@ -98,8 +105,7 @@ public final class WebAppMain implements ServletContextListener {
jvm = new JVM();
new URLClassLoader(new URL[0],getClass().getClassLoader());
} catch(SecurityException e) {
- context.setAttribute(APP,new InsufficientPermissionDetected(e));
- return;
+ throw new InsufficientPermissionDetected(e);
}
try {// remove Sun PKCS11 provider if present. See http://wiki.jenkins-ci.org/display/JENKINS/Solaris+Issue+6276483
@@ -111,21 +117,19 @@ public final class WebAppMain implements ServletContextListener {
installLogger();
final FileAndDescription describedHomeDir = getHomeDir(event);
- final File home = describedHomeDir.file.getAbsoluteFile();
+ home = describedHomeDir.file.getAbsoluteFile();
home.mkdirs();
System.out.println("Jenkins home directory: "+home+" found at: "+describedHomeDir.description);
// check that home exists (as mkdirs could have failed silently), otherwise throw a meaningful error
- if (! home.exists()) {
- context.setAttribute(APP,new NoHomeDir(home));
- return;
- }
+ if (!home.exists())
+ throw new NoHomeDir(home);
+
+ recordBootAttempt(home);
// make sure that we are using XStream in the "enhanced" (JVM-specific) mode
if(jvm.bestReflectionProvider().getClass()==PureJavaReflectionProvider.class) {
- // nope
- context.setAttribute(APP,new IncompatibleVMDetected());
- return;
+ throw new IncompatibleVMDetected(); // nope
}
// JNA is no longer a hard requirement. It's just nice to have. See HUDSON-4820 for more context.
@@ -163,22 +167,19 @@ public final class WebAppMain implements ServletContextListener {
try {
ServletResponse.class.getMethod("setCharacterEncoding",String.class);
} catch (NoSuchMethodException e) {
- context.setAttribute(APP,new IncompatibleServletVersionDetected(ServletResponse.class));
- return;
+ throw new IncompatibleServletVersionDetected(ServletResponse.class);
}
// make sure that we see Ant 1.7
try {
FileSet.class.getMethod("getDirectoryScanner");
} catch (NoSuchMethodException e) {
- context.setAttribute(APP,new IncompatibleAntVersionDetected(FileSet.class));
- return;
+ throw new IncompatibleAntVersionDetected(FileSet.class);
}
// make sure AWT is functioning, or else JFreeChart won't even load.
if(ChartUtil.awtProblemCause!=null) {
- context.setAttribute(APP,new AWTProblem(ChartUtil.awtProblemCause));
- return;
+ throw new AWTProblem(ChartUtil.awtProblemCause);
}
// some containers (in particular Tomcat) doesn't abort a launch
@@ -188,8 +189,7 @@ public final class WebAppMain implements ServletContextListener {
File f = File.createTempFile("test", "test");
f.delete();
} catch (IOException e) {
- context.setAttribute(APP,new NoTempDir(e));
- return;
+ throw new NoTempDir(e);
}
// Tomcat breaks XSLT with JDK 5.0 and onward. Check if that's the case, and if so,
@@ -199,13 +199,13 @@ public final class WebAppMain implements ServletContextListener {
// if this works we are all happy
} catch (TransformerFactoryConfigurationError x) {
// no it didn't.
- LOGGER.log(Level.WARNING, "XSLT not configured correctly. Hudson will try to fix this. See http://issues.apache.org/bugzilla/show_bug.cgi?id=40895 for more details",x);
+ LOGGER.log(WARNING, "XSLT not configured correctly. Hudson will try to fix this. See http://issues.apache.org/bugzilla/show_bug.cgi?id=40895 for more details",x);
System.setProperty(TransformerFactory.class.getName(),"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
try {
TransformerFactory.newInstance();
LOGGER.info("XSLT is set to the JAXP RI in JRE");
} catch(TransformerFactoryConfigurationError y) {
- LOGGER.log(Level.SEVERE, "Failed to correct the problem.");
+ LOGGER.log(SEVERE, "Failed to correct the problem.");
}
}
@@ -213,24 +213,25 @@ public final class WebAppMain implements ServletContextListener {
context.setAttribute(APP,new HudsonIsLoading());
+ final File _home = home;
initThread = new Thread("Jenkins initialization thread") {
@Override
public void run() {
boolean success = false;
try {
- Jenkins instance = new Hudson(home, context);
+ Jenkins instance = new Hudson(_home, context);
context.setAttribute(APP, instance);
+ BootFailure.getBootFailureFile(_home).delete();
+
// at this point we are open for business and serving requests normally
LOGGER.info("Jenkins is fully up and running");
success = true;
} catch (Error e) {
- LOGGER.log(Level.SEVERE, "Failed to initialize Jenkins",e);
- context.setAttribute(APP,new HudsonFailedToLoad(e));
+ new HudsonFailedToLoad(e).publish(context,_home);
throw e;
} catch (Exception e) {
- LOGGER.log(Level.SEVERE, "Failed to initialize Jenkins",e);
- context.setAttribute(APP,new HudsonFailedToLoad(e));
+ new HudsonFailedToLoad(e).publish(context,_home);
} finally {
Jenkins instance = Jenkins.getInstance();
if(!success && instance!=null)
@@ -239,15 +240,39 @@ public final class WebAppMain implements ServletContextListener {
}
};
initThread.start();
+ } catch (BootFailure e) {
+ e.publish(context,home);
} catch (Error e) {
- LOGGER.log(Level.SEVERE, "Failed to initialize Jenkins",e);
+ LOGGER.log(SEVERE, "Failed to initialize Jenkins",e);
throw e;
} catch (RuntimeException e) {
- LOGGER.log(Level.SEVERE, "Failed to initialize Jenkins",e);
+ LOGGER.log(SEVERE, "Failed to initialize Jenkins",e);
throw e;
}
}
+ public void joinInit() throws InterruptedException {
+ initThread.join();
+ }
+
+ /**
+ * To assist boot failure script, record the number of boot attempts.
+ * This file gets deleted in case of successful boot.
+ *
+ * @see BootFailure
+ */
+ private void recordBootAttempt(File home) {
+ FileOutputStream o=null;
+ try {
+ o = new FileOutputStream(BootFailure.getBootFailureFile(home), true);
+ o.write((new Date().toString() + System.getProperty("line.separator", "\n")).toString().getBytes());
+ } catch (IOException e) {
+ LOGGER.log(WARNING, "Failed to record boot attempts",e);
+ } finally {
+ IOUtils.closeQuietly(o);
+ }
+ }
+
public static void installExpressionFactory(ServletContextEvent event) {
JellyFacet.setExpressionFactory(event, new ExpressionFactory2());
}
diff --git a/core/src/main/java/hudson/XmlFile.java b/core/src/main/java/hudson/XmlFile.java
index 18a95412dbaca0aa3be66147a13eb805aca495b6..00eae060e9f378a4631ab505bc3e9daedb39fad2 100644
--- a/core/src/main/java/hudson/XmlFile.java
+++ b/core/src/main/java/hudson/XmlFile.java
@@ -24,7 +24,7 @@
package hudson;
import com.thoughtworks.xstream.XStream;
-import com.thoughtworks.xstream.converters.ConversionException;
+import com.thoughtworks.xstream.XStreamException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.StreamException;
@@ -32,7 +32,6 @@ import com.thoughtworks.xstream.io.xml.XppDriver;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.Descriptor;
import hudson.util.AtomicFileWriter;
-import hudson.util.IOException2;
import hudson.util.XStream2;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
@@ -141,12 +140,10 @@ public final class XmlFile {
InputStream in = new BufferedInputStream(new FileInputStream(file));
try {
return xs.fromXML(in);
- } catch(StreamException e) {
- throw new IOException2("Unable to read "+file,e);
- } catch(ConversionException e) {
- throw new IOException2("Unable to read "+file,e);
+ } catch (XStreamException e) {
+ throw new IOException("Unable to read "+file,e);
} catch(Error e) {// mostly reflection errors
- throw new IOException2("Unable to read "+file,e);
+ throw new IOException("Unable to read "+file,e);
} finally {
in.close();
}
@@ -164,12 +161,10 @@ public final class XmlFile {
try {
// TODO: expose XStream the driver from XStream
return xs.unmarshal(DEFAULT_DRIVER.createReader(in), o);
- } catch (StreamException e) {
- throw new IOException2("Unable to read "+file,e);
- } catch(ConversionException e) {
- throw new IOException2("Unable to read "+file,e);
+ } catch (XStreamException e) {
+ throw new IOException("Unable to read "+file,e);
} catch(Error e) {// mostly reflection errors
- throw new IOException2("Unable to read "+file,e);
+ throw new IOException("Unable to read "+file,e);
} finally {
in.close();
}
@@ -183,7 +178,7 @@ public final class XmlFile {
xs.toXML(o,w);
w.commit();
} catch(StreamException e) {
- throw new IOException2(e);
+ throw new IOException(e);
} finally {
w.abort();
}
@@ -294,7 +289,7 @@ public final class XmlFile {
// in such a case, assume UTF-8 rather than fail, since Jenkins internally always write XML in UTF-8
return "UTF-8";
} catch (SAXException e) {
- throw new IOException2("Failed to detect encoding of "+file,e);
+ throw new IOException("Failed to detect encoding of "+file,e);
} catch (ParserConfigurationException e) {
throw new AssertionError(e); // impossible
} finally {
diff --git a/core/src/main/java/hudson/cli/AddJobToViewCommand.java b/core/src/main/java/hudson/cli/AddJobToViewCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..cea483785a304ea5651758c4f078e8b8d2cb18b3
--- /dev/null
+++ b/core/src/main/java/hudson/cli/AddJobToViewCommand.java
@@ -0,0 +1,68 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2014 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson.cli;
+
+import java.util.List;
+
+import hudson.Extension;
+import hudson.model.TopLevelItem;
+import hudson.model.DirectlyModifiableView;
+import hudson.model.View;
+
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
+
+/**
+ * @author ogondza
+ * @since 1.570
+ */
+@Extension
+public class AddJobToViewCommand extends CLICommand {
+
+ @Argument(usage="Name of the view", required=true, index=0)
+ private View view;
+
+ @Argument(usage="Job names", required=true, index=1)
+ private List jobs;
+
+ @Override
+ public String getShortDescription() {
+ return Messages.AddJobToViewCommand_ShortDescription();
+ }
+
+ @Override
+ protected int run() throws Exception {
+ view.checkPermission(View.CONFIGURE);
+
+ if (!(view instanceof DirectlyModifiableView)) throw new CmdLineException(
+ null, "'" + view.getDisplayName() + "' view can not be modified directly"
+ );
+
+ for (TopLevelItem job: jobs) {
+ ((DirectlyModifiableView) view).add(job);
+ }
+
+ return 0;
+ }
+}
diff --git a/core/src/main/java/hudson/cli/BuildCommand.java b/core/src/main/java/hudson/cli/BuildCommand.java
index 8ee62ac1345d65ff76a2ccbeab4c61f2768b3c96..06093e110d987ba558d2cdc3cef4336ff979f03a 100644
--- a/core/src/main/java/hudson/cli/BuildCommand.java
+++ b/core/src/main/java/hudson/cli/BuildCommand.java
@@ -23,6 +23,7 @@
*/
package hudson.cli;
+import hudson.Util;
import hudson.console.ModelHyperlinkNote;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
@@ -50,6 +51,7 @@ import java.util.ArrayList;
import java.util.Map.Entry;
import java.io.FileNotFoundException;
import java.io.PrintStream;
+import javax.annotation.Nonnull;
import jenkins.model.Jenkins;
@@ -86,11 +88,10 @@ public class BuildCommand extends CLICommand {
@Option(name="-v",usage="Prints out the console output of the build. Use with -s")
public boolean consoleOutput = false;
- @Option(name="-r", usage="Number of times to retry reading of the output log if it does not exists on first attempt. Defaults to 0. Use with -v.")
- public String retryCntStr = "0";
+ @Option(name="-r") @Deprecated
+ public int retryCnt = 10;
- // hold parsed retryCnt;
- private int retryCnt = 0;
+ protected static final String BUILD_SCHEDULING_REFUSED = "Build scheduling Refused by an extension, hence not in Queue";
protected int run() throws Exception {
job.checkPermission(Item.BUILD);
@@ -101,15 +102,21 @@ public class BuildCommand extends CLICommand {
if (pdp==null)
throw new AbortException(job.getFullDisplayName()+" is not parameterized but the -p option was specified");
+ //TODO: switch to type annotations after the migration to Java 1.8
List values = new ArrayList();
for (Entry e : parameters.entrySet()) {
String name = e.getKey();
ParameterDefinition pd = pdp.getParameterDefinition(name);
- if (pd==null)
+ if (pd==null) {
throw new AbortException(String.format("\'%s\' is not a valid parameter. Did you mean %s?",
name, EditDistance.findNearest(name, pdp.getParameterDefinitionNames())));
- values.add(pd.createValue(this,e.getValue()));
+ }
+ ParameterValue val = pd.createValue(this, Util.fixNull(e.getValue()));
+ if (val == null) {
+ throw new AbortException(String.format("Cannot resolve the value for the parameter \'%s\'.",name));
+ }
+ values.add(val);
}
// handle missing parameters by adding as default values ISSUE JENKINS-7162
@@ -118,14 +125,16 @@ public class BuildCommand extends CLICommand {
continue;
// not passed in use default
- values.add(pd.getDefaultParameterValue());
+ ParameterValue defaultValue = pd.getDefaultParameterValue();
+ if (defaultValue == null) {
+ throw new AbortException(String.format("No default value for the parameter \'%s\'.",pd.getName()));
+ }
+ values.add(defaultValue);
}
a = new ParametersAction(values);
}
- retryCnt = Integer.parseInt(retryCntStr);
-
if (checkSCM) {
if (job.poll(new StreamTaskListener(stdout, getClientCharset())).change == Change.NONE) {
return 0;
@@ -146,6 +155,10 @@ public class BuildCommand extends CLICommand {
QueueTaskFuture extends AbstractBuild> f = job.scheduleBuild2(0, new CLICause(Jenkins.getAuthentication().getName()), a);
if (wait || sync || follow) {
+ if (f == null) {
+ stderr.println(BUILD_SCHEDULING_REFUSED);
+ return -1;
+ }
AbstractBuild b = f.waitForStart(); // wait for the start
stdout.println("Started "+b.getFullDisplayName());
diff --git a/core/src/main/java/hudson/cli/CLIAction.java b/core/src/main/java/hudson/cli/CLIAction.java
new file mode 100644
index 0000000000000000000000000000000000000000..3253fc2840074f4674e69d3f45456716f0692fcd
--- /dev/null
+++ b/core/src/main/java/hudson/cli/CLIAction.java
@@ -0,0 +1,136 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson.cli;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import hudson.model.UnprotectedRootAction;
+import jenkins.model.Jenkins;
+
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+import org.kohsuke.stapler.HttpResponses.HttpResponseException;
+import org.kohsuke.stapler.Stapler;
+import org.kohsuke.stapler.StaplerProxy;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+
+import hudson.Extension;
+import hudson.model.FullDuplexHttpChannel;
+import hudson.remoting.Channel;
+
+/**
+ * Shows usage of CLI and commands.
+ *
+ * @author ogondza
+ */
+@Extension
+@Restricted(NoExternalUse.class)
+public class CLIAction implements UnprotectedRootAction, StaplerProxy {
+
+ private transient final Map duplexChannels = new HashMap();
+
+ public String getIconFileName() {
+ return null;
+ }
+
+ public String getDisplayName() {
+
+ return "Jenkins CLI";
+ }
+
+ public String getUrlName() {
+ return "cli";
+ }
+
+ public void doCommand(StaplerRequest req, StaplerResponse rsp) throws ServletException, IOException {
+ final Jenkins jenkins = Jenkins.getInstance();
+ jenkins.checkPermission(Jenkins.READ);
+
+ // Strip trailing slash
+ final String commandName = req.getRestOfPath().substring(1);
+ CLICommand command = CLICommand.clone(commandName);
+ if (command == null) {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND, "No such command " + commandName);
+ return;
+ }
+
+ req.setAttribute("command", command);
+ req.getView(this, "command.jelly").forward(req, rsp);
+ }
+
+ @Override
+ public Object getTarget() {
+ StaplerRequest req = Stapler.getCurrentRequest();
+ if (req.getRestOfPath().length()==0 && "POST".equals(req.getMethod())) {
+ // CLI connection request
+ throw new CliEndpointResponse();
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Serves CLI-over-HTTP response.
+ */
+ private class CliEndpointResponse extends HttpResponseException {
+ @Override
+ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
+ try {
+ // do not require any permission to establish a CLI connection
+ // the actual authentication for the connecting Channel is done by CLICommand
+
+ UUID uuid = UUID.fromString(req.getHeader("Session"));
+ rsp.setHeader("Hudson-Duplex",""); // set the header so that the client would know
+
+ FullDuplexHttpChannel server;
+ if(req.getHeader("Side").equals("download")) {
+ duplexChannels.put(uuid,server=new FullDuplexHttpChannel(uuid, !Jenkins.getInstance().hasPermission(Jenkins.ADMINISTER)) {
+ @Override
+ protected void main(Channel channel) throws IOException, InterruptedException {
+ // capture the identity given by the transport, since this can be useful for SecurityRealm.createCliAuthenticator()
+ channel.setProperty(CLICommand.TRANSPORT_AUTHENTICATION, Jenkins.getAuthentication());
+ channel.setProperty(CliEntryPoint.class.getName(),new CliManagerImpl(channel));
+ }
+ });
+ try {
+ server.download(req,rsp);
+ } finally {
+ duplexChannels.remove(uuid);
+ }
+ } else {
+ duplexChannels.get(uuid).upload(req,rsp);
+ }
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java
index c5a672ff40bb73074d65b7451f873b9e82c31e16..dc79a622de0a5e2437abf9c325cbb9da877f4935 100644
--- a/core/src/main/java/hudson/cli/CLICommand.java
+++ b/core/src/main/java/hudson/cli/CLICommand.java
@@ -46,12 +46,15 @@ import org.apache.commons.discovery.resource.classes.DiscoverClasses;
import org.apache.commons.discovery.resource.names.DiscoverServiceNames;
import org.jvnet.hudson.annotation_indexer.Index;
import org.jvnet.tiger_types.Types;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.args4j.ClassParser;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.spi.OptionHandler;
import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
@@ -186,6 +189,8 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
* The default implementation uses args4j to parse command line arguments and call {@link #run()},
* but if that processing is undesirable, subtypes can directly override this method and leave {@link #run()}
* to an empty method.
+ * You would however then have to consider {@link CliAuthenticator} and {@link #getTransportAuthentication},
+ * so this is not really recommended.
*
* @param args
* Arguments to the sub command. For example, if the CLI is invoked like "java -jar cli.jar foo bar zot",
@@ -208,7 +213,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
this.stderr = stderr;
this.locale = locale;
registerOptionHandlers();
- CmdLineParser p = new CmdLineParser(this);
+ CmdLineParser p = getCmdLineParser();
// add options from the authenticator
SecurityContext sc = SecurityContextHolder.getContext();
@@ -242,6 +247,16 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
sc.setAuthentication(old); // restore
}
}
+
+ /**
+ * Get parser for this command.
+ *
+ * Exposed to be overridden by {@link hudson.cli.declarative.CLIRegisterer}.
+ * @since 1.538
+ */
+ protected CmdLineParser getCmdLineParser() {
+ return new CmdLineParser(this);
+ }
public Channel checkChannel() throws AbortException {
if (channel==null)
@@ -329,11 +344,46 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
protected abstract int run() throws Exception;
protected void printUsage(PrintStream stderr, CmdLineParser p) {
- stderr.println("java -jar jenkins-cli.jar "+getName()+" args...");
+ stderr.print("java -jar jenkins-cli.jar " + getName());
+ p.printSingleLineUsage(stderr);
+ stderr.println();
printUsageSummary(stderr);
p.printUsage(stderr);
}
+ /**
+ * Get single line summary as a string.
+ */
+ @Restricted(NoExternalUse.class)
+ public final String getSingleLineSummary() {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ getCmdLineParser().printSingleLineUsage(out);
+ return out.toString();
+ }
+
+ /**
+ * Get usage as a string.
+ */
+ @Restricted(NoExternalUse.class)
+ public final String getUsage() {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ getCmdLineParser().printUsage(out);
+ return out.toString();
+ }
+
+ /**
+ * Get long description as a string.
+ */
+ @Restricted(NoExternalUse.class)
+ public final String getLongDescription() {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(out);
+
+ printUsageSummary(ps);
+ ps.close();
+ return out.toString();
+ }
+
/**
* Called while producing usage. This is a good method to override
* to render the general description of the command that goes beyond
diff --git a/core/src/main/java/hudson/cli/CliProtocol.java b/core/src/main/java/hudson/cli/CliProtocol.java
index b3b725fe33ff896a3c59b1a167c682c935c7fe00..1d4f25627e077f237fa41dc731605309e7c72b59 100644
--- a/core/src/main/java/hudson/cli/CliProtocol.java
+++ b/core/src/main/java/hudson/cli/CliProtocol.java
@@ -4,9 +4,13 @@ import hudson.Extension;
import hudson.model.Computer;
import hudson.remoting.Channel;
import hudson.remoting.Channel.Mode;
+import hudson.remoting.ChannelBuilder;
import jenkins.AgentProtocol;
import jenkins.model.Jenkins;
+import jenkins.slaves.NioChannelSelector;
+import org.jenkinsci.remoting.nio.NioChannelHub;
+import javax.inject.Inject;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
@@ -23,6 +27,9 @@ import java.net.Socket;
*/
@Extension
public class CliProtocol extends AgentProtocol {
+ @Inject
+ NioChannelSelector nio;
+
@Override
public String getName() {
return "CLI-connect";
@@ -30,13 +37,23 @@ public class CliProtocol extends AgentProtocol {
@Override
public void handle(Socket socket) throws IOException, InterruptedException {
- new Handler(socket).run();
+ new Handler(nio.getHub(),socket).run();
}
protected static class Handler {
+ protected final NioChannelHub hub;
protected final Socket socket;
+ /**
+ * @deprecated as of 1.559
+ * Use {@link #Handler(NioChannelHub, Socket)}
+ */
public Handler(Socket socket) {
+ this(null,socket);
+ }
+
+ public Handler(NioChannelHub hub, Socket socket) {
+ this.hub = hub;
this.socket = socket;
}
@@ -47,9 +64,21 @@ public class CliProtocol extends AgentProtocol {
}
protected void runCli(Connection c) throws IOException, InterruptedException {
- Channel channel = new Channel("CLI channel from " + socket.getInetAddress(),
- Computer.threadPoolForRemoting, Mode.BINARY,
- new BufferedInputStream(c.in), new BufferedOutputStream(c.out), null, true, Jenkins.getInstance().pluginManager.uberClassLoader);
+ ChannelBuilder cb;
+ String name = "CLI channel from " + socket.getInetAddress();
+
+ // Connection can contain cipher wrapper, which can't be NIO-ed.
+// if (hub!=null)
+// cb = hub.newChannelBuilder(name, Computer.threadPoolForRemoting);
+// else
+ cb = new ChannelBuilder(name, Computer.threadPoolForRemoting);
+
+ Channel channel = cb
+ .withMode(Mode.BINARY)
+ .withRestricted(true)
+ .withBaseLoader(Jenkins.getInstance().pluginManager.uberClassLoader)
+ .build(new BufferedInputStream(c.in), new BufferedOutputStream(c.out));
+
channel.setProperty(CliEntryPoint.class.getName(),new CliManagerImpl(channel));
channel.join();
}
diff --git a/core/src/main/java/hudson/cli/CliProtocol2.java b/core/src/main/java/hudson/cli/CliProtocol2.java
index f1cea1add398c40724aa2ad3df51ec425bd752e1..644914a089bdaa2bb2368f8d2c9bf940b5987cc0 100644
--- a/core/src/main/java/hudson/cli/CliProtocol2.java
+++ b/core/src/main/java/hudson/cli/CliProtocol2.java
@@ -1,8 +1,8 @@
package hudson.cli;
import hudson.Extension;
-import hudson.util.IOException2;
import jenkins.model.Jenkins;
+import org.jenkinsci.remoting.nio.NioChannelHub;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
@@ -29,14 +29,22 @@ public class CliProtocol2 extends CliProtocol {
@Override
public void handle(Socket socket) throws IOException, InterruptedException {
- new Handler2(socket).run();
+ new Handler2(nio.getHub(), socket).run();
}
protected static class Handler2 extends Handler {
+ /**
+ * @deprecated as of 1.559
+ * Use {@link #Handler2(NioChannelHub, Socket)}
+ */
public Handler2(Socket socket) {
super(socket);
}
+ public Handler2(NioChannelHub hub, Socket socket) {
+ super(hub, socket);
+ }
+
@Override
public void run() throws IOException, InterruptedException {
try {
@@ -72,7 +80,7 @@ public class CliProtocol2 extends CliProtocol {
runCli(c);
} catch (GeneralSecurityException e) {
- throw new IOException2("Failed to encrypt the CLI channel",e);
+ throw new IOException("Failed to encrypt the CLI channel",e);
}
}
}
diff --git a/core/src/main/java/hudson/cli/ClientAuthenticationCache.java b/core/src/main/java/hudson/cli/ClientAuthenticationCache.java
index cd727e7ba42eb906aec2adf02d16ce0fe0587412..3f9479c6e75827b052f55129970a3b8ca91a202b 100644
--- a/core/src/main/java/hudson/cli/ClientAuthenticationCache.java
+++ b/core/src/main/java/hudson/cli/ClientAuthenticationCache.java
@@ -1,11 +1,10 @@
package hudson.cli;
import hudson.FilePath;
-import jenkins.model.Jenkins;
-import jenkins.model.Jenkins.MasterComputer;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.util.Secret;
+import jenkins.model.Jenkins;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
@@ -40,10 +39,14 @@ public class ClientAuthenticationCache implements Serializable {
private final Properties props = new Properties();
public ClientAuthenticationCache(Channel channel) throws IOException, InterruptedException {
- store = (channel==null ? MasterComputer.localChannel : channel).call(new Callable() {
+ store = (channel==null ? FilePath.localChannel : channel).call(new Callable() {
public FilePath call() throws IOException {
File home = new File(System.getProperty("user.home"));
- return new FilePath(new File(home, ".hudson/cli-credentials"));
+ File hudsonHome = new File(home, ".hudson");
+ if (hudsonHome.exists()) {
+ return new FilePath(new File(hudsonHome, "cli-credentials"));
+ }
+ return new FilePath(new File(home, ".jenkins/cli-credentials"));
}
});
if (store.exists()) {
diff --git a/core/src/main/java/hudson/cli/ConsoleCommand.java b/core/src/main/java/hudson/cli/ConsoleCommand.java
index b6edd0be80f6f4ffa931658bfdfaae7ea76e0a6e..6064cfd6ead8067580f29f5e3f816f3d4e8ee8ca 100644
--- a/core/src/main/java/hudson/cli/ConsoleCommand.java
+++ b/core/src/main/java/hudson/cli/ConsoleCommand.java
@@ -26,7 +26,7 @@ import java.io.PrintStream;
public class ConsoleCommand extends CLICommand {
@Override
public String getShortDescription() {
- return "Retrieves console output of a build";
+ return Messages.ConsoleCommand_ShortDescription();
}
@Argument(metaVar="JOB",usage="Name of the job",required=true)
@@ -77,7 +77,7 @@ public class ConsoleCommand extends CLICommand {
} else {
InputStream in = run.getLogInputStream();
IOUtils.skip(in,pos);
- IOUtils.copy(new InputStreamReader(in,run.getCharset()),w);
+ org.apache.commons.io.IOUtils.copy(new InputStreamReader(in,run.getCharset()),w);
}
} finally {
w.flush(); // this pointless flush needed to work around SSHD-154
@@ -134,7 +134,7 @@ public class ConsoleCommand extends CLICommand {
return rb.get();
} finally {
- IOUtils.closeQuietly(in);
+ org.apache.commons.io.IOUtils.closeQuietly(in);
}
}
diff --git a/core/src/main/java/hudson/cli/CopyJobCommand.java b/core/src/main/java/hudson/cli/CopyJobCommand.java
index a8049bdf28cb4b5a320f5f3cd5921c64afd7e28a..7d83fa99a076ec00c7228c6e4b58b7f562d111ff 100644
--- a/core/src/main/java/hudson/cli/CopyJobCommand.java
+++ b/core/src/main/java/hudson/cli/CopyJobCommand.java
@@ -51,7 +51,6 @@ public class CopyJobCommand extends CLICommand {
protected int run() throws Exception {
Jenkins jenkins = Jenkins.getInstance();
- jenkins.checkPermission(Item.CREATE);
if (jenkins.getItemByFullName(dst)!=null) {
stderr.println("Job '"+dst+"' already exists");
@@ -75,7 +74,7 @@ public class CopyJobCommand extends CLICommand {
dst = dst.substring(i + 1);
}
- ig.copy(src,dst);
+ ig.copy(src,dst).save();
return 0;
}
}
diff --git a/core/src/main/java/hudson/cli/CreateJobCommand.java b/core/src/main/java/hudson/cli/CreateJobCommand.java
index db73c30b51a03816d5a9288aab32b9d4ff3ca618..1e1b807c5ee257880a094a74c8d8bde7dc3db83d 100644
--- a/core/src/main/java/hudson/cli/CreateJobCommand.java
+++ b/core/src/main/java/hudson/cli/CreateJobCommand.java
@@ -23,8 +23,6 @@
*/
package hudson.cli;
-import hudson.model.ModifiableItemGroup;
-import hudson.model.TopLevelItem;
import jenkins.model.Jenkins;
import hudson.Extension;
import hudson.model.Item;
@@ -48,7 +46,6 @@ public class CreateJobCommand extends CLICommand {
protected int run() throws Exception {
Jenkins h = Jenkins.getInstance();
- h.checkPermission(Item.CREATE);
if (h.getItemByFullName(name)!=null) {
stderr.println("Job '"+name+"' already exists");
@@ -72,6 +69,7 @@ public class CreateJobCommand extends CLICommand {
name = name.substring(i + 1);
}
+ Jenkins.checkGoodName(name);
ig.createProjectFromXML(name, stdin);
return 0;
}
diff --git a/core/src/main/java/hudson/cli/CreateViewCommand.java b/core/src/main/java/hudson/cli/CreateViewCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..73fd374b854195f0d96de49f582f61c364509b32
--- /dev/null
+++ b/core/src/main/java/hudson/cli/CreateViewCommand.java
@@ -0,0 +1,76 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson.cli;
+
+import org.kohsuke.args4j.Argument;
+
+import jenkins.model.Jenkins;
+import hudson.Extension;
+import hudson.model.Failure;
+import hudson.model.View;
+
+/**
+ * @author ogondza
+ * @since 1.538
+ */
+@Extension
+public class CreateViewCommand extends CLICommand {
+
+ @Argument(usage="Name of the view to use instead of the one in XML")
+ public String viewName = null;
+
+ @Override
+ public String getShortDescription() {
+
+ return Messages.CreateViewCommand_ShortDescription();
+ }
+
+ @Override
+ protected int run() throws Exception {
+
+ final Jenkins jenkins = Jenkins.getInstance();
+ jenkins.checkPermission(View.CREATE);
+
+ View newView;
+ try {
+
+ newView = View.createViewFromXML(viewName, stdin);
+ } catch (Failure ex) {
+
+ stderr.format("Invalid view name: %s\n", ex.getMessage());
+ return -1;
+ }
+
+ final String newName = newView.getViewName();
+ if (jenkins.getView(newName) != null) {
+
+ stderr.format("View '%s' already exists\n", newName);
+ return -1;
+ }
+
+ jenkins.addView(newView);
+
+ return 0;
+ }
+}
diff --git a/core/src/main/java/hudson/cli/DeleteViewCommand.java b/core/src/main/java/hudson/cli/DeleteViewCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..73414701139bab47cc931edb63df4b12e4e7b4c7
--- /dev/null
+++ b/core/src/main/java/hudson/cli/DeleteViewCommand.java
@@ -0,0 +1,67 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson.cli;
+
+import hudson.Extension;
+import hudson.model.ViewGroup;
+import hudson.model.View;
+
+import org.kohsuke.args4j.Argument;
+
+/**
+ * @author ogondza
+ * @since 1.538
+ */
+@Extension
+public class DeleteViewCommand extends CLICommand {
+
+ @Argument(usage="Name of the view to delete", required=true)
+ private View view;
+
+ @Override
+ public String getShortDescription() {
+
+ return Messages.DeleteViewCommand_ShortDescription();
+ }
+
+ @Override
+ protected int run() throws Exception {
+
+ view.checkPermission(View.DELETE);
+
+ final ViewGroup group = view.getOwner();
+ if (!group.canDelete(view)) {
+
+ stderr.format("%s does not allow to delete '%s' view\n",
+ group.getDisplayName(),
+ view.getViewName()
+ );
+ return -1;
+ }
+
+ group.deleteView(view);;
+
+ return 0;
+ }
+}
diff --git a/core/src/main/java/hudson/cli/GetJobCommand.java b/core/src/main/java/hudson/cli/GetJobCommand.java
index 0b8223442cb9ec78f2a5b9189254d126552f7c78..efc1e90836927f593b6016c53615634d20d67978 100644
--- a/core/src/main/java/hudson/cli/GetJobCommand.java
+++ b/core/src/main/java/hudson/cli/GetJobCommand.java
@@ -24,7 +24,7 @@
package hudson.cli;
import hudson.Extension;
-import hudson.model.AbstractProject;
+import hudson.model.AbstractItem;
import hudson.model.Item;
import hudson.util.IOUtils;
import org.kohsuke.args4j.Argument;
@@ -35,7 +35,7 @@ import org.kohsuke.args4j.Argument;
@Extension
public class GetJobCommand extends CLICommand {
@Argument(metaVar="JOB",usage="Name of the job",required=true)
- public AbstractProject,?> job;
+ public AbstractItem job;
@Override
public String getShortDescription() {
diff --git a/core/src/main/java/hudson/cli/GetViewCommand.java b/core/src/main/java/hudson/cli/GetViewCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ae0ffa552af691e4cdef28be2526917bb291eda
--- /dev/null
+++ b/core/src/main/java/hudson/cli/GetViewCommand.java
@@ -0,0 +1,55 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson.cli;
+
+import hudson.Extension;
+import hudson.model.View;
+
+import org.kohsuke.args4j.Argument;
+
+/**
+ * @author ogondza
+ * @since 1.538
+ */
+@Extension
+public class GetViewCommand extends CLICommand {
+
+ @Argument(usage="Name of the view to obtain", required=true)
+ private View view;
+
+ @Override
+ public String getShortDescription() {
+
+ return Messages.GetViewCommand_ShortDescription();
+ }
+
+ @Override
+ protected int run() throws Exception {
+
+ view.checkPermission(View.READ);
+ view.writeXml(stdout);
+
+ return 0;
+ }
+}
diff --git a/core/src/main/java/hudson/cli/GroovyCommand.java b/core/src/main/java/hudson/cli/GroovyCommand.java
index 7035bfdaf9591a6f0ec8a0784145a2a09ad4e474..7866e2f7228d6fde0ea480631c52676da97702fc 100644
--- a/core/src/main/java/hudson/cli/GroovyCommand.java
+++ b/core/src/main/java/hudson/cli/GroovyCommand.java
@@ -66,7 +66,7 @@ public class GroovyCommand extends CLICommand {
/**
* Remaining arguments.
*/
- @Argument(index=1)
+ @Argument(metaVar="ARGUMENTS", index=1, usage="Command line arguments to pass into script.")
public List remaining = new ArrayList();
protected int run() throws Exception {
diff --git a/core/src/main/java/hudson/cli/GroovyshCommand.java b/core/src/main/java/hudson/cli/GroovyshCommand.java
index ffd0b1d243c6283d0882f77883e1b88e7b3ba417..48babcb90dbc98a2ca04941813357fec51505427 100644
--- a/core/src/main/java/hudson/cli/GroovyshCommand.java
+++ b/core/src/main/java/hudson/cli/GroovyshCommand.java
@@ -34,14 +34,15 @@ import org.codehaus.groovy.tools.shell.Shell;
import org.codehaus.groovy.tools.shell.util.XmlCommandRegistrar;
import java.util.List;
-import java.util.Locale;
import java.io.PrintStream;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.PrintWriter;
+import java.util.ArrayList;
import jline.UnsupportedTerminal;
import jline.Terminal;
+import org.kohsuke.args4j.Argument;
/**
* Executes Groovy shell.
@@ -55,12 +56,12 @@ public class GroovyshCommand extends CLICommand {
return Messages.GroovyshCommand_ShortDescription();
}
+ @Argument(metaVar="ARGS") public List args = new ArrayList();
+
@Override
- public int main(List args, Locale locale, InputStream stdin, PrintStream stdout, PrintStream stderr) {
+ protected int run() {
// this allows the caller to manipulate the JVM state, so require the admin privilege.
Jenkins.getInstance().checkPermission(Jenkins.RUN_SCRIPTS);
- // TODO: ^as this class overrides main() (which has authentication stuff),
- // how to get ADMIN permission for this command?
// this being remote means no jline capability is available
System.setProperty("jline.terminal", UnsupportedTerminal.class.getName());
@@ -123,7 +124,4 @@ public class GroovyshCommand extends CLICommand {
return shell;
}
- protected int run() {
- throw new UnsupportedOperationException();
- }
}
diff --git a/core/src/main/java/hudson/cli/HelpCommand.java b/core/src/main/java/hudson/cli/HelpCommand.java
index d49dd2bccbef7920a9387ae27969ff93fee32fe3..bebb39dfcd9ce8aff1f1fb1a42c685e9ce941899 100644
--- a/core/src/main/java/hudson/cli/HelpCommand.java
+++ b/core/src/main/java/hudson/cli/HelpCommand.java
@@ -29,6 +29,8 @@ import jenkins.model.Jenkins;
import java.util.Map;
import java.util.TreeMap;
+import org.kohsuke.args4j.Argument;
+
/**
* Show the list of all commands.
*
@@ -36,18 +38,32 @@ import java.util.TreeMap;
*/
@Extension
public class HelpCommand extends CLICommand {
+
+ @Argument(metaVar="COMMAND", usage="Name of the command")
+ public String command;
+
@Override
public String getShortDescription() {
return Messages.HelpCommand_ShortDescription();
}
+ @Override
protected int run() {
if (!Jenkins.getInstance().hasPermission(Jenkins.READ)) {
stderr.println("You must authenticate to access this Jenkins.\n"
+ "Use --username/--password/--password-file parameters or login command.");
- return 0;
+ return -1;
}
+ if (command != null)
+ return showCommandDetails();
+
+ showAllCommands();
+
+ return 0;
+ }
+
+ private int showAllCommands() {
Map commands = new TreeMap();
for (CLICommand c : CLICommand.all())
commands.put(c.getName(),c);
@@ -56,6 +72,19 @@ public class HelpCommand extends CLICommand {
stderr.println(" "+c.getName());
stderr.println(" "+c.getShortDescription());
}
+
+ return 0;
+ }
+
+ private int showCommandDetails() {
+ CLICommand command = CLICommand.clone(this.command);
+ if (command == null) {
+ stderr.format("No such command %s. Awailable commands are: ", this.command);
+ showAllCommands();
+ return -1;
+ }
+
+ command.printUsage(stderr, command.getCmdLineParser());
return 0;
}
diff --git a/core/src/main/java/hudson/cli/InstallPluginCommand.java b/core/src/main/java/hudson/cli/InstallPluginCommand.java
index eb64582b083ffeba77e6a092ea1dbaf723ccb5ed..1fd23d81acba7de5c3a4050e4925c96b797934b9 100644
--- a/core/src/main/java/hudson/cli/InstallPluginCommand.java
+++ b/core/src/main/java/hudson/cli/InstallPluginCommand.java
@@ -26,7 +26,6 @@ package hudson.cli;
import hudson.Extension;
import hudson.FilePath;
import hudson.PluginManager;
-import hudson.util.IOException2;
import jenkins.model.Jenkins;
import hudson.model.UpdateSite;
import hudson.model.UpdateSite.Data;
@@ -35,6 +34,7 @@ import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import java.io.File;
+import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.HashSet;
@@ -114,7 +114,7 @@ public class InstallPluginCommand extends CLICommand {
stdout.println(Messages.InstallPluginCommand_InstallingFromUpdateCenter(source));
Throwable e = p.deploy(dynamicLoad).get().getError();
if (e!=null)
- throw new IOException2("Failed to install plugin "+source,e);
+ throw new IOException("Failed to install plugin "+source,e);
continue;
}
@@ -141,7 +141,7 @@ public class InstallPluginCommand extends CLICommand {
}
if (restart)
- h.restart();
+ h.safeRestart();
return 0; // all success
}
diff --git a/core/src/main/java/hudson/cli/InstallToolCommand.java b/core/src/main/java/hudson/cli/InstallToolCommand.java
index fb30efd0c08b52798e618e95f1dce303aefca908..f5ad164f95e2a6dcbf5ad0e9d49160fce2f5fb3e 100644
--- a/core/src/main/java/hudson/cli/InstallToolCommand.java
+++ b/core/src/main/java/hudson/cli/InstallToolCommand.java
@@ -122,6 +122,9 @@ public class InstallToolCommand extends CLICommand {
throw new AbortException(b.getFullDisplayName()+" is not building");
Node node = exec.getOwner().getNode();
+ if (node == null) {
+ throw new AbortException("The node " + exec.getOwner().getDisplayName() + " has been deleted");
+ }
t = t.translate(node, EnvVars.getRemote(checkChannel()), new StreamTaskListener(stderr));
stdout.println(t.getHome());
diff --git a/core/src/main/java/hudson/cli/ListJobsCommand.java b/core/src/main/java/hudson/cli/ListJobsCommand.java
index af858d3f04e6d3de3d6ca97a454685188d4e5467..bdb64208d5c827c71968be3ada50902960a28a78 100644
--- a/core/src/main/java/hudson/cli/ListJobsCommand.java
+++ b/core/src/main/java/hudson/cli/ListJobsCommand.java
@@ -24,16 +24,13 @@
package hudson.cli;
import java.util.Collection;
-import java.util.LinkedHashSet;
-
-import java.util.Collections;
import hudson.model.Item;
-import hudson.model.ItemGroup;
+import hudson.model.Items;
import hudson.model.TopLevelItem;
-import hudson.model.ViewGroup;
import hudson.model.View;
import hudson.Extension;
+import jenkins.model.ModifiableTopLevelItemGroup;
import jenkins.model.Jenkins;
import org.kohsuke.args4j.Argument;
@@ -68,9 +65,8 @@ public class ListJobsCommand extends CLICommand {
final Item item = h.getItemByFullName(name);
// If item group was found use it's jobs.
- if (item instanceof ItemGroup) {
- ItemGroup itemGroup = (ItemGroup) item;
- jobs = itemGroup.getItems();
+ if (item instanceof ModifiableTopLevelItemGroup) {
+ jobs = Items.getAllItems((ModifiableTopLevelItemGroup) item, TopLevelItem.class);
}
// No view and no item group with the given name found.
else {
diff --git a/core/src/main/java/hudson/cli/RemoveJobFromViewCommand.java b/core/src/main/java/hudson/cli/RemoveJobFromViewCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..8c317d908f97bccfe39c14f8434a2167e1814249
--- /dev/null
+++ b/core/src/main/java/hudson/cli/RemoveJobFromViewCommand.java
@@ -0,0 +1,68 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2014 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson.cli;
+
+import java.util.List;
+
+import hudson.Extension;
+import hudson.model.TopLevelItem;
+import hudson.model.DirectlyModifiableView;
+import hudson.model.View;
+
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
+
+/**
+ * @author ogondza
+ * @since 1.570
+ */
+@Extension
+public class RemoveJobFromViewCommand extends CLICommand {
+
+ @Argument(usage="Name of the view", required=true, index=0)
+ private View view;
+
+ @Argument(usage="Job names", required=true, index=1)
+ private List jobs;
+
+ @Override
+ public String getShortDescription() {
+ return Messages.RemoveJobFromViewCommand_ShortDescription();
+ }
+
+ @Override
+ protected int run() throws Exception {
+ view.checkPermission(View.CONFIGURE);
+
+ if (!(view instanceof DirectlyModifiableView)) throw new CmdLineException(
+ null, "'" + view.getDisplayName() + "' view can not be modified directly"
+ );
+
+ for (TopLevelItem job: jobs) {
+ ((DirectlyModifiableView) view).remove(job);
+ }
+
+ return 0;
+ }
+}
diff --git a/core/src/main/java/hudson/cli/SessionIdCommand.java b/core/src/main/java/hudson/cli/SessionIdCommand.java
index 5a38f489228d42435a8c2358ca28e54c36f5d100..5e45dab55d57a678f83d3e7f6f6a8edb08966549 100644
--- a/core/src/main/java/hudson/cli/SessionIdCommand.java
+++ b/core/src/main/java/hudson/cli/SessionIdCommand.java
@@ -13,7 +13,7 @@ import jenkins.model.Jenkins;
public class SessionIdCommand extends CLICommand {
@Override
public String getShortDescription() {
- return "Outputs the session ID, which changes every time Jenkins restarts";
+ return Messages.SessionIdCommand_ShortDescription();
}
protected int run() {
diff --git a/core/src/main/java/hudson/cli/SetBuildDisplayNameCommand.java b/core/src/main/java/hudson/cli/SetBuildDisplayNameCommand.java
index 5233cf7f7f5f408a685bb93bf1fb61f7a11befbb..08b08b7d7166b510e545d598cfddbad85c299ad7 100644
--- a/core/src/main/java/hudson/cli/SetBuildDisplayNameCommand.java
+++ b/core/src/main/java/hudson/cli/SetBuildDisplayNameCommand.java
@@ -3,15 +3,14 @@ package hudson.cli;
import hudson.Extension;
import hudson.model.AbstractProject;
import hudson.model.Run;
-import hudson.remoting.Callable;
import org.apache.commons.io.IOUtils;
import org.kohsuke.args4j.Argument;
-import java.io.IOException;
import java.io.Serializable;
@Extension
public class SetBuildDisplayNameCommand extends CLICommand implements Serializable {
+ private static final long serialVersionUID = 6665171784136358536L;
@Override
public String getShortDescription() {
@@ -27,8 +26,13 @@ public class SetBuildDisplayNameCommand extends CLICommand implements Serializab
@Argument(metaVar="DISPLAYNAME", required=true, usage="DisplayName to be set. '-' to read from stdin.", index=2)
public String displayName;
+ @Override
protected int run() throws Exception {
- Run run = job.getBuildByNumber(number);
+ Run, ?> run = job.getBuildByNumber(number);
+ if (run == null) {
+ stderr.format("Build #%d does not exist\n", number);
+ return -1;
+ }
run.checkPermission(Run.UPDATE);
if ("-".equals(displayName)) {
@@ -39,5 +43,4 @@ public class SetBuildDisplayNameCommand extends CLICommand implements Serializab
return 0;
}
-
}
diff --git a/core/src/main/java/hudson/cli/SetBuildParameterCommand.java b/core/src/main/java/hudson/cli/SetBuildParameterCommand.java
index 2862e9e8d79f8e6c65da69f4f7cb917f26a98aae..5676bb52643d01b2ebb12f03c131dfe019c1e2b6 100644
--- a/core/src/main/java/hudson/cli/SetBuildParameterCommand.java
+++ b/core/src/main/java/hudson/cli/SetBuildParameterCommand.java
@@ -18,10 +18,10 @@ import java.util.Collections;
*/
@Extension
public class SetBuildParameterCommand extends CommandDuringBuild {
- @Argument(index=0, required=true, usage="Name of the build variable")
+ @Argument(index=0, metaVar="NAME", required=true, usage="Name of the build variable")
public String name;
- @Argument(index=1,required=true, usage="Value of the build variable")
+ @Argument(index=1, metaVar="VALUE", required=true, usage="Value of the build variable")
public String value;
@Override
@@ -37,9 +37,7 @@ public class SetBuildParameterCommand extends CommandDuringBuild {
ParametersAction a = r.getAction(ParametersAction.class);
if (a!=null) {
- ParametersAction b = a.createUpdated(Collections.singleton(p));
- r.addAction(b);
- r.getActions().remove(a);
+ r.replaceAction(a.createUpdated(Collections.singleton(p)));
} else {
r.addAction(new ParametersAction(p));
}
diff --git a/core/src/main/java/hudson/cli/UpdateJobCommand.java b/core/src/main/java/hudson/cli/UpdateJobCommand.java
index eaea4227725943861748e87bae1f133bb5651503..a9a34403ec03699296d900e103a498efdccfa008 100644
--- a/core/src/main/java/hudson/cli/UpdateJobCommand.java
+++ b/core/src/main/java/hudson/cli/UpdateJobCommand.java
@@ -24,7 +24,7 @@
package hudson.cli;
import hudson.Extension;
-import hudson.model.AbstractProject;
+import hudson.model.AbstractItem;
import org.kohsuke.args4j.Argument;
import javax.xml.transform.Source;
@@ -36,7 +36,7 @@ import javax.xml.transform.stream.StreamSource;
@Extension
public class UpdateJobCommand extends CLICommand {
@Argument(metaVar="JOB",usage="Name of the job",required=true)
- public AbstractProject,?> job;
+ public AbstractItem job;
@Override
public String getShortDescription() {
diff --git a/core/src/main/java/hudson/cli/UpdateViewCommand.java b/core/src/main/java/hudson/cli/UpdateViewCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f861fcb04d991d1d3a7ea49f3180994878cebdb
--- /dev/null
+++ b/core/src/main/java/hudson/cli/UpdateViewCommand.java
@@ -0,0 +1,55 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson.cli;
+
+import javax.xml.transform.stream.StreamSource;
+
+import hudson.Extension;
+import hudson.model.View;
+
+import org.kohsuke.args4j.Argument;
+
+/**
+ * @author ogondza
+ * @since 1.538
+ */
+@Extension
+public class UpdateViewCommand extends CLICommand {
+
+ @Argument(usage="Name of the view to update", required=true)
+ private View view;
+
+ @Override
+ public String getShortDescription() {
+
+ return Messages.UpdateViewCommand_ShortDescription();
+ }
+
+ @Override
+ protected int run() throws Exception {
+
+ view.updateByXml(new StreamSource(stdin));
+ return 0;
+ }
+}
diff --git a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java
index 9cc5448616d51fb0ead5aa7e9edd41449099bacd..cb1d1db8f2386c23f3f10a8c7b7cd2ee2e97de57 100644
--- a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java
+++ b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java
@@ -110,48 +110,65 @@ public class CLIRegisterer extends ExtensionFinder {
return name;
}
+ @Override
public String getShortDescription() {
// format by using the right locale
return res.format("CLI."+name+".shortDescription");
}
+ @Override
+ protected CmdLineParser getCmdLineParser() {
+ return bindMethod(new ArrayList());
+ }
+
+ private CmdLineParser bindMethod(List binders) {
+
+ registerOptionHandlers();
+ CmdLineParser parser = new CmdLineParser(null);
+
+ // build up the call sequence
+ Stack chains = new Stack();
+ Method method = m;
+ while (true) {
+ chains.push(method);
+ if (Modifier.isStatic(method.getModifiers()))
+ break; // the chain is complete.
+
+ // the method in question is an instance method, so we need to resolve the instance by using another resolver
+ Class> type = method.getDeclaringClass();
+ try {
+ method = findResolver(type);
+ } catch (IOException ex) {
+ throw new RuntimeException("Unable to find the resolver method annotated with @CLIResolver for "+type, ex);
+ }
+ if (method==null) {
+ throw new RuntimeException("Unable to find the resolver method annotated with @CLIResolver for "+type);
+ }
+ }
+
+ while (!chains.isEmpty())
+ binders.add(new MethodBinder(chains.pop(),this,parser));
+
+ new ClassParser().parse(Jenkins.getInstance().getSecurityRealm().createCliAuthenticator(this), parser);
+
+ return parser;
+ }
+
@Override
public int main(List args, Locale locale, InputStream stdin, PrintStream stdout, PrintStream stderr) {
this.stdout = stdout;
this.stderr = stderr;
this.locale = locale;
- registerOptionHandlers();
- CmdLineParser parser = new CmdLineParser(null);
+ List binders = new ArrayList();
+
+ CmdLineParser parser = bindMethod(binders);
try {
SecurityContext sc = SecurityContextHolder.getContext();
Authentication old = sc.getAuthentication();
try {
- // build up the call sequence
- Stack chains = new Stack();
- Method method = m;
- while (true) {
- chains.push(method);
- if (Modifier.isStatic(method.getModifiers()))
- break; // the chain is complete.
-
- // the method in question is an instance method, so we need to resolve the instance by using another resolver
- Class> type = method.getDeclaringClass();
- method = findResolver(type);
- if (method==null) {
- stderr.println("Unable to find the resolver method annotated with @CLIResolver for "+type);
- return 1;
- }
- }
-
- List binders = new ArrayList();
-
- while (!chains.isEmpty())
- binders.add(new MethodBinder(chains.pop(),this,parser));
-
// authentication
CliAuthenticator authenticator = Jenkins.getInstance().getSecurityRealm().createCliAuthenticator(this);
- new ClassParser().parse(authenticator,parser);
// fill up all the binders
parser.parseArgument(args);
diff --git a/core/src/main/java/hudson/cli/declarative/OptionHandlerExtension.java b/core/src/main/java/hudson/cli/declarative/OptionHandlerExtension.java
index b7a18e305b64c447bc58935e993a530708fce693..7e76e117d2b6260bbdd078f1bcaa1f5d193ac84a 100644
--- a/core/src/main/java/hudson/cli/declarative/OptionHandlerExtension.java
+++ b/core/src/main/java/hudson/cli/declarative/OptionHandlerExtension.java
@@ -36,7 +36,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* {@link OptionHandler}s that should be auto-discovered.
- *
+ * TODO is this actually necessary? {@code @MetaInfServices(OptionHandler.class)} seems to work as well.
* @author Kohsuke Kawaguchi
*/
@Indexed
diff --git a/core/src/main/java/hudson/cli/handlers/AbstractItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/AbstractItemOptionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..c99f3090799e291487bff797f9f2b457c26bfb84
--- /dev/null
+++ b/core/src/main/java/hudson/cli/handlers/AbstractItemOptionHandler.java
@@ -0,0 +1,48 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Jesse Glick.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package hudson.cli.handlers;
+
+import hudson.model.AbstractItem;
+import org.kohsuke.MetaInfServices;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Setter;
+
+/**
+ * Refers to an {@link AbstractItem} by name.
+ * @since 1.538
+ */
+@MetaInfServices(OptionHandler.class) public class AbstractItemOptionHandler extends GenericItemOptionHandler {
+
+ public AbstractItemOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) {
+ super(parser, option, setter);
+ }
+
+ @Override protected Class type() {
+ return AbstractItem.class;
+ }
+
+}
diff --git a/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java b/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java
index 0dc515751ca8e16b7c78d92c8327dd7ff0c0d83e..b630e48b56abe656345304b8a7f7acc339c1cfe2 100644
--- a/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java
+++ b/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java
@@ -24,42 +24,26 @@
package hudson.cli.handlers;
import hudson.model.AbstractProject;
-import jenkins.model.Jenkins;
-import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;
import org.kohsuke.MetaInfServices;
+import org.kohsuke.args4j.spi.OptionHandler;
/**
* Refer to {@link AbstractProject} by its name.
*
* @author Kohsuke Kawaguchi
*/
-@MetaInfServices
+@MetaInfServices(OptionHandler.class)
@SuppressWarnings("rawtypes")
-public class AbstractProjectOptionHandler extends OptionHandler {
+public class AbstractProjectOptionHandler extends GenericItemOptionHandler {
public AbstractProjectOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) {
super(parser, option, setter);
}
- @Override
- public int parseArguments(Parameters params) throws CmdLineException {
- Jenkins h = Jenkins.getInstance();
- String src = params.getParameter(0);
-
- AbstractProject s = h.getItemByFullName(src,AbstractProject.class);
- if (s==null) {
- AbstractProject nearest = AbstractProject.findNearest(src);
- if (nearest!=null)
- throw new CmdLineException(owner, "No such job '"+src+"' perhaps you meant '"+ nearest.getFullName() +"'?");
- else
- throw new CmdLineException(owner, "No such job '"+src+"'");
- }
- setter.addValue(s);
- return 1;
+ @Override protected Class type() {
+ return AbstractProject.class;
}
@Override
diff --git a/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..e27bb32dc8709b8972c64cd7db80b24b0c61634f
--- /dev/null
+++ b/core/src/main/java/hudson/cli/handlers/GenericItemOptionHandler.java
@@ -0,0 +1,89 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Jesse Glick.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package hudson.cli.handlers;
+
+import hudson.model.Item;
+import hudson.model.Items;
+import hudson.security.ACL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import jenkins.model.Jenkins;
+import org.acegisecurity.Authentication;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+/**
+ * Refers to an {@link Item} by its name.
+ * May be subclassed to handle specific kinds of items.
+ * (Use {@code @MetaInfServices(OptionHandler.class)} to register the subclass.)
+ * @param the kind of item being handled
+ * @since 1.538
+ */
+public abstract class GenericItemOptionHandler extends OptionHandler {
+
+ private static final Logger LOGGER = Logger.getLogger(GenericItemOptionHandler.class.getName());
+
+ protected GenericItemOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) {
+ super(parser, option, setter);
+ }
+
+ protected abstract Class type();
+
+ @Override public int parseArguments(Parameters params) throws CmdLineException {
+ final Jenkins j = Jenkins.getInstance();
+ final String src = params.getParameter(0);
+ T s = j.getItemByFullName(src, type());
+ if (s == null) {
+ final Authentication who = Jenkins.getAuthentication();
+ ACL.impersonate(ACL.SYSTEM, new Runnable() {
+ @Override public void run() {
+ Item actual = j.getItemByFullName(src);
+ if (actual == null) {
+ LOGGER.log(Level.FINE, "really no item exists named {0}", src);
+ } else {
+ LOGGER.log(Level.WARNING, "running as {0} could not find {1} of {2}", new Object[] {who.getPrincipal(), actual, type()});
+ }
+ }
+ });
+ T nearest = Items.findNearest(type(), src, j);
+ if (nearest != null) {
+ throw new CmdLineException(owner, "No such job '" + src + "'; perhaps you meant '" + nearest.getFullName() + "'?");
+ } else {
+ throw new CmdLineException(owner, "No such job '" + src + "'");
+ }
+ }
+ setter.addValue(s);
+ return 1;
+ }
+
+ @Override public String getDefaultMetaVariable() {
+ return "ITEM";
+ }
+
+}
diff --git a/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java
index 0c4a4ea97869db3f6976869405256a328c0b128e..97fdf4b8c5b648585af94cb233a515529462fb3d 100644
--- a/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java
+++ b/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java
@@ -1,14 +1,10 @@
package hudson.cli.handlers;
-import hudson.model.AbstractProject;
-import jenkins.model.Jenkins;
import hudson.model.TopLevelItem;
import org.kohsuke.MetaInfServices;
-import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.OptionDef;
import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;
/**
@@ -16,33 +12,18 @@ import org.kohsuke.args4j.spi.Setter;
*
* @author Kohsuke Kawaguchi
*/
-@MetaInfServices
-public class TopLevelItemOptionHandler extends OptionHandler {
+@MetaInfServices(OptionHandler.class)
+public class TopLevelItemOptionHandler extends GenericItemOptionHandler {
public TopLevelItemOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) {
super(parser, option, setter);
}
- @Override
- @SuppressWarnings("rawtypes")
- public int parseArguments(Parameters params) throws CmdLineException {
- Jenkins h = Jenkins.getInstance();
- String src = params.getParameter(0);
-
- TopLevelItem s = h.getItemByFullName(src, TopLevelItem.class);
- if (s==null) {
- AbstractProject nearest = AbstractProject.findNearest(src);
- if (nearest!=null)
- throw new CmdLineException(owner, "No such job '"+src+"' perhaps you meant '"+ nearest.getFullName() +"'?");
- else
- throw new CmdLineException(owner, "No such job '"+src+"'");
- }
-
- setter.addValue(s);
- return 1;
+ @Override protected Class type() {
+ return TopLevelItem.class;
}
@Override
public String getDefaultMetaVariable() {
- return "JOB";
+ return "JOB"; // TODO or should we pick up default value, ITEM?
}
}
diff --git a/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..387621ae56b570e70731cbf7f6a6f6a98e755d41
--- /dev/null
+++ b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
@@ -0,0 +1,111 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2013, Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson.cli.handlers;
+
+import hudson.model.ViewGroup;
+import hudson.model.View;
+
+import java.util.StringTokenizer;
+
+import jenkins.model.Jenkins;
+
+import org.kohsuke.MetaInfServices;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+/**
+ * Refers to {@link View} by its name.
+ *
+ *
+ * For example:
+ *
+ *
my_view_name
refers to a top level view with given name.
+ *
nested/inner
refers to a view named inner inside of a top level view group named nested.
+ *
+ *
+ *
+ * View name is a non-empty sequence of {@link View} names delimited by '/'.
+ * Handler traverse the view names from left to right. First one is expected to
+ * be a top level view and all but the last one are expected to be instances of
+ * {@link ViewGroup}. Handler fails to resolve view provided a view with given
+ * name does not exist or a user was not granted {@link View#READ} permission.
+ *
+ * @author ogondza
+ * @since 1.538
+ */
+@MetaInfServices
+public class ViewOptionHandler extends OptionHandler {
+
+ public ViewOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) {
+
+ super(parser, option, setter);
+ }
+
+ @Override
+ public int parseArguments(Parameters params) throws CmdLineException {
+
+ setter.addValue(getView(params.getParameter(0)));
+ return 1;
+ }
+
+ private View getView(String name) throws CmdLineException {
+
+ View view = null;
+ ViewGroup group = Jenkins.getInstance();
+
+ final StringTokenizer tok = new StringTokenizer(name, "/");
+ while(tok.hasMoreTokens()) {
+
+ String viewName = tok.nextToken();
+
+ view = group.getView(viewName);
+ if (view == null) throw new CmdLineException(owner, String.format(
+ "No view named %s inside view %s",
+ viewName, group.getDisplayName()
+ ));
+
+ view.checkPermission(View.READ);
+
+ if (view instanceof ViewGroup) {
+ group = (ViewGroup) view;
+ } else if (tok.hasMoreTokens()) {
+ throw new CmdLineException(
+ owner, view.getViewName() + " view can not contain views"
+ );
+ }
+ }
+
+ return view;
+ }
+
+ @Override
+ public String getDefaultMetaVariable() {
+
+ return "VIEW";
+ }
+}
diff --git a/core/src/main/java/hudson/console/AnnotatedLargeText.java b/core/src/main/java/hudson/console/AnnotatedLargeText.java
index 298e034bf15ebf31a8f728b5c6bb9e7a6a82946b..4a3af765bdd04dd2831a451db7ccb5cd4d45c631 100644
--- a/core/src/main/java/hudson/console/AnnotatedLargeText.java
+++ b/core/src/main/java/hudson/console/AnnotatedLargeText.java
@@ -28,8 +28,6 @@ package hudson.console;
import com.trilead.ssh2.crypto.Base64;
import jenkins.model.Jenkins;
import hudson.remoting.ObjectInputStreamEx;
-import hudson.util.IOException2;
-import hudson.util.Secret;
import hudson.util.TimeUnit2;
import jenkins.security.CryptoConfidentialKey;
import org.apache.commons.io.output.ByteArrayOutputStream;
@@ -50,7 +48,6 @@ import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.charset.Charset;
-import java.security.GeneralSecurityException;
import com.jcraft.jzlib.GZIPInputStream;
import com.jcraft.jzlib.GZIPOutputStream;
@@ -134,7 +131,7 @@ public class AnnotatedLargeText extends LargeText {
}
}
} catch (ClassNotFoundException e) {
- throw new IOException2(e);
+ throw new IOException(e);
}
// start from scratch
return ConsoleAnnotator.initial(context==null ? null : context.getClass());
diff --git a/core/src/main/java/hudson/console/ConsoleNote.java b/core/src/main/java/hudson/console/ConsoleNote.java
index c8ea004643309d73af2d5638bab3bf3f225d50d2..c27fec63643527d8b9f775f6e1c6b6a739a47f62 100644
--- a/core/src/main/java/hudson/console/ConsoleNote.java
+++ b/core/src/main/java/hudson/console/ConsoleNote.java
@@ -30,7 +30,6 @@ import hudson.model.Describable;
import jenkins.model.Jenkins;
import hudson.model.Run;
import hudson.remoting.ObjectInputStreamEx;
-import hudson.util.IOException2;
import hudson.util.IOUtils;
import hudson.util.UnbufferedBase64InputStream;
import org.apache.commons.codec.binary.Base64OutputStream;
@@ -172,16 +171,22 @@ public abstract class ConsoleNote implements Serializable, Describable implements Serializable, Describable {
* In addition, the last character shouldn't be ',' ':', '"', etc, as often those things show up right next
* to URL in plain text (e.g., test="http://www.example.com/")
*/
- private static final Pattern URL = Pattern.compile("\\b(http|https|ftp)://[^\\s<>]+[^\\s<>,\\.:\"'()\\[\\]=]");
+ private static final Pattern URL = Pattern.compile("\\b(http|https|file|ftp)://[^\\s<>]+[^\\s<>,\\.:\"'()\\[\\]=]");
private static final String OPEN = "'\"()[]<>";
private static final String CLOSE= "'\")(][><";
diff --git a/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageChecker.java b/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageChecker.java
index 3576c4388de7cd6e2c61a82b79dd7467322dcbbf..5cf0c81ee412aca2174c44a2e878daa73ed6f4f4 100644
--- a/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageChecker.java
+++ b/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageChecker.java
@@ -26,7 +26,6 @@ package hudson.diagnosis;
import hudson.Extension;
import jenkins.model.Jenkins;
import hudson.model.PeriodicWork;
-import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
import java.util.logging.Logger;
@@ -42,9 +41,7 @@ public class HudsonHomeDiskUsageChecker extends PeriodicWork {
return HOUR;
}
- @IgnoreJRERequirement
protected void doRun() {
- try {
long free = Jenkins.getInstance().getRootDir().getUsableSpace();
long total = Jenkins.getInstance().getRootDir().getTotalSpace();
if(free<=0 || total<=0) {
@@ -61,11 +58,6 @@ public class HudsonHomeDiskUsageChecker extends PeriodicWork {
// it's AND and not OR so that small Hudson home won't get a warning,
// and similarly, if you have a 1TB disk, you don't get a warning when you still have 100GB to go.
HudsonHomeDiskUsageMonitor.get().activated = (total/free>10 && free< FREE_SPACE_THRESHOLD);
- } catch (LinkageError _) {
- // pre Mustang
- LOGGER.info("Not on JDK6. Cannot monitor JENKINS_HOME disk usage");
- cancel();
- }
}
private static final Logger LOGGER = Logger.getLogger(HudsonHomeDiskUsageChecker.class.getName());
diff --git a/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java b/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java
index a72bcd68b3cb9f62e4b88dfebd1571d1ad6810b3..b37934f0232790b4a36f34567bb8bd9c0de294e9 100644
--- a/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java
+++ b/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java
@@ -55,6 +55,11 @@ public final class HudsonHomeDiskUsageMonitor extends AdministrativeMonitor {
public boolean isActivated() {
return activated;
}
+
+ @Override
+ public String getDisplayName() {
+ return Messages.HudsonHomeDiskUsageMonitor_DisplayName();
+ }
/**
* Depending on whether the user said "yes" or "no", send him to the right place.
diff --git a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
index 4b1a6bebaf628803ec4361d0601f3249832e4eca..b9e18d1b689d04f7d376293b7c0110c16019c81f 100644
--- a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
+++ b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
@@ -23,13 +23,13 @@
*/
package hudson.diagnosis;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import hudson.Extension;
import hudson.XmlFile;
import hudson.model.AdministrativeMonitor;
-import hudson.model.ManagementLink;
-import jenkins.model.Jenkins;
-import hudson.Extension;
import hudson.model.Item;
import hudson.model.Job;
+import hudson.model.ManagementLink;
import hudson.model.Run;
import hudson.model.Saveable;
import hudson.model.listeners.ItemListener;
@@ -37,22 +37,22 @@ import hudson.model.listeners.RunListener;
import hudson.model.listeners.SaveableListener;
import hudson.util.RobustReflectionConverter;
import hudson.util.VersionNumber;
-import org.kohsuke.stapler.StaplerRequest;
-import org.kohsuke.stapler.StaplerResponse;
-import com.thoughtworks.xstream.converters.UnmarshallingContext;
-
import java.io.IOException;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.annotation.CheckForNull;
+import jenkins.model.Jenkins;
import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+import org.kohsuke.stapler.interceptor.RequirePOST;
/**
* Tracks whether any data structure changes were corrected when loading XML,
@@ -62,11 +62,15 @@ import org.kohsuke.stapler.HttpResponses;
*/
@Extension
public class OldDataMonitor extends AdministrativeMonitor {
- private static Logger LOGGER = Logger.getLogger(OldDataMonitor.class.getName());
+ private static final Logger LOGGER = Logger.getLogger(OldDataMonitor.class.getName());
- private HashMap data = new HashMap();
+ private HashMap data = new HashMap();
private boolean updating = false;
+ static OldDataMonitor get(Jenkins j) {
+ return (OldDataMonitor) j.getAdministrativeMonitor("OldData");
+ }
+
public OldDataMonitor() {
super("OldData");
}
@@ -81,17 +85,24 @@ public class OldDataMonitor extends AdministrativeMonitor {
}
public synchronized Map getData() {
- return Collections.unmodifiableMap(data);
+ Map r = new HashMap();
+ for (Map.Entry entry : data.entrySet()) {
+ Saveable s = entry.getKey().get();
+ if (s != null) {
+ r.put(s, entry.getValue());
+ }
+ }
+ return r;
}
private static void remove(Saveable obj, boolean isDelete) {
- OldDataMonitor odm = (OldDataMonitor) Jenkins.getInstance().getAdministrativeMonitor("OldData");
+ OldDataMonitor odm = get(Jenkins.getInstance());
synchronized (odm) {
if (odm.updating) return; // Skip during doUpgrade or doDiscard
- odm.data.remove(obj);
+ odm.data.remove(referTo(obj));
if (isDelete && obj instanceof Job,?>)
for (Run r : ((Job,?>)obj).getBuilds())
- odm.data.remove(r);
+ odm.data.remove(referTo(r));
}
}
@@ -129,12 +140,13 @@ public class OldDataMonitor extends AdministrativeMonitor {
* @param version Hudson release when the data structure changed.
*/
public static void report(Saveable obj, String version) {
- OldDataMonitor odm = (OldDataMonitor) Jenkins.getInstance().getAdministrativeMonitor("OldData");
+ OldDataMonitor odm = get(Jenkins.getInstance());
synchronized (odm) {
try {
- VersionRange vr = odm.data.get(obj);
+ SaveableReference ref = referTo(obj);
+ VersionRange vr = odm.data.get(ref);
if (vr != null) vr.add(version);
- else odm.data.put(obj, new VersionRange(version, null));
+ else odm.data.put(ref, new VersionRange(version, null));
} catch (IllegalArgumentException ex) {
LOGGER.log(Level.WARNING, "Bad parameter given to OldDataMonitor", ex);
}
@@ -175,11 +187,20 @@ public class OldDataMonitor extends AdministrativeMonitor {
}
}
if (buf.length() == 0) return;
- OldDataMonitor odm = (OldDataMonitor) Jenkins.getInstance().getAdministrativeMonitor("OldData");
+ Jenkins j = Jenkins.getInstance();
+ if (j == null) {
+ // Startup failed, something is very broken, so report what we can.
+ for (Throwable t : errors) {
+ LOGGER.log(Level.WARNING, "could not read " + obj + " (and Jenkins did not start up)", t);
+ }
+ return;
+ }
+ OldDataMonitor odm = get(j);
synchronized (odm) {
- VersionRange vr = odm.data.get(obj);
+ SaveableReference ref = referTo(obj);
+ VersionRange vr = odm.data.get(ref);
if (vr != null) vr.extra = buf.toString();
- else odm.data.put(obj, new VersionRange(null, buf.toString()));
+ else odm.data.put(ref, new VersionRange(null, buf.toString()));
}
}
@@ -234,6 +255,7 @@ public class OldDataMonitor extends AdministrativeMonitor {
/**
* Depending on whether the user said "yes" or "no", send him to the right place.
*/
+ @RequirePOST
public HttpResponse doAct(StaplerRequest req, StaplerResponse rsp) throws IOException {
if (req.hasParameter("no")) {
disable(true);
@@ -247,16 +269,17 @@ public class OldDataMonitor extends AdministrativeMonitor {
* Save all or some of the files to persist data in the new forms.
* Remove those items from the data map.
*/
- public synchronized HttpResponse doUpgrade(StaplerRequest req, StaplerResponse rsp) throws IOException {
+ @RequirePOST
+ public synchronized HttpResponse doUpgrade(StaplerRequest req, StaplerResponse rsp) {
String thruVerParam = req.getParameter("thruVer");
VersionNumber thruVer = thruVerParam.equals("all") ? null : new VersionNumber(thruVerParam);
updating = true;
- for (Iterator> it = data.entrySet().iterator(); it.hasNext();) {
- Map.Entry entry = it.next();
+ for (Iterator> it = data.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = it.next();
VersionNumber version = entry.getValue().max;
if (version != null && (thruVer == null || !version.isNewerThan(thruVer))) {
- entry.getKey().save();
it.remove();
+ tryToSave(entry.getKey());
}
}
updating = false;
@@ -267,23 +290,89 @@ public class OldDataMonitor extends AdministrativeMonitor {
* Save all files containing only unreadable data (no data upgrades), which discards this data.
* Remove those items from the data map.
*/
- public synchronized HttpResponse doDiscard(StaplerRequest req, StaplerResponse rsp) throws IOException {
+ @RequirePOST
+ public synchronized HttpResponse doDiscard(StaplerRequest req, StaplerResponse rsp) {
updating = true;
- for (Iterator> it = data.entrySet().iterator(); it.hasNext();) {
- Map.Entry entry = it.next();
+ for (Iterator> it = data.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = it.next();
if (entry.getValue().max == null) {
- entry.getKey().save();
it.remove();
+ tryToSave(entry.getKey());
}
}
updating = false;
return HttpResponses.forwardToPreviousPage();
}
+ private void tryToSave(SaveableReference ref) {
+ Saveable s = ref.get();
+ if (s != null) {
+ try {
+ s.save();
+ } catch (Exception x) {
+ LOGGER.log(Level.WARNING, "failed to save " + s, x);
+ }
+ }
+ }
+
public HttpResponse doIndex(StaplerResponse rsp) throws IOException {
return new HttpRedirect("manage");
}
+ /** Reference to a saveable object that need not actually hold it in heap. */
+ private interface SaveableReference {
+ @CheckForNull Saveable get();
+ // must also define equals, hashCode
+ }
+
+ private static SaveableReference referTo(Saveable s) {
+ if (s instanceof Run) {
+ return new RunSaveableReference((Run) s);
+ } else {
+ return new SimpleSaveableReference(s);
+ }
+ }
+
+ private static final class SimpleSaveableReference implements SaveableReference {
+ private final Saveable instance;
+ SimpleSaveableReference(Saveable instance) {
+ this.instance = instance;
+ }
+ @Override public Saveable get() {
+ return instance;
+ }
+ @Override public int hashCode() {
+ return instance.hashCode();
+ }
+ @Override public boolean equals(Object obj) {
+ return obj instanceof SimpleSaveableReference && instance.equals(((SimpleSaveableReference) obj).instance);
+ }
+ }
+
+ // could easily make an ItemSaveableReference, but Jenkins holds all these strongly, so why bother
+
+ private static final class RunSaveableReference implements SaveableReference {
+ private final String id;
+ RunSaveableReference(Run,?> r) {
+ id = r.getExternalizableId();
+ }
+ @Override public Saveable get() {
+ try {
+ return Run.fromExternalizableId(id);
+ } catch (IllegalArgumentException x) {
+ // Typically meaning the job or build was since deleted.
+ LOGGER.log(Level.FINE, null, x);
+ return null;
+ }
+ }
+ @Override public int hashCode() {
+ return id.hashCode();
+ }
+ @Override public boolean equals(Object obj) {
+ return obj instanceof RunSaveableReference && id.equals(((RunSaveableReference) obj).id);
+ }
+ }
+
@Extension
public static class ManagementLinkImpl extends ManagementLink {
@Override
diff --git a/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java b/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java
index 3fa520808c11f19817c71b8c72fcb07fa94a1c98..79e1685f6014b139451f17e75ed168bbbcfde750 100644
--- a/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java
+++ b/core/src/main/java/hudson/diagnosis/ReverseProxySetupMonitor.java
@@ -24,15 +24,18 @@
package hudson.diagnosis;
import hudson.Extension;
+import hudson.Util;
import hudson.model.AdministrativeMonitor;
-import hudson.util.FormValidation;
import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.QueryParameter;
-import org.kohsuke.stapler.WebMethod;
import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import jenkins.model.Jenkins;
+import org.kohsuke.stapler.Stapler;
/**
* Looks out for a broken reverse proxy setup that doesn't rewrite the location header correctly.
@@ -47,6 +50,9 @@ import java.io.IOException;
*/
@Extension
public class ReverseProxySetupMonitor extends AdministrativeMonitor {
+
+ private static final Logger LOGGER = Logger.getLogger(ReverseProxySetupMonitor.class.getName());
+
@Override
public boolean isActivated() {
// return true to always inject an HTML fragment to perform a test
@@ -54,12 +60,26 @@ public class ReverseProxySetupMonitor extends AdministrativeMonitor {
}
public HttpResponse doTest() {
- return new HttpRedirect("test-for-reverse-proxy-setup");
+ String referer = Stapler.getCurrentRequest().getReferer();
+ Jenkins j = Jenkins.getInstance();
+ assert j != null;
+ // May need to send an absolute URL, since handling of HttpRedirect with a relative URL does not currently honor X-Forwarded-Proto/Port at all.
+ String redirect = j.getRootUrl() + "administrativeMonitor/" + id + "/testForReverseProxySetup/" + (referer != null ? Util.rawEncode(referer) : "NO-REFERER") + "/";
+ LOGGER.log(Level.FINE, "coming from {0} and redirecting to {1}", new Object[] {referer, redirect});
+ return new HttpRedirect(redirect);
}
- @WebMethod(name="test-for-reverse-proxy-setup")
- public FormValidation doFoo() {
- return FormValidation.ok();
+ public void getTestForReverseProxySetup(String rest) {
+ Jenkins j = Jenkins.getInstance();
+ assert j != null;
+ String inferred = j.getRootUrlFromRequest() + "manage";
+ // TODO this could also verify that j.getRootUrl() has been properly configured, and send a different message if not
+ if (rest.equals(inferred)) {
+ throw HttpResponses.ok();
+ } else {
+ LOGGER.log(Level.WARNING, "{0} vs. {1}", new Object[] {inferred, rest});
+ throw HttpResponses.errorWithoutStack(404, inferred + " vs. " + rest);
+ }
}
/**
diff --git a/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java b/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java
index 140220c54d99b4e60ebf9aec31fa90f6aa4a8b90..956f4c946412a704feb491dd7b289b45a0236b43 100644
--- a/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java
+++ b/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java
@@ -120,15 +120,15 @@ public class WorkspaceSnapshotSCM extends SCM {
return new Snapshot(snapshot,b);
}
- public SCMRevisionState calcRevisionsFromBuild(AbstractBuild, ?> build, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
+ @Override public SCMRevisionState calcRevisionsFromBuild(AbstractBuild, ?> build, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
return null;
}
- protected PollingResult compareRemoteRevisionWith(AbstractProject project, Launcher launcher, FilePath workspace, TaskListener listener, SCMRevisionState baseline) throws IOException, InterruptedException {
+ @Override protected PollingResult compareRemoteRevisionWith(AbstractProject project, Launcher launcher, FilePath workspace, TaskListener listener, SCMRevisionState baseline) throws IOException, InterruptedException {
return PollingResult.NO_CHANGES;
}
- public boolean checkout(AbstractBuild build, Launcher launcher, FilePath workspace, BuildListener listener, File changelogFile) throws IOException, InterruptedException {
+ @Override public boolean checkout(AbstractBuild build, Launcher launcher, FilePath workspace, BuildListener listener, File changelogFile) throws IOException, InterruptedException {
try {
resolve().restoreTo(workspace,listener);
return true;
@@ -139,11 +139,11 @@ public class WorkspaceSnapshotSCM extends SCM {
}
}
- public ChangeLogParser createChangeLogParser() {
+ @Override public ChangeLogParser createChangeLogParser() {
return null;
}
- public SCMDescriptor> getDescriptor() {
+ @Override public SCMDescriptor> getDescriptor() {
return null;
}
}
diff --git a/core/src/main/java/hudson/init/InitializerFinder.java b/core/src/main/java/hudson/init/InitializerFinder.java
index 4fdd64a299e035d25988ac7cc1e83a2e6752e1ef..8b11b5740c509271f9072e246c6aa83919dd4b03 100644
--- a/core/src/main/java/hudson/init/InitializerFinder.java
+++ b/core/src/main/java/hudson/init/InitializerFinder.java
@@ -23,184 +23,50 @@
*/
package hudson.init;
-import hudson.model.Hudson;
-import jenkins.model.Jenkins;
-import org.jvnet.hudson.annotation_indexer.Index;
import org.jvnet.hudson.reactor.Milestone;
-import org.jvnet.hudson.reactor.Task;
-import org.jvnet.hudson.reactor.TaskBuilder;
-import org.jvnet.hudson.reactor.MilestoneImpl;
-import org.jvnet.hudson.reactor.Reactor;
-import org.jvnet.localizer.ResourceBundleHolder;
-
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.MissingResourceException;
-import java.util.Set;
-import java.util.logging.Logger;
-
-import static java.util.logging.Level.WARNING;
/**
* Discovers initialization tasks from {@link Initializer}.
*
* @author Kohsuke Kawaguchi
*/
-public class InitializerFinder extends TaskBuilder {
- private final ClassLoader cl;
-
- private final Set discovered = new HashSet();
+public class InitializerFinder extends TaskMethodFinder {
public InitializerFinder(ClassLoader cl) {
- this.cl = cl;
+ super(Initializer.class,InitMilestone.class,cl);
}
public InitializerFinder() {
this(Thread.currentThread().getContextClassLoader());
}
- public Collection discoverTasks(Reactor session) throws IOException {
- List result = new ArrayList();
- for (Method e : Index.list(Initializer.class,cl,Method.class)) {
- if (filter(e)) continue; // already reported once
-
- if (!Modifier.isStatic(e.getModifiers()))
- throw new IOException(e+" is not a static method");
-
- Initializer i = e.getAnnotation(Initializer.class);
- if (i==null) continue; // stale index
-
- result.add(new TaskImpl(i, e));
- }
- return result;
+ @Override
+ protected String displayNameOf(Initializer i) {
+ return i.displayName();
}
- /**
- * Return true to ignore this method.
- */
- protected boolean filter(Method e) {
- return !discovered.add(e);
+ @Override
+ protected String[] requiresOf(Initializer i) {
+ return i.requires();
}
- /**
- * Obtains the display name of the given initialization task
- */
- protected String getDisplayNameOf(Method e, Initializer i) {
- Class> c = e.getDeclaringClass();
- String key = i.displayName();
- if (key.length()==0) return c.getSimpleName()+"."+e.getName();
- try {
- ResourceBundleHolder rb = ResourceBundleHolder.get(
- c.getClassLoader().loadClass(c.getPackage().getName() + ".Messages"));
- return rb.format(key);
- } catch (ClassNotFoundException x) {
- LOGGER.log(WARNING, "Failed to load "+x.getMessage()+" for "+e.toString(),x);
- return key;
- } catch (MissingResourceException x) {
- LOGGER.log(WARNING, "Could not find key '" + key + "' in " + c.getPackage().getName() + ".Messages", x);
- return key;
- }
+ @Override
+ protected String[] attainsOf(Initializer i) {
+ return i.attains();
}
- /**
- * Invokes the given initialization method.
- */
- protected void invoke(Method e) {
- try {
- Class>[] pt = e.getParameterTypes();
- Object[] args = new Object[pt.length];
- for (int i=0; i type) {
- if (type==Jenkins.class || type==Hudson.class)
- return Jenkins.getInstance();
- throw new IllegalArgumentException("Unable to inject "+type);
+ @Override
+ protected Milestone beforeOf(Initializer i) {
+ return i.before();
}
- /**
- * Task implementation.
- */
- public class TaskImpl implements Task {
- final Collection requires;
- final Collection attains;
- private final Initializer i;
- private final Method e;
-
- private TaskImpl(Initializer i, Method e) {
- this.i = i;
- this.e = e;
- requires = toMilestones(i.requires(), i.after());
- attains = toMilestones(i.attains(), i.before());
- }
-
- /**
- * {@link Initializer} annotaion on the {@linkplain #getMethod() method}
- */
- public Initializer getAnnotation() {
- return i;
- }
-
- /**
- * Static method that runs the initialization, that this task wraps.
- */
- public Method getMethod() {
- return e;
- }
-
- public Collection