Legend:
- major enhancement enhancement
- major bug fix bug fix
+ major enhancement enhancement
+ major bug fix bug fix
xxxxx
@@ -39,168 +39,863 @@ Some tips:
Help other Jenkins users by letting the community know which releases you've used,
and whether they had any significant issues.
Legend:
- = I use it on my production site without major issues.
- = I don't recommend it.
- = I tried it but rolled back to a previous version.
View ratings below, and click one of the icons next to your version to provide your input.
-
+
Upcoming changesCommunity ratings
+ Added support of default values in the enum.jelly form element.
+ (PR 1926)
+
+ Bytecode Compatibility Transformer computes the common super class without loading classes.
+ Fixes the ClassCircularityError exception in Ruby Runtime Plugin.
+ (issue 31019)
+
+ Extended Choice parameter definitions could not be saved since 1.637.
+ (issue 31458)
+
+ Display expected CRON run times even if a warning occurs.
+ (issue 29059)
+
+ Rework the online-node command implementation, no functional changes.
+ (issue 31776)
+
+ Revert trigger optimizations made in 1.621 by PR 1617.
+ (issue 30745)
+
+ Delegate CLI's delete-node command to the overridable Computer.doDoDelete() method.
+ Fixes the issue in OpenStack and JClouds plugins.
+ (issue 31098, regression in 1.618)
+
+ Prevent autocorrect of username on mobile devices in login forms.
+ (PR 1531)
+
+ Describe the built-in JDK as "(System)".
+ (issue 755)
+
+ Update JNA library to 4.2.1 in order to integrate fixes for linux-ppc64 and linux-arm platforms.
+ (issue 15792)
+
+ Race condition in triggers could cause various NullPointerExceptions.
+ (issue 29790)
- Animated ball in job's build history widget won't open Console Output.
- (issue 26365)
+ Archiving of large artifacts. Tar implementation cannot handle files having a size >8GB.
+ (issue 10629)
+
+ Allow plugins to augment or replace the plugin manager UI.
+ (PR 1788)
- Build format change migrator in 1.597 did not work on some Windows systems.
- (issue 26519)
+
+ RunIdMigrator fails to revert Matrix and Maven jobs.
+ (issue 29989)
- FutureImpl does not cancel its start future.
- (issue 25514)
+ Fix error message "Failed to listen to incoming slave connection" after fixing port through init.groovy.d.
+ (issue 29798)
+
+ Jenkins now support self-restart and daemonization in FreeBSD
+ (PR 1770)
- Flyweight tasks were under some conditions actually being run on heavyweight executors.
- (issue 10944)
- (issue 24519)
+ Node provisioner may fail to correctly indicate that provisioning was finished.
+ (issue 29568)
+
- Folder loading broken when child item loading throws exception.
- (issue 22811)
+ Sort by 'Free Disk Space' is incorrect.
+ (issue 29286)
- Plugin icon images were broken when running Jenkins from a UNC path.
- (issue 26203)
+ Label expression help is missing in recent Jenkins versions.
+ (issue 29376)
- Allow admin signup from /manage as well.
- (issue 26382)
+ Pre-emptively break memory cycles causing excessive live-set retention in remoting layer.
+ (issue 28844)
+
+ Don't run trigger for disabled/copied projects.
+ (PR 1617)
+
- CLI list-jobs command should display raw name, not display name, where they differ.
- (issue 25338)
+ Update auto-installer metadata for newly installed plugins.
+ (issue 27694)
- Show queue item parameters in tool tip.
- (issue 22311)
+ Allow plugins to veto process killing.
+ (issue 9104)
+
- JENKINS_HOME layout change: builds are now keyed by build numbers and not timestamps.
- See Wiki for details
- and downgrade.
- (issue 24380)
+ Prevent NullPointerException in Executor/causeOfDeath page if
+ there is no exception details.
+ (issue 25734)
- Do not throw exception on /signup when not possible.
- (issue 11172)
+ Fixed synchronization issue when setting JDK installations.
+ (issue 28292)
- Tool installer which downloads and unpacks archives should not fail the build if the tool already exists and the server returns an error code.
- (issue 26196)
+ Fix several loggers which are identifying as the wrong class.
+ (PR 1651)
- Fingerprint compaction aggravated lazy-loading performance issues.
- (issue 19392)
+ Revert fix for issue 17290 due to the regressions it caused.
+ (issue 28601)
- Possible unreleased workspace lock if SCM polling fails during setup.
- (issue 26201)
+ Fix deadlock between hudson.model.Queue and hudson.model.Computer.
+ (issue 28840)
- Misleading description of the 'workspace' permission.
- (issue 20148)
+ Fix jobs getting stuck in the Queue when there exists a cycle of upstream/downstream blocks between them.
+ (issue 28926)
- Run parameters should show display name if set, rather than build numbers.
- (issue 25174)
+ Always use earlier start time when merging two equivalent queue items.
+ (issue 2180)
+
- Build page was broken in Hungarian localization while building.
- (issue 26155)
+ Job loading can be broken by NullPointerException in a build trigger
+ (issue 27549)
+
- Spurious warnings in the log after deleting builds.
- (issue 25788)
+ NullPointerException computing load statistics under some conditions.
+ (issue 28384)
- Master labels disappear when system configuration is updated.
- (issue 23966)
+ Plugins using class loader masking did not work properly over the slave channel.
+ (issue 27289)
- Updated icon-set dependency to version 1.0.5.
- (issue 25499,
- issue 25498)
+ DefaultJnlpSlaveReceiver now returns true when rejecting a takeover.
+ (issue 27939)
+
+ Do not follow href after sending POST via l:task
+ (issue 28437)
- After recent Java security updates, Jenkins would not gracefully recover from a deleted secrets/master.key.
- (issue 25937)
+ Update bundled LDAP plugin in order to restore missing help files
+ (issue 28233)
- Restrict where this project can be run regressed in 1.589 when using the ClearCase plugin.
- (issue 25533)
+ hudson.model.Run.getLog() throws IndexOutOfBoundsException when called with maxLines=0
+ (issue 27441)
- Performance problems on large workspaces associated with validating file include patterns.
- (issue 25759)
+ Descriptor.getId fix in 1.610 introduced a regression affecting at least the Copy Artifacts plugin.
+ (issue 28011)
+
+ Search box did not work well inside folders.
+ (issue 24433)
+
- Always use forward slashes in path separators during in ZIP archives generated by Directory Browser
- (issue 22514)
+ Since 1.598 overrides of Descriptor.getId were not correctly handled by form binding, breaking at least the CloudBees Templates plugin.
+ (issue 26781)
+
+ Reverted in 1.611, reimplemented in 1.627. Archiving of large artifacts. Tar implementation cannot handle files having a size >8GB.
+ (issue 10629)
+
+ The queue state was not updated between scheduling builds.
+ (issue 27708,
+ issue 27871)
+ PeepholePermalink RunListenerImpl oncompleted should be triggered before downstream builds are triggered.
+ (issue 20989)
+
+ NPE when /script used on offline slave.
+ (issue 26751)
+
+ Make periodic workspace cleanup configurable through system properties.
+ (issue 21322)
+
+ Do not offer to restart on /restart and /safeRestart if the configuration does not support it.
+ (issue 27414)
+
+ Polling was skipped while quieting down, resulting in ignored commit notifications. This behavior was changed.
+ (issue 26208)
+
+ Starting this version, native packages are produced from the new repository.
+ File issues related to installers and packages in the packaging component.
+
+ JSONP served with the wrong MIME type and rejected by Chrome.
+ (issue 27607)
+
+ Security file pattern whitelist was broken for some plugins since 1.597.
+ (issue 27055)
+
+ Lock an Executor without creating a Thread
+ (issue 25938)
+
+ Hide flyweight master executor when ≥1 heavyweight executors running as subtasks
+ (issue 26900)
+
+ Way to mark an Executable that should not block isReadyToRestart
+ (issue 22941)
+
+ Refactor the Queue and Nodes to use a consistent locking strategy
+ (issue 27565)
+ Note that this change involved moving slave definitions outside the main config.xml file.
+ If you downgrade after this, your slave settings will be lost.
+
+ Makes the Jenkins is loading screen not block on the extensions loading lock
+ (issue 27563)
+
+ AdjunctManager: exception upon startup
+ (issue 15355)
+
+ Removes race condition rendering the list of executors
+ (issue 27564)
+
+ Tidy up the locks that were causing deadlocks with the once retention strategy in durable tasks
+ (issue 27476)
+
+ Remove any requirement from Jenkins Core to lock on the Queue when rendering the Jenkins UI
+ (issue 27566)
+
+ Added a switch (-Dhudson.model.User.allowNonExistentUserToLogin=true) to let users login even when the record is not found in the backend security realm.
+ (issue 22346)
+
+ Avoid deadlock when using build-monitor-plugin.
+ (issue 27183)
+
+ As security hardening, mark "remember me" cookie as HTTP only
+ (issue 27277)
+
+ Show displayName in build remote API.
+ (issue 26723)
+
+ JENKINS_HOME layout change: builds are now keyed by build numbers and not timestamps.
+ See Wiki for details
+ and downgrade.
+ (issue 24380)
+
+ Do not throw exception on /signup when not possible.
+ (issue 11172)
+
+ Tool installer which downloads and unpacks archives should not fail the build if the tool already exists and the server returns an error code.
+ (issue 26196)
+
- 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)
-
- 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)
-
- JNLP slaves are now handled through NIO-based remoting channels for better scalability.
-
- Fixed ArrayIndexOutOfBoundsException in XStream with Oracle JDK8 release version
- (issue 18537)
-
- Corrected permission checks for copy-job and create-job CLI commands.
- (issue 22262)
-
- identity.key, used to secure some communications with Jenkins, now stored encrypted with the master key.
-
- When dynamically loading a plugin which another loaded plugin already had an optional dependency on, class loading errors could result before restart.
- (issue 19976)
-
- 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)
-
- 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)
-
- Random class loading error mostly known to affect static analysis plugins.
- (issue 12124)
-
- 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)
-
- 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.
-
- Do not show Maven modules and matrix configurations in the Copy Job dialog.
- (issue 19559)
-
- Fixed two file descriptor leaks.
- (issue 14336)
-
- RuntimeException if you try to save a config with a choice parameter that has no choices.
- (issue 18434)
-
- 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.
-
- 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)
-
- Test harness was packing copies of Maven into plugin archives under some conditions.
- (issue 18918)
-
- Provided maven settings.xml in maven builder is lost.
- (issue 15976)
-
- Exception when running polling with a Maven installation not defined on master.
- (issue 18898)
-
- 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).
-
- Fixed a regression that broke some plugins' form validation
- (issue 18776)
-
- People View does Not Populate if JQuery plugin enabled.
- (issue 18641)
+
+ Misleading error message trying to dynamically update a plugin (which is impossible) on an NFS filesystem.
+ (issue 12753)
+
+ Updated component used by the swap space monitor to better support Debian and AIX.
+
+ SSH slave connections die after the slave outputs 4MB of stderr, usually during findbugs analysis
+ (issue 22938)
+
- Fixed: claiming of tests doesn't work in Maven jobs (claim-plugin)
- (issue 14585)
+ 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)
- If every node is restricted to tied jobs only, Matrix build jobs can never start.
+ 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)
- 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 the master/slave handshake problem when a slave runs on non-ASCII compatible encoding (such as EBCDIC.)
+ Fixed a corner case handling of tool installation.
+ (issue 16846)
- Added a diagnosis for StreamCorruptedException problem
- (issue 8856)
+ 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)
- Matrix project's parent can be now tied to labels/slaves.
- (issue 7825)
+ Close drop-down button menu when clicking outside
+ (issue 17050)
- Clean up fingerprint records that correspond to the deleted build recods
- (issue 18417)
+ RunParameter with filtering enabled incorrectly includes builds which have not yet completed
+ (issue 20974)
- Fixed "Comparison method violates its general contract" error in BuildTrigger.execute
- (issue 17247)
+ Fixed NPE if RunParameterValue points to a stable build.
+ (issue 20857)
- Edited description wasn't reflected when pressing the "Apply" button.
- (issue 18436)
+ Fixed a JavaScript problem in sortable table with IE8.
+ (issue 21729)
- Fixed a regression in remoting since 1.519 that caused FindBugs plugins to break.
- (issue 18349,
- issue 18405)
+ More efficient deletion of old builds (specified by date).
+ (issue 22607)
- Revisited the extension point added in 1.519 that adds custom plexus components.
-
- Slave launch thread should have the background activity credential.
- (issue 15578)
-
- “Build Now” link did not work for multijobs.
- (issue 16974)
-
- Unix vs. Windows mode not correctly retained for command launchers under some conditions.
- (issue 18368)
+ The matrix project type was moved into its own plugin.
- Edit views with non-ASCII names did not work since 1.500.
- (issue 18373)
-
- Fixed API incompatibility since 1.489.
- (issue 18356)
+ Linkage errors in notifiers could leak workspace leases.
+ (issue 21622)
- “Projects tied to slave” shows unrelated Maven module jobs.
- (issue 17451)
+ Better correction of the anomalous condition that several builds of a job specify the same number.
+ (issue 22631)
- Fixed file descriptor leak in fingerprint computation.
- (issue 18351)
+ Under certain conditions, a running build could mistakenly be shown as completed (and failed), while still producing output.
+ (issue 22681)
- 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)
+ Fix a bug which only showed the first detail part for radio buttons.
+ (issue 22583)
- Possible to create a custom AbstractDiskSpaceMonitor.
+ Update version of bundled Mailer plugin to 1.8 to avoid issues with older versions
- 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)
+ Show larger load statistics graphs.
+ (issue 22674)
- CLI list-jobs command should list all nested jobs.
- (pull request 793)
+ Linebreak project names less aggressively.
+ (issue 22670)
- 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)
-
- NPE in DefaultMatrixExecutionStrategyImpl.waitForCompletion.
- (issue 18024)
-
- Optimizations in fingerprint recording.
- (issue 16301)
-
- 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)
-
- Do not fail startup in case ListView.includeRegex was syntactically malformed.
-
- CSS stylesheets misrendered in Chrome due to caching.
- (issue 17684)
+ Added a new extension point for more pluggable JNLP slave handling
- User icon in People broken if Jenkins root URL unconfigured.
- (issue 18118)
+ Don't ask for confirmation when it doesn't make any sense.
+ (issue 21720)
- Progress bar sometimes broken in People.
- (issue 18119)
-
- Enable word breaking in potentially long strings like job names.
- (issue 17030)
-
- Allow filtering of the Run parameter build list by result.
- (issue 7280)
+ Jenkins asks for confirmation before leaving form even though user is not authorized to make changes.
+ (issue 20597)
- Add support for scalatest-maven-plugin.
- (issue 18086)
-
- When copying a folder, the display names of contained jobs were gratuitously cleared.
- (issue 18074)
-
- “Recurse in subfolders” option for list views produced exceptions when used with native Maven projects.
- (issue 18025)
-
- Using proper directory separator character for permalinks on Windows.
- (issue 17681)
-
- Use markup formater to display parameter description.
- (issue 18427)
-
+ Make the computers monitor status row look different from regular node rows.
+ (pull request 1095)
- Windows services now auto-restart in case of abnormal process termination.
+ Do not offer "Install without restart" for plugin updates.
+ (pull request 1125)
- <f:dropdownDescriptorSelector> does not allow defaulting to specifig instance
- (issue 17858)
+ Require POST on more actions.
+ (pull request 877)
- mark maven settings / global settings as default for new jobs
- (issue 17723)
-
- Symlink handling problem with build permalinks on Windows.
- (issue 17681)
-
- List views missing a required field were unloadable.
- (issue 15309)
+ Optimize image sizes.
+ (pull request 648)
- 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)
+ Properly close resources in case of exceptions.
+ (pull request 737)
- Extension point to transform test names (for use with alternative JVM languages).
- (issue 17478)
+ Fix warning on JBoss AS7 due to unnecessary xpp3_min dependency.
+ (pull request 733)
+
- 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.
-
- 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)
-
- NPE from MatrixConfiguration.newBuild.
- (issue 17728)
+
+ 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)
- Identify the short name of an uploaded plugin from the manifest, so it does not matter what the filename was.
- (issue 4543)
+ Fixed NPE from view new job name autocompletion since 1.553.
+ (issue 22142)
- Slave status monitor page shows when the data is last obtained
+ Deadlocks in concurrent builds under some conditions since 1.556.
+ (issue 22560)
- Delete button to highlight what it is going to delete.
-
- StringIndexOutOfBoundsException in PackageResult.findCorrespondingResult.
- (issue 17721)
+ JNLP slaves are now handled through NIO-based remoting channels for better scalability.
- Breadcrumb is reworked to show descendants to provide additional navigational shortcuts.
- (discussion)
-
- hpi:run did not work for bundled plugins.
- (issue 18352)
-
- Views can now include jobs located within folders.
- (pull 757)
-
- Added confirmation dialog before reloading configuration from disk.
- (issue 15340)
+ Fixed NoSuchMethodException when destroying processes using JDK1.8.
+ (issue 21341)
- Switched confirmation before deleting jobs or wiping out workspace to a dialog.
+ Avoid irrelevant job queing while node is offline.
+ (issue 21394)
- Different text than “Build Now” for parameterized jobs.
- (issue 10738)
+ Debian package now creates 'jenkins' group
- Check the view name with ajax.
-
- “Build Now” context menu item broken for parameterized jobs.
- (issue 17110)
-
- Incorrect redirection after delete of job in folder in view.
- (issue 17575)
-
- ”My Views" links leads to 404 Not Found.
- (issue 17317)
+ Suppress fingerprint link if fingerprint record isn't available.
+ (issue 21818)
- Quoting Issue with JDK Installer with Windows Installer.
- (issue 5408)
+ Job hangs if one of multiple triggered builds was aborted
+ (issue 21932)
- Restored compatibility in ArtifactArchiver signature; broken in 1.509 and could affect plugins.
- (issue 17637)
+ Don't submit form on Save after Apply in new window on some browsers.
+ (issue 20245)
- Fixed a bug in the logic that hides context menu anchor 'v'
- (issue 13995)
+ Remotely triggered builds now show correct IP address through proxy.
+ (issue 18008)
- UnsatisfiedLinkError on CreateSymbolicLinkw on Windows XP.
- (issue 17343)
-
- 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)
+ Slaves connected via Java Web Start now restart themselves when a connection to Jenkins is lost.
+ (issue 19055)
- Improved UI for implicitly locked builds.
- (issue 10197)
+ Fixed NPE from Slave.createLauncher.
+ (issue 21999)
- Incorrect URL computation broke context menu for computers with spaces in their names.
- (issue 18236)
-
- Promote the use of 'H' in cron.
- (issue 17311)
-
- Context menu no longer automatically pops up
- (issue 13995)
+ Faster rendering of views containing many items.
+ (issue 18364)
- Heavy thread congestion saving fingerprints.
- (issue 13154)
-
- Option to make the build not fail if there is nothing to archive.
- (issue 10502)
- Better report file deletion failures.
- (issue 17271)
+ Cron-style trigger configuration will now display expected prior and subsequent run times.
- "Local to the workspace" repository locator does not work when building one module in isolation.
- (issue 17331)
+ Incorrect filtering of build queue and executors widgets after 1.514.
+ (issue 20500)
- Master node mode not correctly displayed in /computer/(master)/configure.
- (issue 17263)
-
- Performance improvement in master/slave communication throughput
- (issue 7813)
+ NoSuchMethodError: hudson.model.BuildAuthorizationToken.checkPermission(…) from Build Token Root plugin since 1.556.
+ (issue 22382)
+
+ Allow a Trigger to be a DependencyDeclarer.
+ (issue 22397)
- Quoted label expression can result into dead executors (throwing exception)
- (issue 17128)
+ Fixed a slow down in resource loading caused by fix to JENKINS-18677.
+ (issue 21579)
- ChangeLog should produce some output even if some (plugin) annotator fails
- (issue 17084)
+ jenkins.war file shouldn't be exploded into /tmp
+ (issue 22442)
- View name should not allow "..".
- (issue 16608)
-
- Fixing a regression in 1.507 that causes a failure to load matrix jobs.
- (issue 17337)
+ JNLP slaves now satisfies stricter requirements imposed by JDK7u45.
+ (issue 20204)
+
+ Fixed NPE executing Pipe.EOF with ProxyWriter
+ (issue 20769)
- Show the reason for a skipped test if the test result contains one
- (issue 8713)
-
- an in-progress build was dropped from JSON API when lazy-loading was introduced.
- (issue 15583)
+ Fixed ArrayIndexOutOfBoundsException in XStream with Oracle JDK8 release version
+ (issue 18537)
+
+ Corrected permission checks for copy-job and create-job CLI commands.
+ (issue 22262)
- In-progress builds now survive the "reload from disk" administrator action.
- (issue 3265)
+ identity.key, used to secure some communications with Jenkins, now stored encrypted with the master key.
- If artifact archiving failed with an I/O error, the build nonetheless was considered to be a success.
- (issue 2058)
+ When dynamically loading a plugin which another loaded plugin already had an optional dependency on, class loading errors could result before restart.
+ (issue 19976)
- Fixed a bad interaction between Windows symlinks and build record lazy loading.
- (issue 15587)
+ Memory leaks in the old data monitor.
+ (issue 19544)
+
+ Ability for custom view types to disable automatic refresh.
+ (issue 21190)
+ (issue 21191)
- Remember the lastStable/Failed/Successful/etc builds to avoid eager loading builds.
- (issue 16089)
+ Option to download metadata directly from Jenkins rather than going through the browser.
+ (issue 19081)
- Wrong build result in post build steps after failed pre build step in maven projects.
- (issue 17177)
+ Allow JDK8 (and other versions) to be downloaded by JDKInstaller correctly.
+ (issue 22347)
- 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)
-
- Preview function for textareas using Jenkins markup did not work when CSRF protection was enabled.
- (issue 17085)
-
- Permalinks created in the wrong place when using external build directories.
- (issue 17137)
-
- External build directories not updated by job rename/delete.
- (issue 17138)
-
- JNA-related error from Windows slave monitoring thrown repeatedly.
- (issue 15796)
-
- New JSON library corrects problems such as form values starting with [.
- (issue 14827)
- Improved the request handling performance (where the file lookup is expensive, such as on NFS).
- (issue 16606)
+ Access through API token and SSH key login now fully retains group memberships.
+ (issue 20064)
- Windows symbolic support on Java5/6.
+ API changes allowing more flexibility in unusual job types.
+ (issue 22131)
- Improved the duration browsers cache static resources.
+ Job can be reloaded individually from disk with "job/FOO/reload" URL or "reload-job" CLI command
- Absolute URLs in console output
- (issue 16368)
-
- Revert ampersand encoding which can cause backward incompatibility issue
- (pull 683)
- Fix dependency graph computation when upstream build trigger is involved
- (issue 13502)
+ Jenkins should recover gracefully from a failure to process "remember me" cookie
+ (issue 11643)
- Disabled Authenticode verification for Windows services.
- (issue 15596)
+ Fixed Up link in matrix projects
+ (issue 21773)
- Not all log messages were being captured at /log/all.
- (issue 16952)
- Incorrect or missing XML encoding declaration on some REST API pages.
- (issue 16881)
-
- Fixed: Human readable file size method returns ",00" for files with byte length 0
- (issue 16630)
-
- “Build” from job context menu produced a confusing warning page.
- (issue 16844)
+ Archiving of symlinks as artifacts did not work in some cases.
+ (issue 21958)
- Maven2 builds with non-standard test plugins failed.
- (issue 16928)
-
- Started bundling XStream 1.4.4
- (issue 12542)
-
- Significant improvement in Traditional Chinese localizations.
- (pull 716)
+ Slow rendering of directories with many entries in remote workspaces.
+ (issue 21780)
- surefire-reports not detected for android-maven-plugin
- (issue 16776)
-
- maven-failsafe-plugin tests not recognized anymore
- (issue 16696)
-
- UI waiting on a queue lock to display cause of queue blockage.
- (issue 16833)
-
- UpdateCenter REST API chokes if there was a plugin installation failure.
- (issue 16836)
+ 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)
- Missing build title in /rssAll when build has no test result.
- (issue 16770)
+ Random class loading error mostly known to affect static analysis plugins.
+ (issue 12124)
- Changed the way matrix axis values are exposed as env variables
- (issue 11577)
+ After restarting Jenkins, users known only from changelogs could be shown as First Last _first.last@some.org_, breaking mail delivery.
+ (issue 16332)
- Maven 3 builds ignored quiet (-q) and debug (-X) options
- (issue 16843)
+ CLI build -s -v command caused 100% CPU usage on the master.
+ (issue 20965)
- JNLP slave installers can now work transparently with secured Jenkins.
- (SECURITY-54 / despite the ticket marker, this is not a security vulnerability)
+ Slave started from Java Web Start can now install itself as a systemd service.
- "Discard old build records" behavior is now pluggable, allowing plugins to define custom logic.
+ Split the “raw HTML” markup formatter out of core into a bundled plugin.
+
+ Do not show Maven modules and matrix configurations in the Copy Job dialog.
+ (issue 19559)
+
- 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)
- Plugin Manager’s Filter field did not work. Regression in 1.500.
- (issue 16651)
+ Fixed handling of default JENKINS_HOME when storing CLI credentials
+ (issue 21772)
- DISCOVER-able jobs break the build queue widget
- (issue 16682)
+ Fixed broken action links on Label page
+ (issue 21778)
- Extension point to provide access to workspace even when node is offline
- (issue 16454)
+ Allow Actions to contribute to Labels' main page
+ (issue 21777)
+
+ Expensive symlink-related calls on Windows can be simplified.
+ (issue 20534)
- Extension point to listen BuildStep execution
+ Improve detection of broken reverse proxy setups.
- Plugin icons in the sidebar were not being properly cached.
- (issue 16530)
+ Valentine's day security release that contains more than a dozen security fixes.
+ (security advisory)
- Broadly as well as deeply nested build causes overwhelmed the UI after 1.482.
- (issue 15747)
-
- API typo DependecyDeclarer corrected.
+ Regression in Windows slaves since 1.547.
+ (issue 21373)
- Avoid eagerly loading builds in Changes in dependency or culprit list.
- (pull 689)
+ Using java -jar jenkins-core.jar folder/external-monitor-job cmd … did not work.
+ (issue 21525)
- Run parameters do not support folders.
- (issue 16462)
+ Jenkins crash on startup after upgrade from 1.546 to 1.548.
+ (issue 21474)
+ The workspace cleanup thread failed to handle the modern workspace location on master, and mishandled folders.
+ (issue 21023)
- Since 1.494, when signing up as a new user in the private security realm the email address was left unconfigured and a stack trace printed.
+ Fixed missing help items on "Configure Global Security" page
+ (issue 19832)
- Enable transparent log decompression support.
- (issue 13655)
+ Sort groups on user index page alphabetically.
+ (issue 21673)
+
+ Should not be able to create a job named . (period).
+ (issue 21639)
- Display authorities at /user/* for convenience.
- (pull 577)
+ Plugins implementing "AsyncPeriodicWork" can overwrite default logging level
+ (pull request #1115)
- Slow rendering of view pages in large installations due to eager check whether the “People” link would show anything.
- (issue 16244)
+ Wrong log message for out-of-order build record repair.
+ (issue 20730)
- Reduced size of memory leak in render-on-demand functionality used e.g. in configuration pages.
- (issue 16341)
+ Existing Fingerprint Action is reused and not added a second time.
+ (issue 19832)
- Improving responsiveness of People page.
- (issue 16342)
- (issue 16397)
+ TestObject doesn't replace '%' character
+ (issue 21707)
- Exception printed to log while adding Build other projects post-build step.
- (issue 16444)
+ "java -jar jenkins.war" should use unique session cookie for users who run multiple Jenkins on the same host.
+
- JNLP slave index page failed to explain how to pass -jnlpCredentials.
- (issue 16273)
+ Removing the "keep this build forever" lock on a build should require the DELETE permission.
+ (issue 16417)
- Links should preserve used protocol
- (issue 16368)
+ Files added to zip archive are closed properly.
+ (issue 20345)
- 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)
+ Broken CSS when reloading Jenkins after a time of inactivity
+ (issue 17526)
+
+ Add Batch Command tool installer for Windows nodes.
+ (issue 21202)
+
+ Allow more specific loggers to reduce log level.
+ (issue 21386)
+ Builds disappear after renaming a job.
+ (issue 18678)
- Delete the oldest build but it still come up on HistoryWidget
- (issue 16194)
+ 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)
+
+ When clicking Apply results in an exception (error page), show it, rather than creating an empty dialog.
+ (issue 20772)
-Older changelogs can be found in a separate file
+Older changelogs can be found in a separate file
diff --git a/cli/pom.xml b/cli/pom.xml
index 67da3a05cde3c7a7c0743f1281b4ade6acd6efba..bdc0fa749c34544e07a6068910088d0346028da3 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -5,7 +5,7 @@
org.jenkins-ci.mainpom
- 1.599-SNAPSHOT
+ 1.648-SNAPSHOTcli
@@ -42,7 +42,7 @@
org.jvnet.localizerlocalizer
- 1.10
+ 1.23org.jenkins-ci
diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java
index 6a3a05c28773b3bd56e4f93341b9c29df589df0f..de2d2bd7950a3a7a73993dc6581213a3f4ca01ca 100644
--- a/cli/src/main/java/hudson/cli/CLI.java
+++ b/cli/src/main/java/hudson/cli/CLI.java
@@ -97,6 +97,7 @@ public class CLI {
* @deprecated
* Use {@link CLIConnectionFactory} to create {@link CLI}
*/
+ @Deprecated
public CLI(URL jenkins, ExecutorService exec) throws IOException, InterruptedException {
this(jenkins,exec,null);
}
@@ -105,6 +106,7 @@ public class CLI {
* @deprecated
* Use {@link CLIConnectionFactory} to create {@link CLI}
*/
+ @Deprecated
public CLI(URL jenkins, ExecutorService exec, String httpsProxyTunnel) throws IOException, InterruptedException {
this(new CLIConnectionFactory().url(jenkins).executorService(exec).httpsProxyTunnel(httpsProxyTunnel));
}
@@ -169,12 +171,18 @@ public class CLI {
private Channel connectViaCliPort(URL jenkins, CliPort clip) throws IOException {
LOGGER.log(FINE, "Trying to connect directly via TCP/IP to {0}", clip.endpoint);
- final Socket s;
+ final Socket s = new Socket();
+ // this prevents a connection from silently terminated by the router in between or the other peer
+ // and that goes without unnoticed. However, the time out is often very long (for example 2 hours
+ // by default in Linux) that this alone is enough to prevent that.
+ s.setKeepAlive(true);
+ // we take care of buffering on our own
+ s.setTcpNoDelay(true);
OutputStream out;
if (httpsProxyTunnel!=null) {
String[] tokens = httpsProxyTunnel.split(":");
- s = new Socket(tokens[0], Integer.parseInt(tokens[1]));
+ s.connect(new InetSocketAddress(tokens[0], Integer.parseInt(tokens[1])));
PrintStream o = new PrintStream(s.getOutputStream());
o.print("CONNECT " + clip.endpoint.getHostName() + ":" + clip.endpoint.getPort() + " HTTP/1.0\r\n\r\n");
@@ -199,7 +207,6 @@ public class CLI {
}
};
} else {
- s = new Socket();
s.connect(clip.endpoint,3000);
out = SocketChannelStream.out(s);
}
@@ -379,7 +386,13 @@ public class CLI {
// h.setLevel(ALL);
// l.addHandler(h);
//
- System.exit(_main(_args));
+ try {
+ System.exit(_main(_args));
+ } catch (Throwable t) {
+ // if the CLI main thread die, make sure to kill the JVM.
+ t.printStackTrace();
+ System.exit(-1);
+ }
}
public static int _main(String[] _args) throws Exception {
diff --git a/cli/src/main/java/hudson/cli/CliPort.java b/cli/src/main/java/hudson/cli/CliPort.java
index 1507704d9c0e79dc40c88aa25d8bbe369c159e2a..8faab7de41bee0c88fa90cdaa0cbb8c9599f6a97 100644
--- a/cli/src/main/java/hudson/cli/CliPort.java
+++ b/cli/src/main/java/hudson/cli/CliPort.java
@@ -11,7 +11,7 @@ import java.security.spec.X509EncodedKeySpec;
/**
* @author Kohsuke Kawaguchi
*/
-final class CliPort {
+public final class CliPort {
/**
* The TCP endpoint to talk to.
*/
@@ -27,7 +27,7 @@ final class CliPort {
*/
final String identity;
- CliPort(InetSocketAddress endpoint, String identity, int version) {
+ public CliPort(InetSocketAddress endpoint, String identity, int version) {
this.endpoint = endpoint;
this.identity = identity;
this.version = version;
diff --git a/cli/src/main/java/hudson/cli/Connection.java b/cli/src/main/java/hudson/cli/Connection.java
index 165a6deb7e6af83adf0ccda8cbe86a474414a65e..1c1ada471fdbaaded6f2203ec130a7e69a671a6f 100644
--- a/cli/src/main/java/hudson/cli/Connection.java
+++ b/cli/src/main/java/hudson/cli/Connection.java
@@ -23,6 +23,8 @@
*/
package hudson.cli;
+import hudson.remoting.ClassFilter;
+import hudson.remoting.ObjectInputStreamEx;
import hudson.remoting.SocketChannelStream;
import org.apache.commons.codec.binary.Base64;
@@ -107,7 +109,8 @@ public class Connection {
* Receives an object sent by {@link #writeObject(Object)}
*/
public T readObject() throws IOException, ClassNotFoundException {
- ObjectInputStream ois = new ObjectInputStream(in);
+ ObjectInputStream ois = new ObjectInputStreamEx(in,
+ getClass().getClassLoader(), ClassFilter.DEFAULT);
return (T)ois.readObject();
}
diff --git a/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java b/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java
index 941dc4b0ae532f71ddd00c61e09b422a138d689a..abde9bb2022664eda42f5b28d91e9d58fbcfe04f 100644
--- a/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java
+++ b/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java
@@ -44,7 +44,7 @@ public class FullDuplexHttpStream {
private static String basicAuth(String userInfo) {
if (userInfo != null)
- return "Basic "+new String(new Base64().encodeBase64(userInfo.getBytes()));
+ return "Basic "+new String(Base64.encodeBase64(userInfo.getBytes()));
return null;
}
diff --git a/cli/src/main/resources/hudson/cli/client/Messages_bg.properties b/cli/src/main/resources/hudson/cli/client/Messages_bg.properties
new file mode 100644
index 0000000000000000000000000000000000000000..df0d2b57e1ac7b168809824f735bec1b102b27c4
--- /dev/null
+++ b/cli/src/main/resources/hudson/cli/client/Messages_bg.properties
@@ -0,0 +1,44 @@
+# The MIT License
+#
+# Bulgarian translation copyright 2015 Alexander Shopov .
+#
+# 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.
+
+CLI.Usage=\
+ \u041f\u0440\u043e\u0433\u0440\u0430\u043c\u0430 \u043d\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0438\u044f \u0440\u0435\u0434 \u043d\u0430 Jenkins\n\
+ \u0423\u043f\u043e\u0442\u0440\u0435\u0431\u0430: java -jar jenkins-cli.jar [-s \u0410\u0414\u0420\u0415\u0421] \u041a\u041e\u041c\u0410\u041d\u0414\u0410 [\u041e\u041f\u0426\u0418\u042f\u2026] \u0410\u0420\u0413\u0423\u041c\u0415\u041d\u0422\u2026\n\
+ \u041e\u043f\u0446\u0438\u0438:\n\
+ -s \u0410\u0414\u0420\u0415\u0421 : \u0430\u0434\u0440\u0435\u0441\u044a\u0442 \u043d\u0430 \u0441\u044a\u0440\u0432\u044a\u0440\u0430 (\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e \u0441\u0435 \u0432\u0437\u0438\u043c\u0430 \u043e\u0442 \u043f\u0440\u043e\u043c\u0435\u043d\u043b\u0438\u0432\u0430\u0442\u0430 \u043d\u0430\n\
+ \u0441\u0440\u0435\u0434\u0430\u0442\u0430 \u201eJENKINS_URL\u201c)\n\
+ -i \u041a\u041b\u042e\u0427 : \u0447\u0430\u0441\u0442\u0435\u043d \u043a\u043b\u044e\u0447 \u0437\u0430 SSH \u0437\u0430 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f\n\
+ -p \u0425\u041e\u0421\u0422:\u041f\u041e\u0420\u0422 : \u0445\u043e\u0441\u0442 \u0438 \u043f\u043e\u0440\u0442 \u0437\u0430 \u0441\u044a\u0440\u0432\u044a\u0440-\u043f\u043e\u0441\u0440\u0435\u0434\u043d\u0438\u043a \u043f\u043e HTTP \u0437\u0430 \u0442\u0443\u043d\u0435\u043b \u043f\u043e HTTPS.\n\
+ \u0417\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f: http://jenkins-ci.org/https-proxy-tunnel\n\
+ -noCertificateCheck : \u0431\u0435\u0437 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u0430 \u0437\u0430 HTTPS (\u0412\u041d\u0418\u041c\u0410\u041d\u0418\u0415!)\n\
+ -noKeyAuth : \u0431\u0435\u0437 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0441 \u0447\u0430\u0441\u0442\u0435\u043d \u043a\u043b\u044e\u0447 \u0437\u0430 SSH, \u043e\u043f\u0446\u0438\u044f\u0442\u0430 \u0435\n\
+ \u043d\u0435\u0441\u044a\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u0430 \u0441 \u043e\u043f\u0446\u0438\u044f\u0442\u0430 \u201e-i\u201c\n\n\
+ \u041d\u0430\u043b\u0438\u0447\u043d\u0438\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0438 \u0437\u0430\u0432\u0438\u0441\u044f\u0442 \u043e\u0442 \u0432\u0435\u0440\u0441\u0438\u044f\u0442\u0430 \u043d\u0430 \u0441\u044a\u0440\u0432\u044a\u0440\u0430. \u0417\u0430 \u0438\u0437\u0432\u0435\u0436\u0434\u0430\u043d\u0435 \u043d\u0430 \u0441\u043f\u0438\u0441\u044a\u043a\u0430\n\
+ \u0438\u043c \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430\u0439\u0442\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u0430\u0442\u0430 \u201ehelp\u201c.\n
+CLI.NoURL=\
+ \u0410\u0434\u0440\u0435\u0441\u044a\u0442 \u043d\u0435 \u0435 \u0437\u0430\u0434\u0430\u0434\u0435\u043d \u043d\u0438\u0442\u043e \u0447\u0440\u0435\u0437 \u043e\u043f\u0446\u0438\u044f\u0442\u0430 \u201e-s\u201c, \u043d\u0438\u0442\u043e \u0447\u0440\u0435\u0437 \u043f\u0440\u043e\u043c\u0435\u043d\u043b\u0438\u0432\u0430\u0442\u0430 \u043d\u0430\
+ \u0441\u0440\u0435\u0434\u0430\u0442\u0430 \u201eJENKINS_URL\u201c.
+CLI.VersionMismatch=\
+ \u0412\u0435\u0440\u0441\u0438\u0438\u0442\u0435 \u043d\u0435 \u0441\u044a\u0432\u043f\u0430\u0434\u0430\u0442. \u0422\u0430\u0437\u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430 \u0437\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0435\u043d \u0440\u0435\u0434 \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0438 \u0441 \u0442\u043e\u0437\u0438 \u0441\u044a\u0440\u0432\u044a\u0440\
+ Jenkins.
+CLI.NoSuchFileExists=\
+ \u0422\u0430\u043a\u044a\u0432 \u0444\u0430\u0439\u043b \u043d\u0435 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430: {0}
diff --git a/cli/src/test/java/hudson/cli/ConnectionTest.java b/cli/src/test/java/hudson/cli/ConnectionTest.java
index 8cfac14fb4bc07eac67bca5370967bc47de94c84..f4cb0e8b1d595516130553bbe6568f8255324327 100644
--- a/cli/src/test/java/hudson/cli/ConnectionTest.java
+++ b/cli/src/test/java/hudson/cli/ConnectionTest.java
@@ -1,9 +1,11 @@
package hudson.cli;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
import hudson.remoting.FastPipedInputStream;
import hudson.remoting.FastPipedOutputStream;
+import org.codehaus.groovy.runtime.Security218;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -71,4 +73,15 @@ public class ConnectionTest {
throw new Error("thread is still alive");
}
}
+
+ @Test
+ public void testSecurity218() throws Exception {
+ c1.writeObject(new Security218());
+ try {
+ c2.readObject();
+ fail();
+ } catch (SecurityException e) {
+ assertTrue(e.getMessage().contains(Security218.class.getName()));
+ }
+ }
}
diff --git a/cli/src/test/java/org/codehaus/groovy/runtime/Security218.java b/cli/src/test/java/org/codehaus/groovy/runtime/Security218.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc3dfeef041c6ac07b91f80d3dc98a5f1530e4c7
--- /dev/null
+++ b/cli/src/test/java/org/codehaus/groovy/runtime/Security218.java
@@ -0,0 +1,11 @@
+package org.codehaus.groovy.runtime;
+
+import java.io.Serializable;
+
+/**
+ * Test payload in a prohibited package name.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public class Security218 implements Serializable {
+}
diff --git a/core/pom.xml b/core/pom.xml
index 66905e2196d3f0d8c7439dc501688093f8f3ad13..59be9513829c152b023923cc8aeb677e734a88da 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -29,7 +29,7 @@ THE SOFTWARE.
org.jenkins-ci.mainpom
- 1.599-SNAPSHOT
+ 1.648-SNAPSHOTjenkins-core
@@ -39,7 +39,7 @@ THE SOFTWARE.
true
- 1.234
+ 1.2372.5.6.SEC031.8.9
@@ -190,7 +190,7 @@ THE SOFTWARE.
args4jargs4j
- 2.0.23
+ 2.0.31org.jenkins-ci
@@ -200,7 +200,7 @@ THE SOFTWARE.
org.jenkins-cibytecode-compatibility-transformer
- 1.5
+ 1.8org.jenkins-ci
@@ -210,7 +210,7 @@ THE SOFTWARE.
org.jvnet.localizerlocalizer
- 1.10
+ 1.23antlr
@@ -276,6 +276,11 @@ THE SOFTWARE.
commons-beanutils1.8.3
+
+ org.apache.commons
+ commons-compress
+ 1.10
+ javax.mailmail
@@ -403,10 +408,10 @@ THE SOFTWARE.
groovy-all${groovy.version}
-
+ jlinejline
- 0.9.94
+ 1.0compile
@@ -464,8 +469,17 @@ THE SOFTWARE.
1.1.0
- commons-logging
- commons-logging
+ org.slf4j
+ jcl-over-slf4j
+
+
+ org.slf4j
+ log4j-over-slf4j
+
+
+ org.slf4j
+ slf4j-jdk14
+ testcom.sun.xml.txw2
@@ -500,12 +514,12 @@ THE SOFTWARE.
net.java.dev.jnajna
- 4.1.0
+ 4.2.1org.kohsukeakuma
- 1.9
+ 1.10org.kohsuke
@@ -558,9 +572,9 @@ THE SOFTWARE.
- findbugs
+ com.google.code.findbugsannotations
- 1.0.0
+ 3.0.0provided
@@ -618,7 +632,7 @@ THE SOFTWARE.
-
+
org.jenkins-ci.toolsmaven-hpi-plugin
@@ -749,7 +763,7 @@ THE SOFTWARE.
0.5Ctrue
- -XX:MaxPermSize=128m
+ -XX:MaxPermSize=128m -noverify
@@ -865,7 +879,6 @@ THE SOFTWARE.
org.codehaus.mojofindbugs-maven-plugin
- 2.5.2MaxHigh
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message.properties
similarity index 92%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message.properties
index d303a63fc3e91e9b1a55c3b395f71c8d94c79e48..fffef47e59cd5c72bb4ac44cd9f2d6bc2ca5cee4 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message.properties
@@ -21,7 +21,8 @@
# THE SOFTWARE.
NewVersionAvailable=New version of Jenkins ({0}) is available for download \
- (changelog).
+ (changelog).
UpgradeComplete=Upgrade to Jenkins {0} is complete, awaiting restart.
UpgradeCompleteRestartNotSupported=Upgrade to Jenkins {0} is complete, awaiting restart.
-UpgradeProgress=Upgrade to Jenkins {0} is in progress or failed.
+UpgradeProgress=Upgrade to Jenkins {0} is in progress.
+UpgradeFailed=Upgrade to Jenkins {0} failed: {1}.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_cs.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_cs.properties
similarity index 93%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_cs.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_cs.properties
index 0627dbf1e4e6eb9c5e980357fc146aaa4cd3e8b6..691b46b7f011fea7b7006332205079333c537c84 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_cs.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_cs.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Nov\u00E1 verze Jenkins-u ({0}) je k dispozici ke sta\u017Een\u00ED (changelog).
+NewVersionAvailable=Nov\u00E1 verze Jenkins-u ({0}) je k dispozici ke sta\u017Een\u00ED (changelog).
Or\ Upgrade\ Automatically=Nebo Upgradovat Automaticky
UpgradeComplete=Upgrade na Jenkinse {0} je hotov, \u010Dek\u00E1 na restart.
UpgradeCompleteRestartNotSupported=Upgrade na Jenkinse {0} je hotov, \u010Dek\u00E1 na restart.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_da.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_da.properties
similarity index 96%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_da.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_da.properties
index 37285e900f14f4efd15dd43e9fb13957cdb3bdb5..8b5776e534c4c7b1976cc64e6cf05be51b585d9f 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_da.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_da.properties
@@ -25,4 +25,4 @@ UpgradeCompleteRestartNotSupported=Opdatering til Jenkins {0} er fuldf\u00f8rt,
UpgradeProgress=Opdatering til Jenkins {0} er i gang eller er fejlet.
Or\ Upgrade\ Automatically=Eller opdater automatisk
NewVersionAvailable=En ny version af Jenkins ({0}) er tilg\u00e6ngelig til hentning \
- (changelog).
+ (changelog).
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_de.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_de.properties
similarity index 93%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_de.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_de.properties
index 5eb9dc40793c4bff153407d10ba9b3ffa0cee571..344e4571183a7eaa3becaab2ef851cd3ac008099 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_de.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_de.properties
@@ -23,5 +23,5 @@
UpgradeComplete=Aktualisierung auf Jenkins {0} abgeschlossen. Neustart von Jenkins erforderlich.
UpgradeCompleteRestartNotSupported=Aktualisierung auf Jenkins {0} abgeschlossen. Neustart von Jenkins erforderlich.
UpgradeProgress=Aktualisierung auf Jenkins {0} in Arbeit oder fehlgeschlagen.
-NewVersionAvailable=Eine neue Version von Jenkins ({0}) kann hier heruntergeladen werden (Was ist neu?).
+NewVersionAvailable=Eine neue Version von Jenkins ({0}) kann hier heruntergeladen werden (Was ist neu?).
Or\ Upgrade\ Automatically=Automatisch aktualisieren
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_es.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_es.properties
similarity index 66%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_es.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_es.properties
index f3bd1ba6f4c69fac5de1ded01f4d7c998d39935a..6016d4d17c127ce0247010aad57455532ffcd8e4 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_es.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_es.properties
@@ -20,8 +20,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Hay una nueva versin de Jenkins disponible ({0}). descargar (listado de cambios).
-Or\ Upgrade\ Automatically=O actualizar automticamente
-UpgradeComplete=La actualizacin a Jenkins {0} se ha completado, esperando para reiniciar.
-UpgradeCompleteRestartNotSupported=La actualizacin a Jenkins {0} se ha completado, esperando para reiniciar.
-UpgradeProgress=La actualizacin a Jenkins {0} est en ejecucin o ha fallado.
+NewVersionAvailable=Hay una nueva versi\u00f3n de Jenkins disponible ({0}). descargar (listado de cambios).
+Or\ Upgrade\ Automatically=O actualizar autom\u00e1ticamente
+UpgradeComplete=La actualizaci\u00f3n a Jenkins {0} se ha completado, esperando para reiniciar.
+UpgradeCompleteRestartNotSupported=La actualizaci\u00f3n a Jenkins {0} se ha completado, esperando para reiniciar.
+UpgradeProgress=La actualizaci\u00f3n a Jenkins {0} est\u00e1 en ejecuci\u00f3n o ha fallado.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_et.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_et.properties
similarity index 52%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_et.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_et.properties
index 7cd8b01f2a7a4eaeeed6bc01da9c269b27517f8c..1df6054b1428d863d5b65a19a2da234fa6cf08b3 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_et.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_et.properties
@@ -1,3 +1,3 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Jenkinsi uus versioon ({0}) on saadaval allalaadimiseks (muudatuste nimekiri).
+NewVersionAvailable=Jenkinsi uus versioon ({0}) on saadaval allalaadimiseks (muudatuste nimekiri).
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fi.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fi.properties
similarity index 58%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fi.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fi.properties
index 4822e11895de090d17bbdec6b2ddb85d1a22e01c..8243a6f68555eec617915e558122db1d0033ae49 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fi.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fi.properties
@@ -1,3 +1,3 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Uusi Jenkinsin versio ({0}) on saatavana (muutosloki).
+NewVersionAvailable=Uusi Jenkinsin versio ({0}) on saatavana (muutosloki).
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fr.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fr.properties
similarity index 94%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fr.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fr.properties
index f00987657878298f54bcca2660ce3e1e701f066e..0dbdae69d7d8df9097c1df2db7ded0102902c8ae 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fr.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_fr.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Une nouvelle version de Jenkins ({0}) est disponible (changelog).
+NewVersionAvailable=Une nouvelle version de Jenkins ({0}) est disponible (changelog).
Or\ Upgrade\ Automatically=Ou mettre \u00E0 jour automatiquement
UpgradeProgress=Mise \u00E0 jour vers Jenkins {0} est en cours ou en \u00E9chec.
UpgradeComplete=La mise \u00E0 jour de Jenkins en {0} est termin\u00E9e, en attente de red\u00E9marrage.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_hu.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_hu.properties
similarity index 90%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_hu.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_hu.properties
index c28160d6590d5656ff448260dac92953c8e19d35..19587285cae841957f0c83b526dcf3cf6750c815 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_hu.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_hu.properties
@@ -20,5 +20,5 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=A Jenkins \u00FAj verzi\u00F3ja ({0}) el\u00E9rhet\u0151 let\u00F6lt\u00E9sre (v\u00E1ltoz\u00E1sok).
+NewVersionAvailable=A Jenkins \u00FAj verzi\u00F3ja ({0}) el\u00E9rhet\u0151 let\u00F6lt\u00E9sre (v\u00E1ltoz\u00E1sok).
Or\ Upgrade\ Automatically=Vagy friss\u00EDtsen automatikusan
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_id.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_id.properties
similarity index 64%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_id.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_id.properties
index aa157c3f69813a8b798ed70b83f1ceefc2d71a68..c28903b2ddf3ae2e9e532bb7006518699b72bf77 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_id.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_id.properties
@@ -1,4 +1,4 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Versi baru Jenkins ({0}) tersedia untuk diunduh (catatan perubahan).
+NewVersionAvailable=Versi baru Jenkins ({0}) tersedia untuk diunduh (catatan perubahan).
Or\ Upgrade\ Automatically=Atau Perbarui Secara Otomatis
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_it.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_it.properties
similarity index 93%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_it.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_it.properties
index f6538f878eacab3b00e9c5fc5a997ba9dfc5fb8f..7378e405c76f9e21a930a2e698b8baf8f024a484 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_it.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_it.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=C''\u00E8 una nuova versione di Jenkins ({0}) disponibile per il download (changelog).
+NewVersionAvailable=C''\u00E8 una nuova versione di Jenkins ({0}) disponibile per il download (changelog).
Or\ Upgrade\ Automatically=Oppure aggiorna automaticamente
UpgradeComplete=Aggiornamento a Jenkins {0} completato, in attesa di riavvio.
UpgradeCompleteRestartNotSupported=Aggiornamento a Jenkins {0} completato, in attesa di riavvio.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ja.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ja.properties
similarity index 93%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ja.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ja.properties
index 91ea7ae49a31a5ef876403f6d732286efb4764ce..5738b71b7682a2f7f0039aac4f593df8a9dce103 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ja.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ja.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Jenkins\u306E\u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3({0})\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3067\u304D\u307E\u3059 (\u5909\u66F4\u5C65\u6B74)\u3002
+NewVersionAvailable=Jenkins\u306E\u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3({0})\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3067\u304D\u307E\u3059 (\u5909\u66F4\u5C65\u6B74)\u3002
Or\ Upgrade\ Automatically=\u81EA\u52D5\u3067\u30A2\u30C3\u30D7\u30B0\u30EC\u30FC\u30C9
UpgradeComplete=Jenkins {0} \u3078\u306E\u30A2\u30C3\u30D7\u30B0\u30EC\u30FC\u30C9\u306F\u5B8C\u4E86\u3057\u307E\u3057\u305F\u3002\u518D\u8D77\u52D5\u5F85\u3061\u3067\u3059\u3002
UpgradeProgress=Jenkins {0}\u3078\u306E\u30A2\u30C3\u30D7\u30B0\u30EC\u30FC\u30C9\u306F\u5B9F\u884C\u4E2D\u304B\u3001\u5931\u6557\u3057\u307E\u3057\u305F\u3002
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ko.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ko.properties
similarity index 93%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ko.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ko.properties
index 3e54bb7c1204196e3c8ee28c652046b67ca14ef8..f5f6a772762aa34cd55aa6893fae4bc67c0aa073 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ko.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ko.properties
@@ -20,5 +20,5 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Jenkins \uC2E0\uADDC\uBC84\uC804({0})\uC744 \uC5EC\uAE30\uC11C \uBC1B\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.(\uBCC0\uACBD\uC0AC\uD56D).
+NewVersionAvailable=Jenkins \uC2E0\uADDC\uBC84\uC804({0})\uC744 \uC5EC\uAE30\uC11C \uBC1B\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.(\uBCC0\uACBD\uC0AC\uD56D).
Or\ Upgrade\ Automatically=\uB610\uB294 \uC790\uB3D9 \uC5C5\uADF8\uB808\uC774\uB4DC
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lt.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lt.properties
similarity index 62%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lt.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lt.properties
index b9995fe88bebe7f6236de20f5278c8d81b2155ed..2a16d979f6579860244892c408238e5e5ea6de66 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lt.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lt.properties
@@ -1,4 +1,4 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Galima parsisi\u0173sti nauj\u0105 ({0}) Jenkins versij\u0105. (pakeitimai).
+NewVersionAvailable=Galima parsisi\u0173sti nauj\u0105 ({0}) Jenkins versij\u0105. (pakeitimai).
Or\ Upgrade\ Automatically=Parsi\u0173sti automati\u0161kai
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lv.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lv.properties
similarity index 93%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lv.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lv.properties
index cf14b56b6fe54478da5e1bb04fa84d46c2dce5f2..9193a28385403d2933853f0dec5069a038406fa6 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lv.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_lv.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Jauna Jenkins ({0}) versija ir pieejama lejupl\u0101dei (izmai\u0146as).
+NewVersionAvailable=Jauna Jenkins ({0}) versija ir pieejama lejupl\u0101dei (izmai\u0146as).
Or\ Upgrade\ Automatically=Vai atjaunin\u0101t autom\u0101tiski
UpgradeComplete=Atjaunin\u0101s\u0101na uz Jenkins {0} ir pabeigta; gaidu p\u0101rstart\u0113\u0161anos.
UpgradeCompleteRestartNotSupported=Atjaunin\u0101s\u0101na uz Jenkins {0} ir pabeigta; gaidu p\u0101rstart\u0113\u0161anos.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nb_NO.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nb_NO.properties
similarity index 92%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nb_NO.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nb_NO.properties
index d86c6788e0fa80a045bf9ba845f7ef3f4eb3bd8a..b15161b4421813795e09b62fb4c26d00a4c7a858 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nb_NO.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nb_NO.properties
@@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=En ny versjon av Jenkins ({0}) er n\u00E5 tilgjengelig (Endringer).
+NewVersionAvailable=En ny versjon av Jenkins ({0}) er n\u00E5 tilgjengelig (Endringer).
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nl.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nl.properties
similarity index 93%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nl.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nl.properties
index d3a0df68e52fa4e22955eb330fb708d7451e20d1..9c291b0fddb44854da763828b29e37eaad0e1534 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nl.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_nl.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Er is een nieuwere versie van Jenkins ({0}) beschikbaar. downloaden (changelog).
+NewVersionAvailable=Er is een nieuwere versie van Jenkins ({0}) beschikbaar. downloaden (changelog).
Or\ Upgrade\ Automatically=Of werk automatisch bij
UpgradeComplete=Upgrade naar Jenkins {0} is volledig; aan het wachten op herstart.
UpgradeCompleteRestartNotSupported=Upgrade naar Jenkins {0} is volledig; aan het wachten op herstart.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pl.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pl.properties
similarity index 54%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pl.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pl.properties
index d22c58b0cc96b9f86675e87f7ea2d4889663bb21..6f5ff479b3092c96f38a93a2f6fb46a126fead29 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pl.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pl.properties
@@ -1,5 +1,6 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Nowa wersja Jenkinsa ({0}) jest dost\u0119pna do pobrania (changelog).
+NewVersionAvailable=Nowa wersja Jenkinsa ({0}) jest dost\u0119pna do pobrania (historia zmian).
Or\ Upgrade\ Automatically=Albo uaktualnij automatycznie
+UpgradeCompleteRestartNotSupported=Aktualizacja Jenkinsa do wersji {0} zako\u0144czy\u0142a si\u0119 pomy\u015Blnie, oczekiwanie na ponowne uruchomienie.
UpgradeProgress=Aktualizacja Jenkinsa do wersji {0} trwa lub nie powiod\u0142a si\u0119.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_BR.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_BR.properties
similarity index 96%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_BR.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_BR.properties
index 96e622fc3bed40133241ed18a2860e57d3d21a29..216eee44d9e947331dd3e7cf1737cb1c88f56f8f 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_BR.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_BR.properties
@@ -27,5 +27,5 @@ UpgradeCompleteRestartNotSupported=Atualiza\u00e7\u00e3o do Jenkins {0} completa
UpgradeProgress=Atualiza\u00e7\u00e3o para Jenkins {0} est\u00e1 em andamento ou falhou.
Or\ Upgrade\ Automatically=Ou fazer a atualiza\u00e7\u00e3o automaticamente.
# New version of Jenkins ({0}) is available for download \
-# (changelog).
+# (changelog).
NewVersionAvailable=Nova vers\u00e3o do Jenkins ({0}) est\u00e1 dispon\u00edvel em download
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_PT.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_PT.properties
similarity index 55%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_PT.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_PT.properties
index ad3dcdcaef7c7ca02225f7327bf19b7e9ec9dda3..dd2d81456d153cad42b32e5d27a0ceea7bea71c2 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_PT.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_pt_PT.properties
@@ -1,4 +1,4 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Uma nova vers\u00E3o do Jenkins ({0}) est\u00E1 dispon\u00EDvel para download (registo de altera\u00E7\u00F5es).
+NewVersionAvailable=Uma nova vers\u00E3o do Jenkins ({0}) est\u00E1 dispon\u00EDvel para download (registo de altera\u00E7\u00F5es).
Or\ Upgrade\ Automatically=Ou atualizar automaticamente
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ru.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ru.properties
similarity index 92%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ru.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ru.properties
index c3cd495635173c5de7b5114ded24a6597be99498..97659c57c7def17ad08d3af7af2b9eba4e7b8fee 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ru.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_ru.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=\u041D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F Jenkins ({0}) \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u0434\u043B\u044F \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 (\u0441\u043F\u0438\u0441\u043E\u043A \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439).
+NewVersionAvailable=\u041D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F Jenkins ({0}) \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u0434\u043B\u044F \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 (\u0441\u043F\u0438\u0441\u043E\u043A \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439).
Or\ Upgrade\ Automatically=\u0418\u043B\u0438 \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438
UpgradeComplete=\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0434\u043E Jenkins {0} \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E, \u043E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438.
UpgradeCompleteRestartNotSupported=\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0434\u043E Jenkins {0} \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043E, \u043E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sk.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sk.properties
similarity index 92%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sk.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sk.properties
index bca3d05fab39593ad4e321072d0956ee6988a40b..96543df586253a9821c3057b38d334cc733e9b25 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sk.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sk.properties
@@ -20,4 +20,4 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=Nov\u00E1 verzia Jenkins ({0}) je dostupn\u00E1 na stiahnutie (zoznam zmien).
+NewVersionAvailable=Nov\u00E1 verzia Jenkins ({0}) je dostupn\u00E1 na stiahnutie (zoznam zmien).
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sl.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sl.properties
similarity index 61%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sl.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sl.properties
index b69224ebd301faa0d6441f52535fc50fa973d86b..649104bcf5a3aa6702a824ed73c0c5195550f13c 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sl.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sl.properties
@@ -1,4 +1,4 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Nova razli\u010Dica Jenkins-a ({0}) je na voljo za prenos (seznam sprememb).
+NewVersionAvailable=Nova razli\u010Dica Jenkins-a ({0}) je na voljo za prenos (seznam sprememb).
Or\ Upgrade\ Automatically=ali posodobi samodejno
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sv_SE.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sv_SE.properties
similarity index 100%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sv_SE.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_sv_SE.properties
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_uk.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_uk.properties
similarity index 92%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_uk.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_uk.properties
index 3379d203678e3487f91fc8dc55a126b05c23e206..625195c3513bf5a71162d33e25b76066a09f3b84 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_uk.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_uk.properties
@@ -20,5 +20,5 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-NewVersionAvailable=\u041D\u043E\u0432\u0430 \u0432\u0435\u0440\u0441\u0456\u044F \u0414\u0436\u0435\u043D\u043A\u0456\u043D\u0441 ({0}) \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u0434\u043B\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F (\u0437\u043C\u0456\u043D\u0438).
+NewVersionAvailable=\u041D\u043E\u0432\u0430 \u0432\u0435\u0440\u0441\u0456\u044F \u0414\u0436\u0435\u043D\u043A\u0456\u043D\u0441 ({0}) \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u0434\u043B\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F (\u0437\u043C\u0456\u043D\u0438).
Or\ Upgrade\ Automatically=\u0410\u0431\u043E \u043E\u043D\u043E\u0432\u0456\u0442\u044C \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u043D\u043E
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_CN.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_CN.properties
similarity index 77%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_CN.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_CN.properties
index 1ddf921908269e57c7370c85b7252ea5dbde5b47..0ef79b530b974daaaed8ae9bc346ed86fb71a2bc 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_CN.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_CN.properties
@@ -1,6 +1,6 @@
# This file is under the MIT License by authors
-NewVersionAvailable=Jenkins\u65B0\u7248\u672C ({0})\u53EF\u70B9\u51FB download (\u53D8\u66F4\u8BF4\u660E)\u4E0B\u8F7D\u3002
+NewVersionAvailable=Jenkins\u65B0\u7248\u672C ({0})\u53EF\u70B9\u51FB download (\u53D8\u66F4\u8BF4\u660E)\u4E0B\u8F7D\u3002
Or\ Upgrade\ Automatically=\u6216 \u81EA\u52A8\u5347\u7EA7\u7248\u672C
UpgradeComplete=Jenkins {0} \u7248\u672C\u5347\u7EA7\u5DF2\u5B8C\u6210,\u7B49\u5F85\u91CD\u542F\u4E2D.
UpgradeCompleteRestartNotSupported=Jenkins {0} \u7248\u672C\u5347\u7EA7\u5DF2\u5B8C\u6210,\u7B49\u5F85\u91CD\u542F\u4E2D.
diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_TW.properties b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_TW.properties
similarity index 92%
rename from core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_TW.properties
rename to core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_TW.properties
index 0423c94c73d6319e64f59b6cebf2c0adb64629f0..76967e8ea16086a1772118b76e503a32fe985ef6 100644
--- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_TW.properties
+++ b/core/src/filter/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message_zh_TW.properties
@@ -24,5 +24,5 @@
UpgradeComplete=Jenkins {0} \u5347\u7248\u5b8c\u6210\uff0c\u7b49\u5019\u91cd\u65b0\u555f\u52d5\u3002
UpgradeCompleteRestartNotSupported=Jenkins {0} \u5347\u7248\u5b8c\u6210\uff0c\u7b49\u5019\u91cd\u65b0\u555f\u52d5\u3002
UpgradeProgress=Jenkins {0} \u5347\u7d1a\u4f5c\u696d\u6b63\u5728\u9032\u884c\u4e2d\u6216\u662f\u5df2\u7d93\u5931\u6557\u3002
-NewVersionAvailable=\u65b0\u7248\u7684 Jenkins ({0}) \u5df2\u7d93\u53ef\u4ee5\u4e0b\u8f09 (\u6539\u7248\u8a18\u9304)\u3002
+NewVersionAvailable=\u65b0\u7248\u7684 Jenkins ({0}) \u5df2\u7d93\u53ef\u4ee5\u4e0b\u8f09 (\u6539\u7248\u8a18\u9304)\u3002
Or\ Upgrade\ Automatically=\u6216\u662f\u81ea\u52d5\u5347\u7248
diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java
index b295de6405b934874fdaaf632d5b9509dac9c29f..b90e6fcfda9e99415c00cd17932ee69c16407be9 100644
--- a/core/src/main/java/hudson/ClassicPluginStrategy.java
+++ b/core/src/main/java/hudson/ClassicPluginStrategy.java
@@ -66,6 +66,7 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.Vector;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
@@ -303,13 +304,19 @@ public class ClassicPluginStrategy implements PluginStrategy {
// don't fix the dependency for yourself, or else we'll have a cycle
String yourName = atts.getValue("Short-Name");
if (shortName.equals(yourName)) return;
+ if (BREAK_CYCLES.contains(yourName + '/' + shortName)) {
+ LOGGER.log(Level.FINE, "skipping implicit dependency {0} → {1}", new Object[] {yourName, shortName});
+ return;
+ }
// some earlier versions of maven-hpi-plugin apparently puts "null" as a literal in Hudson-Version. watch out for them.
String jenkinsVersion = atts.getValue("Jenkins-Version");
if (jenkinsVersion==null)
jenkinsVersion = atts.getValue("Hudson-Version");
- if (jenkinsVersion == null || jenkinsVersion.equals("null") || new VersionNumber(jenkinsVersion).compareTo(splitWhen) <= 0)
- optionalDependencies.add(new PluginWrapper.Dependency(shortName+':'+requireVersion));
+ if (jenkinsVersion == null || jenkinsVersion.equals("null") || new VersionNumber(jenkinsVersion).compareTo(splitWhen) <= 0) {
+ optionalDependencies.add(new PluginWrapper.Dependency(shortName + ':' + requireVersion));
+ LOGGER.log(Level.FINE, "adding implicit dependency {0} → {1} because of {2}", new Object[] {yourName, shortName, jenkinsVersion});
+ }
}
}
@@ -330,6 +337,16 @@ public class ClassicPluginStrategy implements PluginStrategy {
new DetachedPlugin("junit","1.577.*","1.0")
);
+ /** Implicit dependencies that are known to be unnecessary and which must be cut out to prevent a dependency cycle among bundled plugins. */
+ private static final Set BREAK_CYCLES = new HashSet(Arrays.asList(
+ "script-security/matrix-auth",
+ "script-security/windows-slaves",
+ "script-security/antisamy-markup-formatter",
+ "script-security/matrix-project",
+ "credentials/matrix-auth",
+ "credentials/windows-slaves"
+ ));
+
/**
* Computes the classloader that takes the class masking into account.
*
@@ -780,7 +797,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
@Override
protected Class defineClassFromData(File container, byte[] classData, String classname) throws IOException {
if (!DISABLE_TRANSFORMER)
- classData = pluginManager.getCompatibilityTransformer().transform(classname, classData);
+ classData = pluginManager.getCompatibilityTransformer().transform(classname, classData, this);
return super.defineClassFromData(container, classData, classname);
}
}
diff --git a/core/src/main/java/hudson/DNSMultiCast.java b/core/src/main/java/hudson/DNSMultiCast.java
index 8d029b6ae3f4ba42ca5aa253cdea8ea9ee294b80..b1f7d0b5e0c0b9844659a2af23d7a6f6bc1f8fb6 100644
--- a/core/src/main/java/hudson/DNSMultiCast.java
+++ b/core/src/main/java/hudson/DNSMultiCast.java
@@ -66,7 +66,8 @@ public class DNSMultiCast implements Closeable {
jmdns.registerService(ServiceInfo.create("_http._tcp.local.","Jenkins",
jenkins_port,0,0,props));
} catch (IOException e) {
- LOGGER.log(Level.WARNING,"Failed to advertise the service to DNS multi-cast",e);
+ LOGGER.log(Level.INFO, "Cannot advertise service to DNS multi-cast, skipping: {0}", e);
+ LOGGER.log(Level.FINE, null, e);
}
return null;
}
diff --git a/core/src/main/java/hudson/DependencyRunner.java b/core/src/main/java/hudson/DependencyRunner.java
index bd4282f8c209bc8012d02ceb2ff16b913725c41b..acf035676910ad3ac77f6e4b3164ed7358fc0f39 100644
--- a/core/src/main/java/hudson/DependencyRunner.java
+++ b/core/src/main/java/hudson/DependencyRunner.java
@@ -35,7 +35,6 @@ import java.util.Set;
import java.util.Collection;
import java.util.logging.Logger;
-import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
diff --git a/core/src/main/java/hudson/DescriptorExtensionList.java b/core/src/main/java/hudson/DescriptorExtensionList.java
index a3917b2e84b02cd1f9c997f966b9d485977040bc..513c557d44c7c11bcbd0c62c891d83f03997adeb 100644
--- a/core/src/main/java/hudson/DescriptorExtensionList.java
+++ b/core/src/main/java/hudson/DescriptorExtensionList.java
@@ -81,6 +81,7 @@ public class DescriptorExtensionList, D extends Descrip
* @deprecated as of 1.416
* Use {@link #create(Jenkins, Class)}
*/
+ @Deprecated
public static ,D extends Descriptor>
DescriptorExtensionList createDescriptorList(Hudson hudson, Class describableType) {
return (DescriptorExtensionList)createDescriptorList((Jenkins)hudson,describableType);
@@ -95,6 +96,7 @@ public class DescriptorExtensionList, D extends Descrip
* @deprecated as of 1.416
* Use {@link #DescriptorExtensionList(Jenkins, Class)}
*/
+ @Deprecated
protected DescriptorExtensionList(Hudson hudson, Class describableType) {
this((Jenkins)hudson,describableType);
}
@@ -109,6 +111,7 @@ public class DescriptorExtensionList, D extends Descrip
*
* @param fqcn
* Fully qualified name of the descriptor, not the describable.
+ * @deprecated {@link Descriptor#getId} is supposed to be used for new code, not the descriptor class name.
*/
public D find(String fqcn) {
return Descriptor.find(this,fqcn);
diff --git a/core/src/main/java/hudson/ExtensionFinder.java b/core/src/main/java/hudson/ExtensionFinder.java
index d4dc1ef31bc7ebe0b20ea152a53275044e94f25f..1a61b4d64a6dd01cc759d30f891bac4d705b6a60 100644
--- a/core/src/main/java/hudson/ExtensionFinder.java
+++ b/core/src/main/java/hudson/ExtensionFinder.java
@@ -85,6 +85,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
* Use and implement {@link #find(Class,Hudson)} that allows us to put some metadata.
*/
@Restricted(NoExternalUse.class)
+ @Deprecated
public Collection findExtensions(Class type, Hudson hudson) {
return Collections.emptyList();
}
@@ -244,7 +245,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
*/
private List> sezpozIndex;
- private final Map annotations = new HashMap();
+ private final Map annotations = new HashMap<>();
private final Sezpoz moduleFinder = new Sezpoz();
/**
@@ -260,7 +261,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
sezpozIndex = loadSezpozIndices(Jenkins.getInstance().getPluginManager().uberClassLoader);
- List modules = new ArrayList();
+ List modules = new ArrayList<>();
modules.add(new AbstractModule() {
@Override
protected void configure() {
@@ -324,7 +325,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
l.addAll(delta);
sezpozIndex = l;
- List modules = new ArrayList();
+ List modules = new ArrayList<>();
modules.add(new SezpozModule(delta));
for (ExtensionComponent ec : moduleFinder.refresh().find(Module.class)) {
modules.add(ec.getInstance());
@@ -337,7 +338,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
return new ExtensionComponentSet() {
@Override
public Collection> find(Class type) {
- List> result = new ArrayList>();
+ List> result = new ArrayList<>();
_find(type, result, child);
return result;
}
@@ -351,14 +352,11 @@ public abstract class ExtensionFinder implements ExtensionPoint {
private Object instantiate(IndexItem,Object> item) {
try {
return item.instance();
- } catch (LinkageError e) {
+ } catch (LinkageError | Exception e) {
// sometimes the instantiation fails in an indirect classloading failure,
// which results in a LinkageError
LOGGER.log(isOptional(item.annotation()) ? Level.FINE : Level.WARNING,
"Failed to load "+item.className(), e);
- } catch (Exception e) {
- LOGGER.log(isOptional(item.annotation()) ? Level.FINE : Level.WARNING,
- "Failed to load "+item.className(), e);
}
return null;
}
@@ -375,7 +373,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
public Collection> find(Class type, Hudson jenkins) {
// the find method contract requires us to traverse all known components
- List> result = new ArrayList>();
+ List> result = new ArrayList<>();
for (Injector i=container; i!=null; i=i.getParent()) {
_find(type, result, i);
}
@@ -389,7 +387,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
Object o = e.getValue().getProvider().get();
if (o!=null) {
GuiceExtensionAnnotation gea = a!=null ? extensionAnnotations.get(a.annotationType()) : null;
- result.add(new ExtensionComponent(type.cast(o),gea!=null?gea.getOrdinal(a):0));
+ result.add(new ExtensionComponent<>(type.cast(o), gea != null ? gea.getOrdinal(a) : 0));
}
}
}
@@ -443,7 +441,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
}
};
}
- };
+ }
private static final Logger LOGGER = Logger.getLogger(GuiceFinder.class.getName());
@@ -532,14 +530,11 @@ public abstract class ExtensionFinder implements ExtensionPoint {
}
}).in(scope);
}
- } catch (LinkageError e) {
+ } catch (Exception|LinkageError e) {
// sometimes the instantiation fails in an indirect classloading failure,
// which results in a LinkageError
LOGGER.log(optional ? Level.FINE : Level.WARNING,
"Failed to load "+item.className(), e);
- } catch (Exception e) {
- LOGGER.log(optional ? Level.FINE : Level.WARNING,
- "Failed to load "+item.className(), e);
}
}
}
@@ -621,7 +616,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
* Finds all the matching {@link IndexItem}s that match the given type and instantiate them.
*/
private Collection> _find(Class type, List> indices) {
- List> result = new ArrayList>();
+ List> result = new ArrayList<>();
for (IndexItem item : indices) {
try {
@@ -641,14 +636,12 @@ public abstract class ExtensionFinder implements ExtensionPoint {
if(type.isAssignableFrom(extType)) {
Object instance = item.instance();
if(instance!=null)
- result.add(new ExtensionComponent(type.cast(instance),item.annotation()));
+ result.add(new ExtensionComponent<>(type.cast(instance),item.annotation()));
}
- } catch (LinkageError e) {
+ } catch (LinkageError|Exception e) {
// sometimes the instantiation fails in an indirect classloading failure,
// which results in a LinkageError
LOGGER.log(logLevel(item), "Failed to load "+item.className(), e);
- } catch (Exception e) {
- LOGGER.log(logLevel(item), "Failed to load "+item.className(), e);
}
}
@@ -678,9 +671,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
// according to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6459208
// this appears to be the only way to force a class initialization
Class.forName(extType.getName(),true,extType.getClassLoader());
- } catch (Exception e) {
- LOGGER.log(logLevel(item), "Failed to scout "+item.className(), e);
- } catch (LinkageError e) {
+ } catch (Exception | LinkageError e) {
LOGGER.log(logLevel(item), "Failed to scout "+item.className(), e);
}
}
diff --git a/core/src/main/java/hudson/ExtensionList.java b/core/src/main/java/hudson/ExtensionList.java
index 7861de26c2a5b39bc006b922b92476d36cef46c0..46447fbcdabaffddfb0716b0cbbb94a73569f073 100644
--- a/core/src/main/java/hudson/ExtensionList.java
+++ b/core/src/main/java/hudson/ExtensionList.java
@@ -73,6 +73,7 @@ public class ExtensionList extends AbstractList {
* @deprecated as of 1.417
* Use {@link #jenkins}
*/
+ @Deprecated
public final Hudson hudson;
public final @CheckForNull Jenkins jenkins;
public final Class extensionType;
@@ -83,6 +84,8 @@ public class ExtensionList extends AbstractList {
@CopyOnWrite
private volatile List> extensions;
+ private final List listeners = new CopyOnWriteArrayList();
+
/**
* Place to store manually registered instances with the per-Hudson scope.
* {@link CopyOnWriteArrayList} is used here to support concurrent iterations and mutation.
@@ -93,6 +96,7 @@ public class ExtensionList extends AbstractList {
* @deprecated as of 1.416
* Use {@link #ExtensionList(Jenkins, Class)}
*/
+ @Deprecated
protected ExtensionList(Hudson hudson, Class extensionType) {
this((Jenkins)hudson,extensionType);
}
@@ -105,6 +109,7 @@ public class ExtensionList extends AbstractList {
* @deprecated as of 1.416
* Use {@link #ExtensionList(Jenkins, Class, CopyOnWriteArrayList)}
*/
+ @Deprecated
protected ExtensionList(Hudson hudson, Class extensionType, CopyOnWriteArrayList> legacyStore) {
this((Jenkins)hudson,extensionType,legacyStore);
}
@@ -126,11 +131,19 @@ public class ExtensionList extends AbstractList {
}
}
+ /**
+ * Add a listener to the extension list.
+ * @param listener The listener.
+ */
+ public void addListener(@Nonnull ExtensionListListener listener) {
+ listeners.add(listener);
+ }
+
/**
* Looks for the extension instance of the given type (subclasses excluded),
* or return null.
*/
- public U get(Class type) {
+ public @CheckForNull U get(Class type) {
for (T ext : this)
if(ext.getClass()==type)
return type.cast(ext);
@@ -180,7 +193,17 @@ public class ExtensionList extends AbstractList {
}
@Override
- public synchronized boolean remove(Object o) {
+ public boolean remove(Object o) {
+ try {
+ return removeSync(o);
+ } finally {
+ if(extensions!=null) {
+ fireOnChangeListeners();
+ }
+ }
+ }
+
+ private synchronized boolean removeSync(Object o) {
boolean removed = removeComponent(legacyInstances, o);
if(extensions!=null) {
List> r = new ArrayList>(extensions);
@@ -214,7 +237,18 @@ public class ExtensionList extends AbstractList {
* Prefer automatic registration.
*/
@Override
- public synchronized boolean add(T t) {
+ @Deprecated
+ public boolean add(T t) {
+ try {
+ return addSync(t);
+ } finally {
+ if(extensions!=null) {
+ fireOnChangeListeners();
+ }
+ }
+ }
+
+ private synchronized boolean addSync(T t) {
legacyInstances.add(new ExtensionComponent(t));
// if we've already filled extensions, add it
if(extensions!=null) {
@@ -270,6 +304,7 @@ public class ExtensionList extends AbstractList {
* Do not call from anywhere else.
*/
public void refresh(ExtensionComponentSet delta) {
+ boolean fireOnChangeListeners = false;
synchronized (getLoadLock()) {
if (extensions==null)
return; // not yet loaded. when we load it, we'll load everything visible by then, so no work needed
@@ -279,6 +314,20 @@ public class ExtensionList extends AbstractList {
List> l = Lists.newArrayList(extensions);
l.addAll(found);
extensions = sort(l);
+ fireOnChangeListeners = true;
+ }
+ }
+ if (fireOnChangeListeners) {
+ fireOnChangeListeners();
+ }
+ }
+
+ private void fireOnChangeListeners() {
+ for (ExtensionListListener listener : listeners) {
+ try {
+ listener.onChange();
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Error firing ExtensionListListener.onChange().", e);
}
}
}
@@ -325,6 +374,7 @@ public class ExtensionList extends AbstractList {
* @deprecated as of 1.416
* Use {@link #create(Jenkins, Class)}
*/
+ @Deprecated
public static ExtensionList create(Hudson hudson, Class type) {
return create((Jenkins)hudson,type);
}
diff --git a/core/src/main/java/hudson/ExtensionListListener.java b/core/src/main/java/hudson/ExtensionListListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..f5c8528add3995535060b3d39bd9bea75d447124
--- /dev/null
+++ b/core/src/main/java/hudson/ExtensionListListener.java
@@ -0,0 +1,42 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2015, CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package hudson;
+
+/**
+ * {@link ExtensionList} listener.
+ *
+ * @author tom.fennelly@gmail.com
+ * @since 1.614
+ */
+public abstract class ExtensionListListener {
+
+ /**
+ * {@link ExtensionList} contents has changed.
+ *
+ * This would be called when an entry gets added to or removed from the list for any reason e.g.
+ * when a dynamically loaded plugin introduces a new {@link ExtensionPoint} implementation
+ * that adds an entry to the {@link ExtensionList} being listened to.
+ */
+ public abstract void onChange();
+}
diff --git a/core/src/main/java/hudson/ExtensionListView.java b/core/src/main/java/hudson/ExtensionListView.java
index 95ea80068c636ffc393785fb27f53ce7e4517961..59b81ffd95869297d20ea67694b923652235812d 100644
--- a/core/src/main/java/hudson/ExtensionListView.java
+++ b/core/src/main/java/hudson/ExtensionListView.java
@@ -24,7 +24,6 @@
package hudson;
import jenkins.model.Jenkins;
-import hudson.tasks.UserNameResolver;
import hudson.util.CopyOnWriteList;
import java.util.AbstractList;
diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java
index 1fe2de55c802bc06dd0223cc6bdadaa1db87e929..9690c39cd0d03cef537a76aa399c7ed2bced97b1 100644
--- a/core/src/main/java/hudson/FilePath.java
+++ b/core/src/main/java/hudson/FilePath.java
@@ -33,7 +33,6 @@ 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;
@@ -70,7 +69,6 @@ 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.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.kohsuke.stapler.Stapler;
@@ -92,7 +90,6 @@ import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.io.Writer;
-import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
@@ -120,6 +117,8 @@ import static hudson.Util.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import jenkins.security.MasterToSlaveCallable;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.jenkinsci.remoting.RoleChecker;
import org.jenkinsci.remoting.RoleSensitive;
@@ -175,7 +174,7 @@ import org.jenkinsci.remoting.RoleSensitive;
*
*
*
- * When {@link FileCallable} is transfered to a remote node, it will be done so
+ * When {@link FileCallable} is transferred to a remote node, it will be done so
* by using the same Java serialization scheme that the remoting module uses.
* See {@link Channel} for more about this.
*
@@ -367,6 +366,7 @@ public final class FilePath implements Serializable {
*
* @deprecated as of 1.315. Use {@link #zip(OutputStream)} that has more consistent name.
*/
+ @Deprecated
public void createZipArchive(OutputStream os) throws IOException, InterruptedException {
zip(os);
}
@@ -411,6 +411,7 @@ public final class FilePath implements Serializable {
* @deprecated as of 1.315
* Use {@link #zip(OutputStream,String)} that has more consistent name.
*/
+ @Deprecated
public void createZipArchive(OutputStream os, final String glob) throws IOException, InterruptedException {
archive(ArchiverFactory.ZIP,os,glob);
}
@@ -2266,12 +2267,15 @@ public final class FilePath implements Serializable {
/**
* Reads from a tar stream and stores obtained files to the base dir.
+ * @since TODO supports large files > 10 GB, migration to commons-compress
*/
private void readFromTar(String name, File baseDir, InputStream in) throws IOException {
- TarInputStream t = new TarInputStream(in);
+ TarArchiveInputStream t = new TarArchiveInputStream(in);
+
+ // TarInputStream t = new TarInputStream(in);
try {
- TarEntry te;
- while ((te = t.getNextEntry()) != null) {
+ TarArchiveEntry te;
+ while ((te = t.getNextTarEntry()) != null) {
File f = new File(baseDir,te.getName());
if(te.isDirectory()) {
mkdirs(f);
@@ -2280,8 +2284,7 @@ public final class FilePath implements Serializable {
if (parent != null) mkdirs(parent);
writing(f);
- byte linkFlag = (Byte) LINKFLAG_FIELD.get(te);
- if (linkFlag==TarEntry.LF_SYMLINK) {
+ if (te.isSymbolicLink()) {
new FilePath(f).symlinkTo(te.getLinkName(), TaskListener.NULL);
} else {
IOUtils.copy(t,f);
@@ -2298,8 +2301,6 @@ public final class FilePath implements Serializable {
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // process this later
throw new IOException("Failed to extract "+name,e);
- } catch (IllegalAccessException e) {
- throw new IOException("Failed to extract "+name,e);
} finally {
t.close();
}
@@ -2337,12 +2338,20 @@ public final class FilePath implements Serializable {
* @see #validateFileMask(FilePath, String)
* @deprecated use {@link #validateAntFileMask(String, int)} instead
*/
+ @Deprecated
public String validateAntFileMask(final String fileMasks) throws IOException, InterruptedException {
return validateAntFileMask(fileMasks, Integer.MAX_VALUE);
}
/**
- * Default bound for {@link #validateAntFileMask(String, int)}.
+ * Same as {@link #validateFileMask(String, int, boolean)} with caseSensitive set to true
+ */
+ public String validateAntFileMask(final String fileMasks, final int bound) throws IOException, InterruptedException {
+ return validateAntFileMask(fileMasks, bound, true);
+ }
+
+ /**
+ * Default bound for {@link #validateAntFileMask(String, int, boolean)}.
* @since 1.592
*/
public static int VALIDATE_ANT_FILE_MASK_BOUND = Integer.getInteger(FilePath.class.getName() + ".VALIDATE_ANT_FILE_MASK_BOUND", 10000);
@@ -2360,7 +2369,7 @@ public final class FilePath implements Serializable {
* @throws InterruptedException not only in case of a channel failure, but also if too many operations were performed without finding any matches
* @since 1.484
*/
- public String validateAntFileMask(final String fileMasks, final int bound) throws IOException, InterruptedException {
+ public String validateAntFileMask(final String fileMasks, final int bound, final boolean caseSensitive) throws IOException, InterruptedException {
return act(new MasterToSlaveFileCallable() {
private static final long serialVersionUID = 1;
public String invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException {
@@ -2371,15 +2380,21 @@ public final class FilePath implements Serializable {
while(tokens.hasMoreTokens()) {
final String fileMask = tokens.nextToken().trim();
- if(hasMatch(dir,fileMask))
+ if(hasMatch(dir,fileMask,caseSensitive))
continue; // no error on this portion
+
+ // JENKINS-5253 - if we can get some match in case insensitive mode
+ // and user requested case sensitive match, notify the user
+ if (caseSensitive && hasMatch(dir, fileMask, false)) {
+ return Messages.FilePath_validateAntFileMask_matchWithCaseInsensitive(fileMask);
+ }
// in 1.172 we introduced an incompatible change to stop using ' ' as the separator
// so see if we can match by using ' ' as the separator
if(fileMask.contains(" ")) {
boolean matched = true;
for (String token : Util.tokenize(fileMask))
- matched &= hasMatch(dir,token);
+ matched &= hasMatch(dir,token,caseSensitive);
if(matched)
return Messages.FilePath_validateAntFileMask_whitespaceSeprator();
}
@@ -2395,7 +2410,7 @@ public final class FilePath implements Serializable {
if(idx==-1) break;
f=f.substring(idx+1);
- if(hasMatch(dir,f))
+ if(hasMatch(dir,f,caseSensitive))
return Messages.FilePath_validateAntFileMask_doesntMatchAndSuggest(fileMask,f);
}
}
@@ -2403,6 +2418,7 @@ public final class FilePath implements Serializable {
{// check the (2) above next as this is more expensive.
// Try prepending "**/" to see if that results in a match
FileSet fs = Util.createFileSet(reading(dir),"**/"+fileMask);
+ fs.setCaseSensitive(caseSensitive);
DirectoryScanner ds = fs.getDirectoryScanner(new Project());
if(ds.getIncludedFilesCount()!=0) {
// try shorter name first so that the suggestion results in least amount of changes
@@ -2422,7 +2438,7 @@ public final class FilePath implements Serializable {
prefix+=f.substring(0,idx)+'/';
f=f.substring(idx+1);
- if(hasMatch(dir,prefix+fileMask))
+ if(hasMatch(dir,prefix+fileMask,caseSensitive))
return Messages.FilePath_validateAntFileMask_doesntMatchAndSuggest(fileMask, prefix+fileMask);
}
}
@@ -2434,7 +2450,7 @@ public final class FilePath implements Serializable {
String pattern = fileMask;
while(true) {
- if(hasMatch(dir,pattern)) {
+ if(hasMatch(dir,pattern,caseSensitive)) {
// found a match
if(previous==null)
return Messages.FilePath_validateAntFileMask_portionMatchAndSuggest(fileMask,pattern);
@@ -2460,7 +2476,7 @@ public final class FilePath implements Serializable {
return null; // no error
}
- private boolean hasMatch(File dir, String pattern) throws InterruptedException {
+ private boolean hasMatch(File dir, String pattern, boolean bCaseSensitive) throws InterruptedException {
class Cancel extends RuntimeException {}
DirectoryScanner ds = bound == Integer.MAX_VALUE ? new DirectoryScanner() : new DirectoryScanner() {
int ticks;
@@ -2478,6 +2494,7 @@ public final class FilePath implements Serializable {
};
ds.setBasedir(reading(dir));
ds.setIncludes(new String[] {pattern});
+ ds.setCaseSensitive(bCaseSensitive);
try {
ds.scan();
} catch (Cancel c) {
@@ -2504,18 +2521,32 @@ public final class FilePath implements Serializable {
}
/**
- * Shortcut for {@link #validateFileMask(String)} in case the left-hand side can be null.
+ * Short for {@code validateFileMask(path, value, true)}
*/
public static FormValidation validateFileMask(@CheckForNull FilePath path, String value) throws IOException {
+ return FilePath.validateFileMask(path, value, true);
+ }
+
+ /**
+ * Shortcut for {@link #validateFileMask(String,true,boolean)} as the left-hand side can be null.
+ */
+ public static FormValidation validateFileMask(@CheckForNull FilePath path, String value, boolean caseSensitive) throws IOException {
if(path==null) return FormValidation.ok();
- return path.validateFileMask(value);
+ return path.validateFileMask(value, true, caseSensitive);
}
/**
- * Short for {@code validateFileMask(value,true)}
+ * Short for {@code validateFileMask(value, true, true)}
*/
public FormValidation validateFileMask(String value) throws IOException {
- return validateFileMask(value,true);
+ return validateFileMask(value, true, true);
+ }
+
+ /**
+ * Short for {@code validateFileMask(value, errorIfNotExist, true)}
+ */
+ public FormValidation validateFileMask(String value, boolean errorIfNotExist) throws IOException {
+ return validateFileMask(value, errorIfNotExist, true);
}
/**
@@ -2524,7 +2555,7 @@ public final class FilePath implements Serializable {
* or admin permission if no such ancestor is found.
* @since 1.294
*/
- public FormValidation validateFileMask(String value, boolean errorIfNotExist) throws IOException {
+ public FormValidation validateFileMask(String value, boolean errorIfNotExist, boolean caseSensitive) throws IOException {
checkPermissionForValidate();
value = fixEmpty(value);
@@ -2535,7 +2566,7 @@ public final class FilePath implements Serializable {
if(!exists()) // no workspace. can't check
return FormValidation.ok();
- String msg = validateAntFileMask(value, VALIDATE_ANT_FILE_MASK_BOUND);
+ String msg = validateAntFileMask(value, VALIDATE_ANT_FILE_MASK_BOUND, caseSensitive);
if(errorIfNotExist) return FormValidation.error(msg);
else return FormValidation.warning(msg);
} catch (InterruptedException e) {
@@ -2722,20 +2753,6 @@ public final class FilePath implements Serializable {
}
};
- private static final Field LINKFLAG_FIELD = getTarEntryLinkFlagField();
-
- private static Field getTarEntryLinkFlagField() {
- try {
- Field f = TarEntry.class.getDeclaredField("linkFlag");
- f.setAccessible(true);
- return f;
- } catch (SecurityException e) {
- throw new AssertionError(e);
- } catch (NoSuchFieldException e) {
- throw new AssertionError(e);
- }
- }
-
/**
* Gets the {@link FilePath} representation of the "~" directory
* (User's home directory in the Unix sense) of the given channel.
diff --git a/core/src/main/java/hudson/FileSystemProvisioner.java b/core/src/main/java/hudson/FileSystemProvisioner.java
index 58680eb88847035b1d00790d2c453e5edb82537e..bbb98f5e9d0c873ca4bc5af33b1d2b0b34048e58 100644
--- a/core/src/main/java/hudson/FileSystemProvisioner.java
+++ b/core/src/main/java/hudson/FileSystemProvisioner.java
@@ -204,6 +204,7 @@ public abstract class FileSystemProvisioner implements ExtensionPoint, Describab
/**
* @deprecated as of 1.350
*/
+ @Deprecated
public WorkspaceSnapshot snapshot(AbstractBuild, ?> build, FilePath ws, TaskListener listener) throws IOException, InterruptedException {
return snapshot(build, ws, "**/*", listener);
}
@@ -237,7 +238,7 @@ public abstract class FileSystemProvisioner implements ExtensionPoint, Describab
@Extension
public static final class DescriptorImpl extends FileSystemProvisionerDescriptor {
public boolean discard(FilePath ws, TaskListener listener) throws IOException, InterruptedException {
- // the default provisioner doens't do anything special,
+ // the default provisioner does not do anything special,
// so allow other types to manage it
return false;
}
diff --git a/core/src/main/java/hudson/FileSystemProvisionerDescriptor.java b/core/src/main/java/hudson/FileSystemProvisionerDescriptor.java
index 2cb0506fa9da19b11cea287563d6a6c4343e7b9c..4827366d36f41f3c7845aa71571eef7480d61f09 100644
--- a/core/src/main/java/hudson/FileSystemProvisionerDescriptor.java
+++ b/core/src/main/java/hudson/FileSystemProvisionerDescriptor.java
@@ -39,7 +39,7 @@ public abstract class FileSystemProvisionerDescriptor extends Descriptor
* Because users may modify the file system behind Hudson, and slaves may come and go when
- * configuration changes hapen, in general case Hudson is unable to keep track of which jobs
+ * configuration changes happen, in general case Hudson is unable to keep track of which jobs
* have workspaces in which slaves.
*
*
diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java
index f331d7f1cb7b70f19335d1e35ff9639fa0400801..9f74f42b20647bba4d770e2dcec9ddffbdd9008f 100644
--- a/core/src/main/java/hudson/Functions.java
+++ b/core/src/main/java/hudson/Functions.java
@@ -28,6 +28,7 @@ package hudson;
import hudson.cli.CLICommand;
import hudson.console.ConsoleAnnotationDescriptor;
import hudson.console.ConsoleAnnotatorFactory;
+import hudson.init.InitMilestone;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.Describable;
@@ -74,6 +75,7 @@ import hudson.tasks.UserAvatarResolver;
import hudson.util.Area;
import hudson.util.FormValidation.CheckMethod;
import hudson.util.Iterators;
+import hudson.util.jna.GNUCLibrary;
import hudson.util.Secret;
import hudson.views.MyViewsTabBar;
import hudson.views.ViewsTabBar;
@@ -151,6 +153,7 @@ import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import hudson.util.RunList;
import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.CheckForNull;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@@ -201,7 +204,25 @@ public class Functions {
public static String rfc822Date(Calendar cal) {
return Util.RFC822_DATETIME_FORMATTER.format(cal.getTime());
}
-
+
+ /**
+ * During Jenkins start-up, before {@link InitMilestone#PLUGINS_STARTED} the extensions lists will be empty
+ * and they are not guaranteed to be fully populated until after {@link InitMilestone#EXTENSIONS_AUGMENTED}.
+ * If you attempt to access the extensions list from a UI thread while the extensions are being loaded you will
+ * hit a big honking great monitor lock that will block until the effective extension list has been determined
+ * (as if a plugin fails to start, all of the failed plugin's extensions and any dependent plugins' extensions
+ * will have to be evicted from the list of extensions. In practical terms this only affects the
+ * "Jenkins is loading" screen, but as that screen uses the generic layouts we provide this utility method
+ * so that the generic layouts can avoid iterating extension lists while Jenkins is starting up.
+ *
+ * @return {@code true} if the extensions lists have been populated.
+ * @since 1.607
+ */
+ public static boolean isExtensionsAvailable() {
+ final Jenkins jenkins = Jenkins.getInstance();
+ return jenkins != null && jenkins.getInitLevel().compareTo(InitMilestone.EXTENSIONS_AUGMENTED) >= 0;
+ }
+
public static void initPageVariables(JellyContext context) {
StaplerRequest currentRequest = Stapler.getCurrentRequest();
String rootURL = currentRequest.getContextPath();
@@ -419,6 +440,7 @@ public class Functions {
* JEXL now supports the real ternary operator "x?y:z", so this work around
* is no longer necessary.
*/
+ @Deprecated
public static Object ifThenElse(boolean cond, Object thenValue, Object elseValue) {
return cond ? thenValue : elseValue;
}
@@ -438,6 +460,15 @@ public class Functions {
public static boolean isWindows() {
return File.pathSeparatorChar==';';
}
+
+ public static boolean isGlibcSupported() {
+ try {
+ GNUCLibrary.LIBC.getpid();
+ return true;
+ } catch(Throwable t) {
+ return false;
+ }
+ }
public static List getLogRecords() {
return Jenkins.logRecords;
@@ -820,6 +851,10 @@ public class Functions {
return JobPropertyDescriptor.getPropertyDescriptors(clazz);
}
+ public static List getJobPropertyDescriptors(Job job) {
+ return DescriptorVisibilityFilter.apply(job, JobPropertyDescriptor.getPropertyDescriptors(job.getClass()));
+ }
+
public static List> getBuildWrapperDescriptors(AbstractProject,?> project) {
return BuildWrappers.getFor(project);
}
@@ -1364,7 +1399,7 @@ public class Functions {
/**
* If the value exists, return that value. Otherwise return the default value.
*
- * Starting 1.294, JEXL supports the elvis operator "x?:y" that supercedes this.
+ * Starting 1.294, JEXL supports the elvis operator "x?:y" that supersedes this.
*
* @since 1.150
*/
@@ -1372,7 +1407,17 @@ public class Functions {
return value!=null ? value : defaultValue;
}
- public static String printThrowable(Throwable t) {
+ /**
+ * Gets info about the specified {@link Throwable}.
+ * @param t Input {@link Throwable}
+ * @return If {@link Throwable} is not null, a summary info with the
+ * stacktrace will be returned. Otherwise, the method returns a default
+ * "No exception details" string.
+ */
+ public static String printThrowable(@CheckForNull Throwable t) {
+ if (t == null) {
+ return Messages.Functions_NoExceptionDetails();
+ }
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
return sw.toString();
@@ -1554,6 +1599,7 @@ public class Functions {
* @deprecated
* Use {@link #calcCheckUrl}
*/
+ @Deprecated
public String getCheckUrl(String userDefined, Object descriptor, String field) {
if(userDefined!=null || field==null) return userDefined;
if (descriptor instanceof Descriptor) {
@@ -1804,6 +1850,7 @@ public class Functions {
* @deprecated as of 1.451
* Use {@link #getAvatar}
*/
+ @Deprecated
public String getUserAvatar(User user, String avatarSize) {
return getAvatar(user,avatarSize);
}
@@ -1868,9 +1915,10 @@ public class Functions {
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());
+ int p = TcpSlaveAgentListener.CLI_PORT !=null ? TcpSlaveAgentListener.CLI_PORT : tal.getPort();
+ rsp.setIntHeader("X-Hudson-CLI-Port", p);
+ rsp.setIntHeader("X-Jenkins-CLI-Port", p);
+ rsp.setIntHeader("X-Jenkins-CLI2-Port", p);
rsp.setHeader("X-Jenkins-CLI-Host", TcpSlaveAgentListener.CLI_HOST_NAME);
}
}
diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java
index be003dfb648a76a3463bdae92151d9f812e37e0f..5be9397699f83c37d694fb9caceb8527df5caa27 100644
--- a/core/src/main/java/hudson/Launcher.java
+++ b/core/src/main/java/hudson/Launcher.java
@@ -29,7 +29,6 @@ import hudson.util.QuotedStringTokenizer;
import jenkins.model.Jenkins;
import hudson.model.TaskListener;
import hudson.model.Node;
-import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.Pipe;
import hudson.remoting.RemoteInputStream;
@@ -133,6 +132,7 @@ public abstract class Launcher {
* figure out the current {@link Computer} from within a build, use
* {@link Computer#currentComputer()}
*/
+ @Deprecated
public Computer getComputer() {
for( Computer c : Jenkins.getInstance().getComputers() )
if(c.getChannel()==channel)
@@ -412,6 +412,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public final Proc launch(String cmd, Map env, OutputStream out, FilePath workDir) throws IOException {
return launch(cmd,Util.mapToEnv(env),out,workDir);
}
@@ -420,6 +421,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public final Proc launch(String[] cmd, Map env, OutputStream out, FilePath workDir) throws IOException {
return launch(cmd, Util.mapToEnv(env), out, workDir);
}
@@ -428,6 +430,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public final Proc launch(String[] cmd, Map env, InputStream in, OutputStream out) throws IOException {
return launch(cmd, Util.mapToEnv(env), in, out);
}
@@ -449,6 +452,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public final Proc launch(String[] cmd, boolean[] mask, Map env, OutputStream out, FilePath workDir) throws IOException {
return launch(cmd, mask, Util.mapToEnv(env), out, workDir);
}
@@ -470,6 +474,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public final Proc launch(String[] cmd, boolean[] mask, Map env, InputStream in, OutputStream out) throws IOException {
return launch(cmd, mask, Util.mapToEnv(env), in, out);
}
@@ -478,6 +483,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public final Proc launch(String cmd,String[] env,OutputStream out, FilePath workDir) throws IOException {
return launch(Util.tokenize(cmd),env,out,workDir);
}
@@ -486,6 +492,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public final Proc launch(String[] cmd, String[] env, OutputStream out, FilePath workDir) throws IOException {
return launch(cmd, env, null, out, workDir);
}
@@ -494,6 +501,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public final Proc launch(String[] cmd, String[] env, InputStream in, OutputStream out) throws IOException {
return launch(cmd, env, in, out, null);
}
@@ -515,6 +523,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public final Proc launch(String[] cmd, boolean[] mask, String[] env, OutputStream out, FilePath workDir) throws IOException {
return launch(cmd, mask, env, null, out, workDir);
}
@@ -536,6 +545,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public final Proc launch(String[] cmd, boolean[] mask, String[] env, InputStream in, OutputStream out) throws IOException {
return launch(cmd, mask, env, in, out, null);
}
@@ -554,6 +564,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public Proc launch(String[] cmd, String[] env, InputStream in, OutputStream out, FilePath workDir) throws IOException {
return launch(launch().cmds(cmd).envs(env).stdin(in).stdout(out).pwd(workDir));
}
@@ -576,6 +587,7 @@ public abstract class Launcher {
* @deprecated as of 1.311
* Use {@link #launch()} and its associated builder pattern
*/
+ @Deprecated
public Proc launch(String[] cmd, boolean[] mask, String[] env, InputStream in, OutputStream out, FilePath workDir) throws IOException {
return launch(launch().cmds(cmd).masks(mask).envs(env).stdin(in).stdout(out).pwd(workDir));
}
diff --git a/core/src/main/java/hudson/LauncherDecorator.java b/core/src/main/java/hudson/LauncherDecorator.java
index 3bd5f7d0212b56bb87be7ebe56d1e5bda08c96c7..86f69fe496a2e3f6cfabe3cb8ddbe15bcc3fe0f0 100644
--- a/core/src/main/java/hudson/LauncherDecorator.java
+++ b/core/src/main/java/hudson/LauncherDecorator.java
@@ -1,6 +1,5 @@
package hudson;
-import jenkins.model.Jenkins;
import hudson.model.Node;
import hudson.model.Executor;
import hudson.tasks.BuildWrapper;
diff --git a/core/src/main/java/hudson/MarkupText.java b/core/src/main/java/hudson/MarkupText.java
index 4a56b1eb60faf98c04c5e18429630f4f2b401b04..5240cd3bdf62f6109f8362804576ead3c9147f54 100644
--- a/core/src/main/java/hudson/MarkupText.java
+++ b/core/src/main/java/hudson/MarkupText.java
@@ -283,6 +283,7 @@ public class MarkupText extends AbstractMarkupText {
* Use {@link #toString(boolean)} to be explicit about the escape mode.
*/
@Override
+ @Deprecated
public String toString() {
return toString(false);
}
diff --git a/core/src/main/java/hudson/Plugin.java b/core/src/main/java/hudson/Plugin.java
index 1e11d23bcd848cc1625542e564cbc7833228fa05..75af4fda013ef329000e81e1bed297d63dc6ab6a 100644
--- a/core/src/main/java/hudson/Plugin.java
+++ b/core/src/main/java/hudson/Plugin.java
@@ -40,15 +40,18 @@ import java.io.File;
import net.sf.json.JSONObject;
import com.thoughtworks.xstream.XStream;
+import hudson.init.Initializer;
+import hudson.init.Terminator;
import java.net.URI;
import java.net.URISyntaxException;
+import jenkins.model.GlobalConfiguration;
import org.kohsuke.stapler.HttpResponses;
/**
* Base class of Hudson plugin.
*
*
- * A plugin may derive from this class, or it may directly define extension
+ * A plugin may {@linkplain #Plugin derive from this class}, or it may directly define extension
* points annotated with {@link hudson.Extension}. For a list of extension
* points, see
* https://wiki.jenkins-ci.org/display/JENKINS/Extension+points.
@@ -78,6 +81,22 @@ import org.kohsuke.stapler.HttpResponses;
*/
public abstract class Plugin implements Saveable {
+ /**
+ * You do not need to create custom subtypes:
+ *
+ *
{@code config.jelly}, {@link #configure(StaplerRequest, JSONObject)}, {@link #load}, and {@link #save}
+ * can be replaced by {@link GlobalConfiguration}
+ *
{@link #start} and {@link #postInitialize} can be replaced by {@link Initializer} (or {@link ItemListener#onLoaded})
+ *
{@link #stop} can be replaced by {@link Terminator}
+ *
{@link #setServletContext} can be replaced by {@link Jenkins#servletContext}
+ *
+ * Note that every plugin gets a {@link DummyImpl} by default,
+ * which will still route the URL space, serve {@link #getWrapper}, and so on.
+ * @deprecated Use more modern APIs rather than subclassing.
+ */
+ @Deprecated
+ protected Plugin() {}
+
/**
* Set by the {@link PluginManager}, before the {@link #start()} method is called.
* This points to the {@link PluginWrapper} that wraps
@@ -164,6 +183,7 @@ public abstract class Plugin implements Saveable {
* @since 1.233
* @deprecated as of 1.305 override {@link #configure(StaplerRequest,JSONObject)} instead
*/
+ @Deprecated
public void configure(JSONObject formData) throws IOException, ServletException, FormException {
}
diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java
index 41605eed99865a7a0d5f94d134256e80c1f9398e..d7eb14f7b6dd2000c3b5a9d12bf407bf712a4beb 100644
--- a/core/src/main/java/hudson/PluginManager.java
+++ b/core/src/main/java/hudson/PluginManager.java
@@ -51,6 +51,8 @@ import jenkins.RestartRequiredException;
import jenkins.YesNoMaybe;
import jenkins.model.Jenkins;
import jenkins.util.io.OnMaster;
+import jenkins.util.xml.RestrictiveEntityResolver;
+
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.fileupload.FileItem;
@@ -69,6 +71,7 @@ import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.QueryParameter;
+import org.kohsuke.stapler.StaplerOverridable;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
@@ -109,12 +112,15 @@ import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
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.SEVERE;
import static java.util.logging.Level.WARNING;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
@@ -125,7 +131,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
* @author Kohsuke Kawaguchi
*/
@ExportedBean
-public abstract class PluginManager extends AbstractModelObject implements OnMaster {
+public abstract class PluginManager extends AbstractModelObject implements OnMaster, StaplerOverridable {
/**
* All discovered plugins.
*/
@@ -148,6 +154,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
* {@link PluginManager} can now live longer than {@link jenkins.model.Jenkins} instance, so
* use {@code Hudson.getInstance().servletContext} instead.
*/
+ @Deprecated
public final ServletContext context;
/**
@@ -211,6 +218,16 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
return new Api(this);
}
+ /**
+ * Find all registered overrides (intended to allow overriding/adding views)
+ * @return List of extensions
+ * @since 1.627
+ */
+ @Override
+ public Collection getOverrides() {
+ return PluginManagerStaplerOverride.all();
+ }
+
/**
* Called immediately after the construction.
* This is a separate method so that code executed from here will see a valid value in
@@ -505,9 +522,33 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
}
+ // Redo who depends on who.
+ resolveDependantPlugins();
+
LOGGER.info("Plugin " + p.getShortName()+":"+p.getVersion() + " dynamically installed");
}
+ @Restricted(NoExternalUse.class)
+ public synchronized void resolveDependantPlugins() {
+ for (PluginWrapper plugin : plugins) {
+ Set dependants = new HashSet<>();
+ for (PluginWrapper possibleDependant : plugins) {
+ // The plugin could have just been deleted. If so, it doesn't
+ // count as a dependant.
+ if (possibleDependant.isDeleted()) {
+ continue;
+ }
+ List dependencies = possibleDependant.getDependencies();
+ for (Dependency dependency : dependencies) {
+ if (dependency.shortName.equals(plugin.getShortName())) {
+ dependants.add(possibleDependant.getShortName());
+ }
+ }
+ }
+ plugin.setDependants(dependants);
+ }
+ }
+
/**
* If the war file has any "/WEB-INF/plugins/[*.jpi | *.hpi]", extract them into the plugin directory.
*
@@ -764,6 +805,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
*/
public void doInstall(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
boolean dynamicLoad = req.getParameter("dynamicLoad")!=null;
+ final List> deployJobs = new ArrayList<>();
Enumeration en = req.getParameterNames();
while (en.hasMoreElements()) {
@@ -796,9 +838,36 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
if (p == null) {
throw new Failure("No such plugin: " + n);
}
- p.deploy(dynamicLoad);
+
+ deployJobs.add(p.deploy(dynamicLoad));
}
}
+
+ // Fire a one-off thread to wait for the plugins to be deployed and then
+ // refresh the dependant plugins list.
+ new Thread() {
+ @Override
+ public void run() {
+ INSTALLING: while (true) {
+ for (Future deployJob : deployJobs) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ LOGGER.log(SEVERE, "Unexpected error while waiting for some plugins to install. Plugin Manager state may be invalid. Please restart Jenkins ASAP.", e);
+ }
+ if (!deployJob.isCancelled() && !deployJob.isDone()) {
+ // One of the plugins is not installing/canceled, so
+ // go back to sleep and try again in a while.
+ continue INSTALLING;
+ }
+ }
+ // All the plugins are installed. It's now safe to refresh.
+ resolveDependantPlugins();
+ break;
+ }
+ }
+ }.start();
+
rsp.sendRedirect("../updateCenter/");
}
@@ -806,6 +875,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
/**
* Bare-minimum configuration mechanism to change the update center.
*/
+ @RequirePOST
public HttpResponse doSiteConfigure(@QueryParameter String site) throws IOException {
Jenkins hudson = Jenkins.getInstance();
hudson.checkPermission(CONFIGURE_UPDATECENTER);
@@ -821,6 +891,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
}
+ @RequirePOST
public HttpResponse doProxyConfigure(StaplerRequest req) throws IOException, ServletException {
Jenkins jenkins = Jenkins.getInstance();
jenkins.checkPermission(CONFIGURE_UPDATECENTER);
@@ -839,6 +910,7 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
/**
* Uploads a plugin.
*/
+ @RequirePOST
public HttpResponse doUploadPlugin(StaplerRequest req) throws IOException, ServletException {
try {
Jenkins.getInstance().checkPermission(UPLOAD_PLUGINS);
@@ -1045,6 +1117,12 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
requestedPlugins.put(shortName, requested);
}
}
+
+ @Override public InputSource resolveEntity(String publicId, String systemId) throws IOException,
+ SAXException {
+ return RestrictiveEntityResolver.INSTANCE.resolveEntity(publicId, systemId);
+ }
+
});
} catch (SAXException x) {
throw new IOException("Failed to parse XML",x);
diff --git a/core/src/main/java/hudson/PluginManagerStaplerOverride.java b/core/src/main/java/hudson/PluginManagerStaplerOverride.java
new file mode 100644
index 0000000000000000000000000000000000000000..3103ba181835d9ba31bab32fb6581f9aee82b9a9
--- /dev/null
+++ b/core/src/main/java/hudson/PluginManagerStaplerOverride.java
@@ -0,0 +1,26 @@
+package hudson;
+
+
+import javax.annotation.Nonnull;
+
+/**
+ * Extension point for selectively overriding parts of the {@link PluginManager} views
+ * Anything extending this and registered with an @Extension can replace existing views and define new views.
+ *
+ * It is also possible to add/modify API calls coming via Stapler, but this requires caution.
+ *
+ * In both cases, this is simply done by defining a resource or method that matches the existing one
+ *
+ * @author Sam Van Oort
+ * @since 1.627
+ */
+public abstract class PluginManagerStaplerOverride implements ExtensionPoint {
+
+ /**
+ * Return all implementations of this extension point
+ * @return All implementations of this extension point
+ */
+ public static @Nonnull ExtensionList all() {
+ return ExtensionList.lookup(PluginManagerStaplerOverride.class);
+ }
+}
diff --git a/core/src/main/java/hudson/PluginWrapper.java b/core/src/main/java/hudson/PluginWrapper.java
index aa683e630f7af7ca0306d86ad3474c2269584f1b..60eabf68cf5005593f278f2718336e7ab5ed8cc0 100644
--- a/core/src/main/java/hudson/PluginWrapper.java
+++ b/core/src/main/java/hudson/PluginWrapper.java
@@ -24,6 +24,7 @@
*/
package hudson;
+import com.google.common.collect.ImmutableSet;
import hudson.PluginManager.PluginInstanceStore;
import hudson.model.Api;
import hudson.model.ModelObject;
@@ -40,7 +41,10 @@ import java.io.OutputStream;
import java.io.Closeable;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.jar.Manifest;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
@@ -57,13 +61,14 @@ import java.util.Enumeration;
import java.util.jar.JarFile;
import java.util.logging.Level;
import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
/**
* Represents a Jenkins plug-in and associated control information
* for Jenkins to control {@link Plugin}.
*
*
- * A plug-in is packaged into a jar file whose extension is ".jpi" (or ".hpi" for backward compatability),
+ * A plug-in is packaged into a jar file whose extension is ".jpi" (or ".hpi" for backward compatibility),
* A plugin needs to have a special manifest entry to identify what it is.
*
*
@@ -150,6 +155,54 @@ public class PluginWrapper implements Comparable, ModelObject {
*/
/*package*/ boolean isBundled;
+ /**
+ * List of plugins that depend on this plugin.
+ */
+ private Set dependants = Collections.emptySet();
+
+ /**
+ * The core can depend on a plugin if it is bundled. Sometimes it's the only thing that
+ * depends on the plugin e.g. UI support library bundle plugin.
+ */
+ private static Set CORE_ONLY_DEPENDANT = ImmutableSet.copyOf(Arrays.asList("jenkins-core"));
+
+ /**
+ * Set the list of components that depend on this plugin.
+ * @param dependants The list of components that depend on this plugin.
+ */
+ public void setDependants(@Nonnull Set dependants) {
+ this.dependants = dependants;
+ }
+
+ /**
+ * Get the list of components that depend on this plugin.
+ * @return The list of components that depend on this plugin.
+ */
+ public @Nonnull Set getDependants() {
+ if (isBundled && dependants.isEmpty()) {
+ return CORE_ONLY_DEPENDANT;
+ } else {
+ return dependants;
+ }
+ }
+
+ /**
+ * Does this plugin have anything that depends on it.
+ * @return {@code true} if something (Jenkins core, or another plugin) depends on this
+ * plugin, otherwise {@code false}.
+ */
+ public boolean hasDependants() {
+ return (isBundled || !dependants.isEmpty());
+ }
+
+ /**
+ * Does this plugin depend on any other plugins.
+ * @return {@code true} if this plugin depends on other plugins, otherwise {@code false}.
+ */
+ public boolean hasDependencies() {
+ return (dependencies != null && !dependencies.isEmpty());
+ }
+
@ExportedBean
public static final class Dependency {
@Exported
@@ -632,8 +685,14 @@ public class PluginWrapper implements Comparable, ModelObject {
@RequirePOST
public HttpResponse doDoUninstall() throws IOException {
- Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
+ Jenkins jenkins = Jenkins.getActiveInstance();
+
+ jenkins.checkPermission(Jenkins.ADMINISTER);
archive.delete();
+
+ // Redo who depends on who.
+ jenkins.getPluginManager().resolveDependantPlugins();
+
return HttpResponses.redirectViaContextPath("/pluginManager/installed"); // send back to plugin manager
}
diff --git a/core/src/main/java/hudson/Proc.java b/core/src/main/java/hudson/Proc.java
index 5114cbd2c445a9e490efe2477f7d396932296ad3..598fbca374efd2694ab661b9364bec81141cbd76 100644
--- a/core/src/main/java/hudson/Proc.java
+++ b/core/src/main/java/hudson/Proc.java
@@ -431,6 +431,7 @@ public abstract class Proc {
*
* @deprecated as of 1.399. Replaced by {@link Launcher.RemoteLauncher.ProcImpl}
*/
+ @Deprecated
public static final class RemoteProc extends Proc {
private final Future process;
diff --git a/core/src/main/java/hudson/ProxyConfiguration.java b/core/src/main/java/hudson/ProxyConfiguration.java
index 0e36a4cd9d946d9bfa1f0e97a55bca3852deedf9..9b5199092502586e20b2609e271a6f02f8184063 100644
--- a/core/src/main/java/hudson/ProxyConfiguration.java
+++ b/core/src/main/java/hudson/ProxyConfiguration.java
@@ -165,6 +165,7 @@ public final class ProxyConfiguration extends AbstractDescribableImpl properties) {
return replaceMacro(s,new VariableResolver.ByMap(properties));
}
-
+
/**
* Replaces the occurrence of '$key' by resolver.get('key').
*
@@ -143,7 +151,7 @@ public class Util {
if (s == null) {
return null;
}
-
+
int idx=0;
while(true) {
Matcher m = VARIABLE.matcher(s);
@@ -370,6 +378,30 @@ public class Util {
}
}
+ /**
+ * A mostly accurate check of whether a path is a relative path or not. This is designed to take a path against
+ * an unknown operating system so may give invalid results.
+ *
+ * @param path the path.
+ * @return {@code true} if the path looks relative.
+ * @since 1.606
+ */
+ public static boolean isRelativePath(String path) {
+ if (path.startsWith("/"))
+ return false;
+ if (path.startsWith("\\\\") && path.length() > 3 && path.indexOf('\\', 3) != -1)
+ return false; // a UNC path which is the most absolute you can get on windows
+ if (path.length() >= 3 && ':' == path.charAt(1)) {
+ // never mind that the drive mappings can be changed between sessions, we just want to
+ // know if the 3rd character is a `\` (or a '/' is acceptable too)
+ char p = path.charAt(0);
+ if (('A' <= p && p <= 'Z') || ('a' <= p && p <= 'z')) {
+ return path.charAt(2) != '\\' && path.charAt(2) != '/';
+ }
+ }
+ return true;
+ }
+
/**
* Creates a new temporary directory.
*/
@@ -624,7 +656,7 @@ public class Util {
* Converts a string into 128-bit AES key.
* @since 1.308
*/
- @Nonnull
+ @Nonnull
public static SecretKey toAes128Key(@Nonnull String s) {
try {
// turn secretKey into 256 bit hash
@@ -743,13 +775,14 @@ public class Util {
/**
* Combines number and unit, with a plural suffix if needed.
- *
- * @deprecated
- * Use individual localization methods instead.
+ *
+ * @deprecated
+ * Use individual localization methods instead.
* See {@link Messages#Util_year(Object)} for an example.
* Deprecated since 2009-06-24, remove method after 2009-12-24.
*/
@Nonnull
+ @Deprecated
public static String combine(long n, @Nonnull String suffix) {
String s = Long.toString(n)+' '+suffix;
if(n!=1)
@@ -780,7 +813,7 @@ 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).
*/
- @Nonnull
+ @Nonnull
public static String encode(@Nonnull String s) {
try {
boolean escaped = false;
@@ -888,7 +921,7 @@ public class Util {
/**
* Escapes HTML unsafe characters like <, & to the respective character entities.
*/
- @Nonnull
+ @Nonnull
public static String escape(@Nonnull String text) {
if (text==null) return null;
StringBuilder buf = new StringBuilder(text.length()+64);
@@ -1112,7 +1145,7 @@ public class Util {
* @param symlinkPath
* Where to create a symlink in (relative to {@code baseDir})
*/
- public static void createSymlink(@Nonnull File baseDir, @Nonnull String targetPath,
+ public static void createSymlink(@Nonnull File baseDir, @Nonnull String targetPath,
@Nonnull String symlinkPath, @Nonnull TaskListener listener) throws InterruptedException {
try {
if (createSymlinkJava7(baseDir, targetPath, symlinkPath)) {
@@ -1245,6 +1278,7 @@ public class Util {
* @deprecated as of 1.456
* Use {@link #resolveSymlink(File)}
*/
+ @Deprecated
public static String resolveSymlink(File link, TaskListener listener) throws InterruptedException, IOException {
return resolveSymlink(link);
}
@@ -1346,7 +1380,7 @@ public class Util {
* @deprecated since 2008-05-13. This method is broken (see ISSUE#1666). It should probably
* be removed but I'm not sure if it is considered part of the public API
* that needs to be maintained for backwards compatibility.
- * Use {@link #encode(String)} instead.
+ * Use {@link #encode(String)} instead.
*/
@Deprecated
public static String encodeRFC2396(String url) {
@@ -1367,7 +1401,7 @@ public class Util {
s = ""+s+"";
return s;
}
-
+
/**
* Returns the parsed string if parsed successful; otherwise returns the default number.
* If the string is null, empty or a ParseException is thrown then the defaultNumber
@@ -1389,16 +1423,36 @@ public class Util {
}
/**
- * Checks if the public method defined on the base type with the given arguments
- * are overridden in the given derived type.
+ * Checks if the method defined on the base type with the given arguments
+ * is overridden in the given derived type.
*/
public static boolean isOverridden(@Nonnull Class base, @Nonnull Class derived, @Nonnull String methodName, @Nonnull Class... types) {
+ return !getMethod(base, methodName, types).equals(getMethod(derived, methodName, types));
+ }
+
+ private static Method getMethod(@Nonnull Class clazz, @Nonnull String methodName, @Nonnull Class... types) {
+ Method res = null;
try {
- return !base.getMethod(methodName, types).equals(
- derived.getMethod(methodName,types));
+ res = clazz.getDeclaredMethod(methodName, types);
+ // private, static or final methods can not be overridden
+ if (res != null && (Modifier.isPrivate(res.getModifiers()) || Modifier.isFinal(res.getModifiers())
+ || Modifier.isStatic(res.getModifiers()))) {
+ res = null;
+ }
} catch (NoSuchMethodException e) {
+ // Method not found in clazz, let's search in superclasses
+ Class superclass = clazz.getSuperclass();
+ if (superclass != null) {
+ res = getMethod(superclass, methodName, types);
+ }
+ } catch (SecurityException e) {
throw new AssertionError(e);
}
+ if (res == null) {
+ throw new IllegalArgumentException(
+ String.format("Method %s not found in %s (or it is private, final or static)", methodName, clazz.getName()));
+ }
+ return res;
}
/**
diff --git a/core/src/main/java/hudson/WebAppMain.java b/core/src/main/java/hudson/WebAppMain.java
index 11d438d5ab0496010403ef6e046d9684c8ca0a66..d3375be646ad1a264c6a857178b826c25572b9e8 100644
--- a/core/src/main/java/hudson/WebAppMain.java
+++ b/core/src/main/java/hudson/WebAppMain.java
@@ -396,8 +396,10 @@ public class WebAppMain implements ServletContextListener {
if(instance!=null)
instance.cleanUp();
Thread t = initThread;
- if (t!=null)
+ if (t != null && t.isAlive()) {
+ LOGGER.log(Level.INFO, "Shutting down a Jenkins instance that was still starting up", new Throwable("reason"));
t.interrupt();
+ }
// Logger is in the system classloader, so if we don't do this
// the whole web app will never be undepoyed.
diff --git a/core/src/main/java/hudson/cli/BuildCommand.java b/core/src/main/java/hudson/cli/BuildCommand.java
index 39df01901ad1fe0a916afd00b704bed6cbf7c17f..5a018aeeb70d50b41e4ee94f3175c603f2779b44 100644
--- a/core/src/main/java/hudson/cli/BuildCommand.java
+++ b/core/src/main/java/hudson/cli/BuildCommand.java
@@ -35,7 +35,6 @@ import hudson.model.ParameterDefinition;
import hudson.Extension;
import hudson.AbortException;
import hudson.model.Item;
-import hudson.model.Result;
import hudson.model.TaskListener;
import hudson.model.User;
import hudson.model.queue.QueueTaskFuture;
@@ -52,7 +51,6 @@ 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;
diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java
index 2c68b0f6ff7ae094250e8dc89c14c81a8fad3280..a40a8b5bac1175fb1dfe2a6ade5a26f37d319deb 100644
--- a/core/src/main/java/hudson/cli/CLICommand.java
+++ b/core/src/main/java/hudson/cli/CLICommand.java
@@ -37,6 +37,7 @@ import hudson.remoting.ChannelProperty;
import hudson.security.CliAuthenticator;
import hudson.security.SecurityRealm;
import jenkins.security.MasterToSlaveCallable;
+import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.context.SecurityContext;
@@ -239,6 +240,9 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
stderr.println(e.getMessage());
printUsage(stderr, p);
return -1;
+ } catch (AccessDeniedException e) {
+ stderr.println(e.getMessage());
+ return -1;
} catch (AbortException e) {
// signals an error without stack trace
stderr.println(e.getMessage());
@@ -251,6 +255,10 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable {
stderr.println("Bad Credentials. Search the server log for "+id+" for more details.");
return -1;
} catch (Exception e) {
+ final String errorMsg = String.format("Unexpected exception occurred while performing %s command!",
+ getName());
+ stderr.println(errorMsg);
+ LOGGER.log(Level.WARNING, errorMsg, e);
e.printStackTrace(stderr);
return -1;
} finally {
diff --git a/core/src/main/java/hudson/cli/CliProtocol.java b/core/src/main/java/hudson/cli/CliProtocol.java
index 1d4f25627e077f237fa41dc731605309e7c72b59..eecb4c5faf9da881203b3668cbfd9fe23f330182 100644
--- a/core/src/main/java/hudson/cli/CliProtocol.java
+++ b/core/src/main/java/hudson/cli/CliProtocol.java
@@ -48,6 +48,7 @@ public class CliProtocol extends AgentProtocol {
* @deprecated as of 1.559
* Use {@link #Handler(NioChannelHub, Socket)}
*/
+ @Deprecated
public Handler(Socket socket) {
this(null,socket);
}
diff --git a/core/src/main/java/hudson/cli/CliProtocol2.java b/core/src/main/java/hudson/cli/CliProtocol2.java
index 644914a089bdaa2bb2368f8d2c9bf940b5987cc0..0f2757df786b7dc72c0e8d4e59528aa79df4e6ad 100644
--- a/core/src/main/java/hudson/cli/CliProtocol2.java
+++ b/core/src/main/java/hudson/cli/CliProtocol2.java
@@ -37,6 +37,7 @@ public class CliProtocol2 extends CliProtocol {
* @deprecated as of 1.559
* Use {@link #Handler2(NioChannelHub, Socket)}
*/
+ @Deprecated
public Handler2(Socket socket) {
super(socket);
}
diff --git a/core/src/main/java/hudson/cli/CliTransportAuthenticator.java b/core/src/main/java/hudson/cli/CliTransportAuthenticator.java
index 0db90cce10c8a57cb7bb9b56b4d63060cf625afd..f561a50eddf21c362f130fdf1c445f8003ceaa62 100644
--- a/core/src/main/java/hudson/cli/CliTransportAuthenticator.java
+++ b/core/src/main/java/hudson/cli/CliTransportAuthenticator.java
@@ -4,7 +4,6 @@ import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.remoting.Channel;
import hudson.security.SecurityRealm;
-import jenkins.model.Jenkins;
/**
* Perform {@link SecurityRealm} independent authentication.
diff --git a/core/src/main/java/hudson/cli/CloneableCLICommand.java b/core/src/main/java/hudson/cli/CloneableCLICommand.java
index 7fb3b96dd35a3841c491e4138f2cfc61c79c798c..f6a4c60b02c12a17b77481735f1d3b421adfe800 100644
--- a/core/src/main/java/hudson/cli/CloneableCLICommand.java
+++ b/core/src/main/java/hudson/cli/CloneableCLICommand.java
@@ -26,7 +26,7 @@ package hudson.cli;
/**
* {@link Cloneable} {@link CLICommand}.
*
- * Uses {@link #clone()} instead of "new" to create a copy for exection.
+ * Uses {@link #clone()} instead of "new" to create a copy for execution.
*
* @author Kohsuke Kawaguchi
*/
diff --git a/core/src/main/java/hudson/cli/CommandDuringBuild.java b/core/src/main/java/hudson/cli/CommandDuringBuild.java
index dfa65f487ae5de60b985039fb204575a8d3b18c0..3834238a41fafb0b9f951e30880bca5144709ccc 100644
--- a/core/src/main/java/hudson/cli/CommandDuringBuild.java
+++ b/core/src/main/java/hudson/cli/CommandDuringBuild.java
@@ -27,7 +27,6 @@ package hudson.cli;
import jenkins.model.Jenkins;
import hudson.model.Job;
import hudson.model.Run;
-import hudson.remoting.Callable;
import jenkins.security.MasterToSlaveCallable;
import org.kohsuke.args4j.CmdLineException;
diff --git a/core/src/main/java/hudson/cli/DeleteJobCommand.java b/core/src/main/java/hudson/cli/DeleteJobCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..361d889bccdd376eae9a0e4cc8db0215bcb74b6f
--- /dev/null
+++ b/core/src/main/java/hudson/cli/DeleteJobCommand.java
@@ -0,0 +1,101 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2015 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.AbstractItem;
+import jenkins.model.Jenkins;
+import org.kohsuke.args4j.Argument;
+
+import java.util.List;
+import java.util.HashSet;
+import java.util.logging.Logger;
+
+/**
+ * @author pjanouse
+ * @since TODO
+ */
+@Extension
+public class DeleteJobCommand extends CLICommand {
+
+ @Argument(usage="Name of the job(s) to delete", required=true, multiValued=true)
+ private List jobs;
+
+ private static final Logger LOGGER = Logger.getLogger(DeleteJobCommand.class.getName());
+
+ @Override
+ public String getShortDescription() {
+
+ return Messages.DeleteJobCommand_ShortDescription();
+ }
+
+ @Override
+ protected int run() throws Exception {
+
+ boolean errorOccurred = false;
+ final Jenkins jenkins = Jenkins.getInstance();
+
+ if (jenkins == null) {
+ stderr.println("The Jenkins instance has not been started, or was already shut down!");
+ return -1;
+ }
+
+ final HashSet hs = new HashSet();
+ hs.addAll(jobs);
+
+ for (String job_s: hs) {
+ AbstractItem job = null;
+
+ try {
+ job = (AbstractItem) jenkins.getItemByFullName(job_s);
+
+ if(job == null) {
+ stderr.format("No such job '%s'\n", job_s);
+ errorOccurred = true;
+ continue;
+ }
+
+ try {
+ job.checkPermission(AbstractItem.DELETE);
+ } catch (Exception e) {
+ stderr.println(e.getMessage());
+ errorOccurred = true;
+ continue;
+ }
+
+ job.delete();
+ } catch (Exception e) {
+ final String errorMsg = String.format("Unexpected exception occurred during deletion of job '%s': %s",
+ job == null ? "(null)" : job.getFullName(),
+ e.getMessage());
+ stderr.println(errorMsg);
+ LOGGER.warning(errorMsg);
+ errorOccurred = true;
+ //noinspection UnnecessaryContinue
+ continue;
+ }
+ }
+ return errorOccurred ? -1 : 0;
+ }
+}
diff --git a/core/src/main/java/hudson/cli/DeleteNodeCommand.java b/core/src/main/java/hudson/cli/DeleteNodeCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d48e2b78f14cb5e618321d50858ee395c080e11
--- /dev/null
+++ b/core/src/main/java/hudson/cli/DeleteNodeCommand.java
@@ -0,0 +1,99 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2015 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.Node;
+import org.acegisecurity.AccessDeniedException;
+import jenkins.model.Jenkins;
+import org.kohsuke.args4j.Argument;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * @author pjanouse
+ * @since TODO
+ */
+@Extension
+public class DeleteNodeCommand extends CLICommand {
+
+ @Argument(usage="Nodes name to delete", required=true, multiValued=true)
+ private List nodes;
+
+ private static final Logger LOGGER = Logger.getLogger(DeleteNodeCommand.class.getName());
+
+ @Override
+ public String getShortDescription() {
+
+ return Messages.DeleteNodeCommand_ShortDescription();
+ }
+
+ @Override
+ protected int run() throws Exception {
+
+ boolean errorOccurred = false;
+ final Jenkins jenkins = Jenkins.getInstance();
+
+ if (jenkins == null) {
+ stderr.println("The Jenkins instance has not been started, or was already shut down!");
+ return -1;
+ }
+
+ final HashSet hs = new HashSet();
+ hs.addAll(nodes);
+
+ for (String node_s : hs) {
+ Node node = null;
+
+ try {
+ node = jenkins.getNode(node_s);
+
+ if(node == null) {
+ stderr.format("No such node '%s'\n", node_s);
+ errorOccurred = true;
+ continue;
+ }
+
+ node.toComputer().doDoDelete();
+ } catch (AccessDeniedException e) {
+ stderr.println(e.getMessage());
+ errorOccurred = true;
+ //noinspection UnnecessaryContinue
+ continue;
+ } catch (Exception e) {
+ final String errorMsg = String.format("Unexpected exception occurred during deletion of node '%s': %s",
+ node == null ? "(null)" : node.toComputer().getName(),
+ e.getMessage());
+ stderr.println(errorMsg);
+ LOGGER.warning(errorMsg);
+ errorOccurred = true;
+ //noinspection UnnecessaryContinue
+ continue;
+ }
+ }
+ return errorOccurred ? -1 : 0;
+ }
+}
diff --git a/core/src/main/java/hudson/cli/DeleteViewCommand.java b/core/src/main/java/hudson/cli/DeleteViewCommand.java
index 73414701139bab47cc931edb63df4b12e4e7b4c7..4c86edeb70221ed239d4d973371430ce792cccbe 100644
--- a/core/src/main/java/hudson/cli/DeleteViewCommand.java
+++ b/core/src/main/java/hudson/cli/DeleteViewCommand.java
@@ -1,7 +1,7 @@
/*
* The MIT License
*
- * Copyright (c) 2013 Red Hat, Inc.
+ * Copyright (c) 2013-5 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
@@ -24,20 +24,27 @@
package hudson.cli;
import hudson.Extension;
+import hudson.cli.handlers.ViewOptionHandler;
import hudson.model.ViewGroup;
import hudson.model.View;
import org.kohsuke.args4j.Argument;
+import java.util.HashSet;
+import java.util.List;
+import java.util.logging.Logger;
+
/**
- * @author ogondza
+ * @author ogondza, pjanouse
* @since 1.538
*/
@Extension
public class DeleteViewCommand extends CLICommand {
- @Argument(usage="Name of the view to delete", required=true)
- private View view;
+ @Argument(usage="View names to delete", required=true, multiValued=true)
+ private List views;
+
+ private static final Logger LOGGER = Logger.getLogger(DeleteViewCommand.class.getName());
@Override
public String getShortDescription() {
@@ -48,20 +55,54 @@ public class DeleteViewCommand extends CLICommand {
@Override
protected int run() throws Exception {
- view.checkPermission(View.DELETE);
+ boolean errorOccurred = false;
- final ViewGroup group = view.getOwner();
- if (!group.canDelete(view)) {
+ // Remove duplicates
+ final HashSet hs = new HashSet();
+ hs.addAll(views);
- stderr.format("%s does not allow to delete '%s' view\n",
- group.getDisplayName(),
- view.getViewName()
- );
- return -1;
- }
+ ViewOptionHandler voh = new ViewOptionHandler(null, null, null);
- group.deleteView(view);;
+ for(String view_s : hs) {
+ View view = null;
- return 0;
+ try {
+ try {
+ view = voh.getView(view_s);
+ if (view == null) {
+ stderr.println("user is missing the View/Read permission");
+ errorOccurred = true;
+ continue;
+ }
+ view.checkPermission(View.DELETE);
+ } catch (Exception e) {
+ stderr.println(e.getMessage());
+ errorOccurred = true;
+ continue;
+ }
+
+ ViewGroup group = view.getOwner();
+ if (!group.canDelete(view)) {
+ stderr.format("%s does not allow to delete '%s' view\n",
+ group.getDisplayName(),
+ view.getViewName()
+ );
+ errorOccurred = true;
+ continue;
+ }
+
+ group.deleteView(view);
+ } catch (Exception e) {
+ final String errorMsg = String.format("Unexpected exception occurred during deletion of view '%s': %s",
+ view == null ? "(null)" : view.getViewName(),
+ e.getMessage());
+ stderr.println(errorMsg);
+ LOGGER.warning(errorMsg);
+ errorOccurred = true;
+ //noinspection UnnecessaryContinue
+ continue;
+ }
+ }
+ return errorOccurred ? -1 : 0;
}
}
diff --git a/core/src/main/java/hudson/cli/GroovyCommand.java b/core/src/main/java/hudson/cli/GroovyCommand.java
index 7866e2f7228d6fde0ea480631c52676da97702fc..a77f41fd92057bf0c9561759646c3abe8e080881 100644
--- a/core/src/main/java/hudson/cli/GroovyCommand.java
+++ b/core/src/main/java/hudson/cli/GroovyCommand.java
@@ -30,23 +30,15 @@ import hudson.model.AbstractProject;
import jenkins.model.Jenkins;
import hudson.model.Item;
import hudson.model.Run;
-import hudson.remoting.Callable;
-import hudson.AbortException;
import hudson.Extension;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.FileUtils;
import java.io.IOException;
-import java.io.Serializable;
-import java.io.File;
-import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.net.URL;
-import java.net.MalformedURLException;
/**
* Executes the specified groovy script.
diff --git a/core/src/main/java/hudson/cli/OnlineNodeCommand.java b/core/src/main/java/hudson/cli/OnlineNodeCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ed90c746165a26ffacb448327b6b440b72374a5
--- /dev/null
+++ b/core/src/main/java/hudson/cli/OnlineNodeCommand.java
@@ -0,0 +1,85 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2015 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.Computer;
+import jenkins.model.Jenkins;
+
+import org.acegisecurity.AccessDeniedException;
+import org.kohsuke.args4j.Argument;
+
+import java.util.logging.Logger;
+
+/**
+ * @author pjanouse
+ * @since TODO
+ */
+@Extension
+public class OnlineNodeCommand extends CLICommand {
+
+ @Argument(metaVar="NAME", usage="Slave name, or empty string for master")
+ public String computerName;
+
+ private static final Logger LOGGER = Logger.getLogger(OnlineNodeCommand.class.getName());
+
+ @Override
+ public String getShortDescription() {
+
+ return Messages.OnlineNodeCommand_ShortDescription();
+ }
+
+ @Override
+ protected int run() throws Exception {
+
+ boolean errorOccurred = false;
+
+ final Jenkins jenkins = Jenkins.getInstance();
+
+ Computer computer = jenkins.getComputer(computerName);
+
+ if (computer == null) {
+ stderr.println(hudson.model.Messages.Computer_NoSuchSlaveExists(computerName, null));
+ errorOccurred = true;
+ } else {
+ try {
+ computer.cliOnline();
+ } catch (AccessDeniedException e) {
+ stderr.println(e.getMessage());
+ errorOccurred = true;
+ } catch (Exception e) {
+ final String errorMsg = String.format("Unexpected exception occurred during performing online operation on node '%s': %s",
+ computer == null ? "(null)" : computer.getName(),
+ e.getMessage());
+ stderr.println(errorMsg);
+ LOGGER.warning(errorMsg);
+ errorOccurred = true;
+ }
+ }
+
+ return errorOccurred ? 1 : 0;
+ }
+
+}
diff --git a/core/src/main/java/hudson/cli/ReloadJobCommand.java b/core/src/main/java/hudson/cli/ReloadJobCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..a8905b79e0c17df9fa857fa226332bf201b04765
--- /dev/null
+++ b/core/src/main/java/hudson/cli/ReloadJobCommand.java
@@ -0,0 +1,117 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2015 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.AbstractItem;
+import hudson.model.AbstractProject;
+import hudson.model.Item;
+
+import jenkins.model.Jenkins;
+import org.kohsuke.args4j.Argument;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author pjanouse
+ * @since TODO
+ */
+@Extension
+public class ReloadJobCommand extends CLICommand {
+
+ @Argument(usage="Name of the job(s) to reload", required=true, multiValued=true)
+ private List jobs;
+
+ private static final Logger LOGGER = Logger.getLogger(ReloadJobCommand.class.getName());
+
+ @Override
+ public String getShortDescription() {
+
+ return Messages.ReloadJobCommand_ShortDescription();
+ }
+
+ @Override
+ protected int run() throws Exception {
+
+ boolean errorOccurred = false;
+ final Jenkins jenkins = Jenkins.getInstance();
+
+ if (jenkins == null) {
+ stderr.println("The Jenkins instance has not been started, or was already shut down!");
+ return -1;
+ }
+
+ final HashSet hs = new HashSet();
+ hs.addAll(jobs);
+
+ for (String job_s: hs) {
+ AbstractItem job = null;
+
+ try {
+ // TODO: JENKINS-30786
+ Item item = jenkins.getItemByFullName(job_s);
+ if (item instanceof AbstractItem) {
+ job = (AbstractItem) item;
+ } else if (item != null) {
+ LOGGER.log(Level.WARNING, "Unsupported item type: {0}", item.getClass().getName());
+ }
+
+ if(job == null) {
+ // TODO: JENKINS-30785
+ AbstractProject project = AbstractProject.findNearest(job_s);
+ if(project == null) {
+ stderr.format("No such job \u2018%s\u2019 exists.\n", job_s);
+ } else {
+ stderr.format("No such job \u2018%s\u2019 exists. Perhaps you meant \u2018%s\u2019?", job_s, project.getFullName());
+ }
+ errorOccurred = true;
+ continue;
+ }
+
+ try {
+ job.checkPermission(AbstractItem.CONFIGURE);
+ } catch (Exception e) {
+ stderr.println(e.getMessage());
+ errorOccurred = true;
+ continue;
+ }
+
+ job.doReload();
+ } catch (Exception e) {
+ final String errorMsg = String.format("Unexpected exception occurred during reloading of job '%s': %s",
+ job == null ? "(null)" : job.getFullName(),
+ e.getMessage());
+ stderr.println(errorMsg);
+ LOGGER.warning(errorMsg);
+ errorOccurred = true;
+ //noinspection UnnecessaryContinue
+ continue;
+ }
+ }
+ return errorOccurred ? 1 : 0;
+ }
+}
diff --git a/core/src/main/java/hudson/cli/SetBuildDescriptionCommand.java b/core/src/main/java/hudson/cli/SetBuildDescriptionCommand.java
index 0aeedb4a37303b7875d1b5178181d79eccabc96c..c7767a796e7d81c48b989b629a89ca33b57dd607 100644
--- a/core/src/main/java/hudson/cli/SetBuildDescriptionCommand.java
+++ b/core/src/main/java/hudson/cli/SetBuildDescriptionCommand.java
@@ -3,9 +3,7 @@ package hudson.cli;
import hudson.Extension;
import hudson.model.AbstractProject;
import hudson.model.Run;
-import hudson.remoting.Callable;
-import java.io.IOException;
import java.io.Serializable;
import org.apache.commons.io.IOUtils;
diff --git a/core/src/main/java/hudson/cli/SetBuildResultCommand.java b/core/src/main/java/hudson/cli/SetBuildResultCommand.java
index 7666d590a5f7147f33b08f7b15b4a08235e3800a..82756472c7b3ac09f7b6276e472512d4ad8df314 100644
--- a/core/src/main/java/hudson/cli/SetBuildResultCommand.java
+++ b/core/src/main/java/hudson/cli/SetBuildResultCommand.java
@@ -25,7 +25,6 @@
package hudson.cli;
import hudson.Extension;
-import hudson.model.Item;
import hudson.model.Result;
import hudson.model.Run;
import org.kohsuke.args4j.Argument;
diff --git a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java
index fe7ed1f690b8fa4aa486a2cc0ae9cd689fa91ac4..a96daf60f9ca8c510232bb3f045735376350e203 100644
--- a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java
+++ b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java
@@ -33,7 +33,6 @@ import hudson.model.Hudson;
import jenkins.ExtensionComponentSet;
import jenkins.ExtensionRefreshException;
import jenkins.model.Jenkins;
-import hudson.remoting.Channel;
import hudson.security.CliAuthenticator;
import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContext;
diff --git a/core/src/main/java/hudson/cli/declarative/MethodBinder.java b/core/src/main/java/hudson/cli/declarative/MethodBinder.java
index 6b137727588a3bbee7a4befcaadb50f3e71a1a73..f9b8885e8ed306790feaaae77197ee2242d8b19a 100644
--- a/core/src/main/java/hudson/cli/declarative/MethodBinder.java
+++ b/core/src/main/java/hudson/cli/declarative/MethodBinder.java
@@ -30,10 +30,12 @@ import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
+import org.kohsuke.args4j.spi.FieldSetter;
import org.kohsuke.args4j.spi.Setter;
import org.kohsuke.args4j.spi.OptionHandler;
import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
@@ -79,6 +81,16 @@ class MethodBinder {
public boolean isMultiValued() {
return false;
}
+
+ @Override
+ public FieldSetter asFieldSetter() {
+ return null;
+ }
+
+ @Override
+ public AnnotatedElement asAnnotatedElement() {
+ return p;
+ }
};
Option option = p.annotation(Option.class);
if (option!=null) {
@@ -148,5 +160,10 @@ class MethodBinder {
public Class extends Annotation> annotationType() {
return base.annotationType();
}
+
+ @Override
+ public boolean hidden() {
+ return base.hidden();
+ }
}
}
diff --git a/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
index 387621ae56b570e70731cbf7f6a6f6a98e755d41..77d086a1d1776141cc5932bb5a41cda0d902d0f5 100644
--- a/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
+++ b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java
@@ -38,6 +38,8 @@ import org.kohsuke.args4j.spi.OptionHandler;
import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;
+import javax.annotation.CheckForNull;
+
/**
* Refers to {@link View} by its name.
*
@@ -73,10 +75,27 @@ public class ViewOptionHandler extends OptionHandler {
return 1;
}
- private View getView(String name) throws CmdLineException {
+ /**
+ *
+ * Gets a view by its name
+ * Note: Personal user's views aren't supported now.
+ *
+ * @param name A view name
+ * @return The {@link View} instance. Null if {@link Jenkins#getInstance()} returns null
+ * sor user doesn't have a READ permission.
+ * @throws CmdLineException
+ * If view isn't found or an un-expected error occurred
+ * @since TODO
+ */
+ @CheckForNull
+ public View getView(final String name) throws CmdLineException {
- View view = null;
ViewGroup group = Jenkins.getInstance();
+ View view = null;
+
+ if (group == null)
+ throw new CmdLineException(owner,
+ "The Jenkins instance has not been started, or was already shut down!");
final StringTokenizer tok = new StringTokenizer(name, "/");
while(tok.hasMoreTokens()) {
@@ -84,18 +103,23 @@ public class ViewOptionHandler extends OptionHandler {
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 == null)
+ throw new CmdLineException(owner, String.format(
+ "No view named %s inside view %s",
+ viewName, group.getDisplayName()
+ ));
+
+ try {
+ view.checkPermission(View.READ);
+ } catch (Exception e) {
+ throw new CmdLineException(owner, e.getMessage());
+ }
if (view instanceof ViewGroup) {
group = (ViewGroup) view;
} else if (tok.hasMoreTokens()) {
throw new CmdLineException(
- owner, view.getViewName() + " view can not contain views"
+ owner, view.getViewName() + " view can not contain views"
);
}
}
diff --git a/core/src/main/java/hudson/console/AnnotatedLargeText.java b/core/src/main/java/hudson/console/AnnotatedLargeText.java
index 89e307bf22df8a46ad4e97dc0acc09bda283eedf..4e8c40658af523124c83815290ea0efaa8d2f43d 100644
--- a/core/src/main/java/hudson/console/AnnotatedLargeText.java
+++ b/core/src/main/java/hudson/console/AnnotatedLargeText.java
@@ -147,7 +147,7 @@ public class AnnotatedLargeText extends LargeText {
/**
* Strips annotations using a {@link PlainTextConsoleOutputStream}.
- * @inheritDoc
+ * {@inheritDoc}
*/
@Override
public long writeLogTo(long start, OutputStream out) throws IOException {
@@ -156,7 +156,6 @@ public class AnnotatedLargeText extends LargeText {
/**
* Calls {@link LargeText#writeLogTo(long, OutputStream)} without stripping annotations as {@link #writeLogTo(long, OutputStream)} would.
- * @inheritDoc
* @since 1.577
*/
public long writeRawLogTo(long start, OutputStream out) throws IOException {
diff --git a/core/src/main/java/hudson/console/ConsoleAnnotationDescriptor.java b/core/src/main/java/hudson/console/ConsoleAnnotationDescriptor.java
index b4e260b6af257a5f16fb62611253ce7c0ce7ec31..d5ac5081364fe5f7220d2435f65940aa2b436c2e 100644
--- a/core/src/main/java/hudson/console/ConsoleAnnotationDescriptor.java
+++ b/core/src/main/java/hudson/console/ConsoleAnnotationDescriptor.java
@@ -55,7 +55,10 @@ public abstract class ConsoleAnnotationDescriptor extends Descriptor
+ * Even though this method is not marked 'abstract', this is the method that must be overridden
+ * by extensions.
+ */
+ public OutputStream decorateLogger(Run build, OutputStream logger) throws IOException, InterruptedException {
+ // this implementation is backward compatibility thunk in case subtypes only override the
+ // old signature (AbstractBuild,OutputStream)
+
+ if (build instanceof AbstractBuild) {
+ // maybe the plugin implements the old signature.
+ return decorateLogger((AbstractBuild) build, logger);
+ } else {
+ // this ConsoleLogFilter can only decorate AbstractBuild, so just pass through
+ return logger;
+ }
+ }
+
+ /**
+ * Called to decorate logger for master/slave communication.
+ *
+ * @param computer
+ * Slave computer for which the logger is getting decorated. Useful to do
+ * contextual decoration.
+ * @since 1.632
+ */
+ public OutputStream decorateLogger(@Nonnull Computer computer, OutputStream logger) throws IOException, InterruptedException {
+ return logger; // by default no-op
+ }
/**
* All the registered {@link ConsoleLogFilter}s.
diff --git a/core/src/main/java/hudson/console/PlainTextConsoleOutputStream.java b/core/src/main/java/hudson/console/PlainTextConsoleOutputStream.java
index f1afa2fad5ad91447f5588d35edaede9defcab40..ee02d177d48c4fbd7d0aadc9e422b02aa3d02421 100644
--- a/core/src/main/java/hudson/console/PlainTextConsoleOutputStream.java
+++ b/core/src/main/java/hudson/console/PlainTextConsoleOutputStream.java
@@ -89,5 +89,5 @@ public class PlainTextConsoleOutputStream extends LineTransformationOutputStream
}
- private static final Logger LOGGER = Logger.getLogger(ConsoleAnnotationOutputStream.class.getName());
+ private static final Logger LOGGER = Logger.getLogger(PlainTextConsoleOutputStream.class.getName());
}
diff --git a/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java b/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java
index bb4e8e50401e9292932cf948995d4bb21029ebcb..3b7c7d653f4440b34ddad1f5cad4de7bc5c2c2c5 100644
--- a/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java
+++ b/core/src/main/java/hudson/diagnosis/HudsonHomeDiskUsageMonitor.java
@@ -24,7 +24,6 @@
package hudson.diagnosis;
import hudson.model.AdministrativeMonitor;
-import jenkins.model.Jenkins;
import hudson.model.AbstractModelObject;
import hudson.Extension;
import hudson.ExtensionPoint;
diff --git a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
index f2e1b1eaa14c330bd89418f998bd69f3b7dc0e37..b955cd21783419bec1731a7a2a4d5bd47cfd0911 100644
--- a/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
+++ b/core/src/main/java/hudson/diagnosis/OldDataMonitor.java
@@ -36,6 +36,7 @@ import hudson.model.Saveable;
import hudson.model.listeners.ItemListener;
import hudson.model.listeners.RunListener;
import hudson.model.listeners.SaveableListener;
+import hudson.security.ACL;
import hudson.util.RobustReflectionConverter;
import hudson.util.VersionNumber;
import java.io.IOException;
@@ -46,10 +47,16 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import jenkins.model.Jenkins;
+import org.acegisecurity.context.SecurityContext;
+import org.acegisecurity.context.SecurityContextHolder;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
@@ -67,7 +74,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST;
public class OldDataMonitor extends AdministrativeMonitor {
private static final Logger LOGGER = Logger.getLogger(OldDataMonitor.class.getName());
- private HashMap data = new HashMap();
+ private ConcurrentMap data = new ConcurrentHashMap();
static OldDataMonitor get(Jenkins j) {
return (OldDataMonitor) j.getAdministrativeMonitor("OldData");
@@ -87,12 +94,8 @@ public class OldDataMonitor extends AdministrativeMonitor {
}
public Map getData() {
- Map _data;
- synchronized (this) {
- _data = new HashMap(this.data);
- }
Map r = new HashMap();
- for (Map.Entry entry : _data.entrySet()) {
+ for (Map.Entry entry : this.data.entrySet()) {
Saveable s = entry.getKey().get();
if (s != null) {
r.put(s, entry.getValue());
@@ -102,12 +105,20 @@ public class OldDataMonitor extends AdministrativeMonitor {
}
private static void remove(Saveable obj, boolean isDelete) {
- OldDataMonitor odm = get(Jenkins.getInstance());
- synchronized (odm) {
- odm.data.remove(referTo(obj));
- if (isDelete && obj instanceof Job,?>)
- for (Run r : ((Job,?>)obj).getBuilds())
- odm.data.remove(referTo(r));
+ Jenkins j = Jenkins.getInstance();
+ if (j != null) {
+ OldDataMonitor odm = get(j);
+ SecurityContext oldContext = ACL.impersonate(ACL.SYSTEM);
+ try {
+ odm.data.remove(referTo(obj));
+ if (isDelete && obj instanceof Job, ?>) {
+ for (Run r : ((Job, ?>) obj).getBuilds()) {
+ odm.data.remove(referTo(r));
+ }
+ }
+ } finally {
+ SecurityContextHolder.setContext(oldContext);
+ }
}
}
@@ -146,15 +157,18 @@ public class OldDataMonitor extends AdministrativeMonitor {
*/
public static void report(Saveable obj, String version) {
OldDataMonitor odm = get(Jenkins.getInstance());
- synchronized (odm) {
- try {
- SaveableReference ref = referTo(obj);
+ try {
+ SaveableReference ref = referTo(obj);
+ while (true) {
VersionRange vr = odm.data.get(ref);
- if (vr != null) vr.add(version);
- else odm.data.put(ref, new VersionRange(version, null));
- } catch (IllegalArgumentException ex) {
- LOGGER.log(Level.WARNING, "Bad parameter given to OldDataMonitor", ex);
+ if (vr != null && odm.data.replace(ref, vr, new VersionRange(vr, version, null))) {
+ break;
+ } else if (odm.data.putIfAbsent(ref, new VersionRange(null, version, null)) == null) {
+ break;
+ }
}
+ } catch (IllegalArgumentException ex) {
+ LOGGER.log(Level.WARNING, "Bad parameter given to OldDataMonitor", ex);
}
}
@@ -201,32 +215,49 @@ public class OldDataMonitor extends AdministrativeMonitor {
return;
}
OldDataMonitor odm = get(j);
- synchronized (odm) {
- SaveableReference ref = referTo(obj);
+ SaveableReference ref = referTo(obj);
+ while (true) {
VersionRange vr = odm.data.get(ref);
- if (vr != null) vr.extra = buf.toString();
- else odm.data.put(ref, new VersionRange(null, buf.toString()));
+ if (vr != null && odm.data.replace(ref, vr, new VersionRange(vr, null, buf.toString()))) {
+ break;
+ } else if (odm.data.putIfAbsent(ref, new VersionRange(null, null, buf.toString())) == null) {
+ break;
+ }
}
}
public static class VersionRange {
private static VersionNumber currentVersion = Jenkins.getVersion();
- VersionNumber min, max;
- boolean single = true;
- public String extra;
-
- public VersionRange(String version, String extra) {
- min = max = version != null ? new VersionNumber(version) : null;
- this.extra = extra;
- }
-
- public void add(String version) {
- VersionNumber ver = new VersionNumber(version);
- if (min==null) { min = max = ver; }
- else {
- if (ver.isOlderThan(min)) { min = ver; single = false; }
- if (ver.isNewerThan(max)) { max = ver; single = false; }
+ final VersionNumber min;
+ final VersionNumber max;
+ final boolean single;
+ final public String extra;
+
+ public VersionRange(VersionRange previous, String version, String extra) {
+ if (previous == null) {
+ min = max = version != null ? new VersionNumber(version) : null;
+ this.single = true;
+ this.extra = extra;
+ } else if (version == null) {
+ min = previous.min;
+ max = previous.max;
+ single = previous.single;
+ this.extra = extra;
+ } else {
+ VersionNumber ver = new VersionNumber(version);
+ if (previous.min == null || ver.isOlderThan(previous.min)) {
+ this.min = ver;
+ } else {
+ this.min = previous.min;
+ }
+ if (previous.max == null || ver.isNewerThan(previous.max)) {
+ this.max = ver;
+ } else {
+ this.max = previous.max;
+ }
+ this.single = this.max.isNewerThan(this.min);
+ this.extra = extra;
}
}
@@ -245,15 +276,20 @@ public class OldDataMonitor extends AdministrativeMonitor {
|| (currentVersion.digit(0) == min.digit(0)
&& currentVersion.digit(1) - min.digit(1) >= threshold));
}
+
}
/**
* Sorted list of unique max-versions in the data set. For select list in jelly.
*/
- public synchronized Iterator getVersionList() {
+ @Restricted(NoExternalUse.class)
+ public Iterator getVersionList() {
TreeSet set = new TreeSet();
- for (VersionRange vr : data.values())
- if (vr.max!=null) set.add(vr.max);
+ for (VersionRange vr : data.values()) {
+ if (vr.max != null) {
+ set.add(vr.max);
+ }
+ }
return set.iterator();
}
@@ -279,7 +315,7 @@ public class OldDataMonitor extends AdministrativeMonitor {
final String thruVerParam = req.getParameter("thruVer");
final VersionNumber thruVer = thruVerParam.equals("all") ? null : new VersionNumber(thruVerParam);
- saveAndRemoveEntries( new Predicate>() {
+ saveAndRemoveEntries(new Predicate>() {
@Override
public boolean apply(Map.Entry entry) {
VersionNumber version = entry.getValue().max;
@@ -318,13 +354,8 @@ public class OldDataMonitor extends AdministrativeMonitor {
* does occur: just means the user will be prompted to discard less than they should have been (and
* would see the warning again after next restart).
*/
- Map localCopy = null;
- synchronized (this) {
- localCopy = new HashMap(data);
- }
-
List removed = new ArrayList();
- for (Map.Entry entry : localCopy.entrySet()) {
+ for (Map.Entry entry : data.entrySet()) {
if (matchingPredicate.apply(entry)) {
Saveable s = entry.getKey().get();
if (s != null) {
@@ -338,9 +369,7 @@ public class OldDataMonitor extends AdministrativeMonitor {
}
}
- synchronized (this) {
- data.keySet().removeAll(removed);
- }
+ data.keySet().removeAll(removed);
}
public HttpResponse doIndex(StaplerResponse rsp) throws IOException {
@@ -355,10 +384,12 @@ public class OldDataMonitor extends AdministrativeMonitor {
private static SaveableReference referTo(Saveable s) {
if (s instanceof Run) {
- return new RunSaveableReference((Run) s);
- } else {
- return new SimpleSaveableReference(s);
+ Job parent = ((Run) s).getParent();
+ if (Jenkins.getInstance().getItemByFullName(parent.getFullName()) == parent) {
+ return new RunSaveableReference((Run) s);
+ }
}
+ return new SimpleSaveableReference(s);
}
private static final class SimpleSaveableReference implements SaveableReference {
diff --git a/core/src/main/java/hudson/init/Terminator.java b/core/src/main/java/hudson/init/Terminator.java
index 72a2c9025b6f4e1e1d2417bcb0f9e998c57aedb8..7cea40031c9052d93fe64291e72c0f38ac5ec6a4 100644
--- a/core/src/main/java/hudson/init/Terminator.java
+++ b/core/src/main/java/hudson/init/Terminator.java
@@ -1,7 +1,6 @@
package hudson.init;
import org.jvnet.hudson.annotation_indexer.Indexed;
-import org.jvnet.hudson.reactor.Task;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
diff --git a/core/src/main/java/hudson/init/impl/GroovyInitScript.java b/core/src/main/java/hudson/init/impl/GroovyInitScript.java
index 621d52ce635f59c8d09bc4f198a3a24e4b90b88f..20cd1a481c006f82f2f69557f5a7fc1bcf72ccb0 100644
--- a/core/src/main/java/hudson/init/impl/GroovyInitScript.java
+++ b/core/src/main/java/hudson/init/impl/GroovyInitScript.java
@@ -27,7 +27,6 @@ import hudson.init.Initializer;
import jenkins.model.Jenkins;
import jenkins.util.groovy.GroovyHookScript;
-import java.io.IOException;
import static hudson.init.InitMilestone.*;
@@ -39,6 +38,6 @@ import static hudson.init.InitMilestone.*;
public class GroovyInitScript {
@Initializer(after=JOB_LOADED)
public static void init(Jenkins j) {
- new GroovyHookScript("init").run();
+ new GroovyHookScript("init", j.servletContext, j.getRootDir(), j.getPluginManager().uberClassLoader).run();
}
}
diff --git a/core/src/main/java/hudson/lifecycle/UnixLifecycle.java b/core/src/main/java/hudson/lifecycle/UnixLifecycle.java
index 6a558594532ca65ad95372afecb3cd692c3ea835..0e489277e50ad2d9d486e1ca5ac2212d3673f04d 100644
--- a/core/src/main/java/hudson/lifecycle/UnixLifecycle.java
+++ b/core/src/main/java/hudson/lifecycle/UnixLifecycle.java
@@ -24,7 +24,6 @@
package hudson.lifecycle;
import com.sun.akuma.JavaVMArguments;
-import com.sun.akuma.Daemon;
import com.sun.jna.Native;
import com.sun.jna.StringArray;
diff --git a/core/src/main/java/hudson/logging/LogRecorder.java b/core/src/main/java/hudson/logging/LogRecorder.java
index 81c9486e3fa8fd0f6ce3d4b9c4ec111faaf5e06b..17ec36ee169ce16bfb67fdebc6ebcc6bd4b51fd5 100644
--- a/core/src/main/java/hudson/logging/LogRecorder.java
+++ b/core/src/main/java/hudson/logging/LogRecorder.java
@@ -33,7 +33,6 @@ import hudson.model.*;
import hudson.util.HttpResponses;
import jenkins.model.Jenkins;
import hudson.model.listeners.SaveableListener;
-import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.VirtualChannel;
import hudson.slaves.ComputerListener;
diff --git a/core/src/main/java/hudson/logging/LogRecorderManager.java b/core/src/main/java/hudson/logging/LogRecorderManager.java
index d16ece6c2babdcfdf0fd9f95ddf25ce1098c0b06..e35a098d4bac0af2bc594e1d40f06c777d194ad4 100644
--- a/core/src/main/java/hudson/logging/LogRecorderManager.java
+++ b/core/src/main/java/hudson/logging/LogRecorderManager.java
@@ -40,6 +40,7 @@ import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpRedirect;
+import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.servlet.ServletException;
import java.io.File;
@@ -106,6 +107,7 @@ public class LogRecorderManager extends AbstractModelObject implements ModelObje
/**
* Creates a new log recorder.
*/
+ @RequirePOST
public HttpResponse doNewLogRecorder(@QueryParameter String name) {
Jenkins.checkGoodName(name);
diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java
index d6dc13703f0345bbe310d37793e78450e07676b4..e9c4626706c6cb564cb4f2b6e260bf8ef541f709 100644
--- a/core/src/main/java/hudson/model/AbstractBuild.java
+++ b/core/src/main/java/hudson/model/AbstractBuild.java
@@ -30,8 +30,6 @@ import hudson.EnvVars;
import hudson.FilePath;
import hudson.Functions;
import hudson.Launcher;
-import hudson.console.AnnotatedLargeText;
-import hudson.console.ExpandableDetailsNote;
import hudson.console.ModelHyperlinkNote;
import hudson.model.Fingerprint.BuildPtr;
import hudson.model.Fingerprint.RangeSet;
@@ -70,7 +68,6 @@ import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
-import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.AbstractSet;
import java.util.ArrayList;
@@ -413,6 +410,7 @@ public abstract class AbstractBuild
,R extends Abs
* @deprecated as of 1.467
* Please use {@link hudson.model.Run.RunExecution}
*/
+ @Deprecated
public abstract class AbstractRunner extends AbstractBuildExecution {
}
@@ -691,6 +689,7 @@ public abstract class AbstractBuild
,R extends Abs
* @deprecated as of 1.356
* Use {@link #performAllBuildSteps(BuildListener, Map, boolean)}
*/
+ @Deprecated
protected final void performAllBuildStep(BuildListener listener, Map,? extends BuildStep> buildSteps, boolean phase) throws InterruptedException, IOException {
performAllBuildSteps(listener,buildSteps.values(),phase);
}
@@ -703,6 +702,7 @@ public abstract class AbstractBuild
,R extends Abs
* @deprecated as of 1.356
* Use {@link #performAllBuildSteps(BuildListener, Iterable, boolean)}
*/
+ @Deprecated
protected final void performAllBuildStep(BuildListener listener, Iterable extends BuildStep> buildSteps, boolean phase) throws InterruptedException, IOException {
performAllBuildSteps(listener,buildSteps,phase);
}
@@ -712,6 +712,8 @@ public abstract class AbstractBuild
,R extends Abs
*
* @param phase
* true for the post build processing, and false for the final "run after finished" execution.
+ *
+ * @return false if any build step failed
*/
protected final boolean performAllBuildSteps(BuildListener listener, Iterable extends BuildStep> buildSteps, boolean phase) throws InterruptedException, IOException {
boolean r = true;
@@ -721,20 +723,39 @@ public abstract class AbstractBuild
,R extends Abs
if (!perform(bs,listener)) {
LOGGER.log(Level.FINE, "{0} : {1} failed", new Object[] {AbstractBuild.this, bs});
r = false;
+ if (phase) {
+ setResult(Result.FAILURE);
+ }
}
} catch (Exception e) {
reportError(bs, e, listener, phase);
+ r = false;
} catch (LinkageError e) {
reportError(bs, e, listener, phase);
+ r = false;
}
}
return r;
}
private void reportError(BuildStep bs, Throwable e, BuildListener listener, boolean phase) {
- String msg = "Publisher " + bs.getClass().getName() + " aborted due to exception";
- e.printStackTrace(listener.error(msg));
- LOGGER.log(WARNING, msg, e);
+ final String buildStep;
+
+ if (bs instanceof Describable) {
+ buildStep = ((Describable) bs).getDescriptor().getDisplayName();
+ } else {
+ buildStep = bs.getClass().getName();
+ }
+
+ if (e instanceof AbortException) {
+ LOGGER.log(Level.FINE, "{0} : {1} failed", new Object[] {AbstractBuild.this, buildStep});
+ listener.error("Step ‘" + buildStep + "’ failed: " + e.getMessage());
+ } else {
+ String msg = "Step ‘" + buildStep + "’ aborted due to exception: ";
+ e.printStackTrace(listener.error(msg));
+ LOGGER.log(WARNING, msg, e);
+ }
+
if (phase) {
setResult(Result.FAILURE);
}
@@ -1051,6 +1072,7 @@ public abstract class AbstractBuild
,R extends Abs
/**
* @deprecated Use {@link #getAction(Class)} on {@link AbstractTestResultAction}.
*/
+ @Deprecated
public Action getTestResultAction() {
try {
return getAction(Jenkins.getInstance().getPluginManager().uberClassLoader.loadClass("hudson.tasks.test.AbstractTestResultAction").asSubclass(Action.class));
@@ -1062,6 +1084,7 @@ public abstract class AbstractBuild
,R extends Abs
/**
* @deprecated Use {@link #getAction(Class)} on {@link AggregatedTestResultAction}.
*/
+ @Deprecated
public Action getAggregatedTestResultAction() {
try {
return getAction(Jenkins.getInstance().getPluginManager().uberClassLoader.loadClass("hudson.tasks.test.AggregatedTestResultAction").asSubclass(Action.class));
@@ -1340,6 +1363,7 @@ public abstract class AbstractBuild
,R extends Abs
* @deprecated as of 1.489
* Use {@link #doStop()}
*/
+ @Deprecated
public void doStop(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
doStop().generateResponse(req,rsp,this);
}
diff --git a/core/src/main/java/hudson/model/AbstractCIBase.java b/core/src/main/java/hudson/model/AbstractCIBase.java
index cf9cec9bf27df85901cfcfd41bf502aacd9cecf4..cf487105c207d235765df804143e8b5e3796f1bd 100644
--- a/core/src/main/java/hudson/model/AbstractCIBase.java
+++ b/core/src/main/java/hudson/model/AbstractCIBase.java
@@ -34,7 +34,6 @@ import jenkins.model.Jenkins;
import org.kohsuke.stapler.StaplerFallback;
import org.kohsuke.stapler.StaplerProxy;
-import java.io.IOException;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Logger;
@@ -48,8 +47,6 @@ public abstract class AbstractCIBase extends Node implements ItemGroup computers = getComputerMap();
- for (Map.Entry e : computers.entrySet()) {
- if (e.getValue() == computer) {
- computers.remove(e.getKey());
- computer.onRemoved();
- return;
+ /*package*/ void removeComputer(final Computer computer) {
+ Queue.withLock(new Runnable() {
+ @Override
+ public void run() {
+ Map computers = getComputerMap();
+ for (Map.Entry e : computers.entrySet()) {
+ if (e.getValue() == computer) {
+ computers.remove(e.getKey());
+ computer.onRemoved();
+ return;
+ }
+ }
}
- }
+ });
}
/*package*/ @CheckForNull Computer getComputer(Node n) {
@@ -160,36 +163,46 @@ public abstract class AbstractCIBase extends Node implements ItemGroup computers = getComputerMap();
- synchronized(updateComputerLock) {// just so that we don't have two code updating computer list at the same time
- Map byName = new HashMap();
- for (Computer c : computers.values()) {
- Node node = c.getNode();
- if (node == null)
- continue; // this computer is gone
- byName.put(node.getNodeName(),c);
- }
+ protected void updateComputerList(final boolean automaticSlaveLaunch) {
+ final Map computers = getComputerMap();
+ final Set old = new HashSet(computers.size());
+ Queue.withLock(new Runnable() {
+ @Override
+ public void run() {
+ Map byName = new HashMap();
+ for (Computer c : computers.values()) {
+ old.add(c);
+ Node node = c.getNode();
+ if (node == null)
+ continue; // this computer is gone
+ byName.put(node.getNodeName(),c);
+ }
- final Set old = new HashSet(computers.values());
- Set used = new HashSet();
+ Set used = new HashSet(old.size());
- updateComputer(this, byName, used, automaticSlaveLaunch);
- for (Node s : getNodes()) {
- long start = System.currentTimeMillis();
- updateComputer(s, byName, used, automaticSlaveLaunch);
- if(LOG_STARTUP_PERFORMANCE)
- LOGGER.info(String.format("Took %dms to update node %s",
- System.currentTimeMillis()-start, s.getNodeName()));
- }
+ updateComputer(AbstractCIBase.this, byName, used, automaticSlaveLaunch);
+ for (Node s : getNodes()) {
+ long start = System.currentTimeMillis();
+ updateComputer(s, byName, used, automaticSlaveLaunch);
+ if(LOG_STARTUP_PERFORMANCE)
+ LOGGER.info(String.format("Took %dms to update node %s",
+ System.currentTimeMillis()-start, s.getNodeName()));
+ }
- // find out what computers are removed, and kill off all executors.
- // when all executors exit, it will be removed from the computers map.
- // so don't remove too quickly
- old.removeAll(used);
- for (Computer c : old) {
- killComputer(c);
+ // find out what computers are removed, and kill off all executors.
+ // when all executors exit, it will be removed from the computers map.
+ // so don't remove too quickly
+ old.removeAll(used);
+ // we need to start the process of reducing the executors on all computers as distinct
+ // from the killing action which should not excessively use the Queue lock.
+ for (Computer c : old) {
+ c.inflictMortalWound();
+ }
}
+ });
+ for (Computer c : old) {
+ // when we get to here, the number of executors should be zero so this call should not need the Queue.lock
+ killComputer(c);
}
getQueue().scheduleMaintenance();
for (ComputerListener cl : ComputerListener.all())
diff --git a/core/src/main/java/hudson/model/AbstractDescribableImpl.java b/core/src/main/java/hudson/model/AbstractDescribableImpl.java
index 5961a80834a13feb490f3d29fd3976860913dcda..07ed386a930885d755b29f9b68c1d184d1bc7a18 100644
--- a/core/src/main/java/hudson/model/AbstractDescribableImpl.java
+++ b/core/src/main/java/hudson/model/AbstractDescribableImpl.java
@@ -37,6 +37,7 @@ public abstract class AbstractDescribableImpl{@inheritDoc}
*/
+ @Override
public Descriptor getDescriptor() {
return Jenkins.getInstance().getDescriptorOrDie(getClass());
}
diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java
index 3524444feaf3fe4c837502244dd049509a48f14f..f0f300805ea6c25ed8718c1be0f147429414cc7c 100644
--- a/core/src/main/java/hudson/model/AbstractItem.java
+++ b/core/src/main/java/hudson/model/AbstractItem.java
@@ -30,7 +30,6 @@ import hudson.XmlFile;
import hudson.Util;
import hudson.Functions;
import hudson.BulkChange;
-import hudson.cli.declarative.CLIMethod;
import hudson.cli.declarative.CLIResolver;
import hudson.model.listeners.ItemListener;
import hudson.model.listeners.SaveableListener;
@@ -45,6 +44,8 @@ import jenkins.model.DirectlyModifiableTopLevelItemGroup;
import jenkins.model.Jenkins;
import jenkins.security.NotReallyRoleSensitiveCallable;
import org.acegisecurity.Authentication;
+import jenkins.util.xml.XMLUtils;
+
import org.apache.tools.ant.taskdefs.Copy;
import org.apache.tools.ant.types.FileSet;
import org.kohsuke.stapler.WebMethod;
@@ -67,12 +68,11 @@ import org.kohsuke.stapler.HttpDeletable;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.stapler.interceptor.RequirePOST;
+import org.xml.sax.SAXException;
import javax.servlet.ServletException;
import javax.xml.transform.Source;
-import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
@@ -163,7 +163,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
}
public void setDisplayName(String displayName) throws IOException {
- this.displayName = Util.fixEmpty(displayName);
+ this.displayName = Util.fixEmptyAndTrim(displayName);
save();
}
@@ -215,6 +215,8 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
protected void renameTo(final String newName) throws IOException {
// always synchronize from bigger objects first
final ItemGroup parent = getParent();
+ String oldName = this.name;
+ String oldFullName = getFullName();
synchronized (parent) {
synchronized (this) {
// sanity check
@@ -247,9 +249,6 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
}
});
-
- String oldName = this.name;
- String oldFullName = getFullName();
File oldRoot = this.getRootDir();
doSetName(newName);
@@ -323,10 +322,9 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
} catch (AbstractMethodError _) {
// ignore
}
-
- ItemListener.fireLocationChange(this, oldFullName);
}
}
+ ItemListener.fireLocationChange(this, oldFullName);
}
@@ -539,7 +537,6 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
* since it predates {@code }. {@code /delete} goes to a Jelly page
* which should now be unused by core but is left in case plugins are still using it.
*/
- @CLIMethod(name="delete-job")
@RequirePOST
public void doDoDelete( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, InterruptedException {
delete();
@@ -622,30 +619,30 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
* @deprecated as of 1.473
* Use {@link #updateByXml(Source)}
*/
+ @Deprecated
public void updateByXml(StreamSource source) throws IOException {
updateByXml((Source)source);
}
/**
- * Updates Job by its XML definition.
+ * Updates an Item by its XML definition.
+ * @param source source of the Item's new definition.
+ * The source should be either a StreamSource or a SAXSource, other
+ * sources may not be handled.
* @since 1.473
*/
public void updateByXml(Source source) throws IOException {
checkPermission(CONFIGURE);
XmlFile configXmlFile = getConfigFile();
- AtomicFileWriter out = new AtomicFileWriter(configXmlFile.getFile());
+ final AtomicFileWriter out = new AtomicFileWriter(configXmlFile.getFile());
try {
try {
- // this allows us to use UTF-8 for storing data,
- // plus it checks any well-formedness issue in the submitted
- // data
- Transformer t = TransformerFactory.newInstance()
- .newTransformer();
- t.transform(source,
- new StreamResult(out));
+ XMLUtils.safeTransform(source, new StreamResult(out));
out.close();
} catch (TransformerException e) {
throw new IOException("Failed to persist config.xml", e);
+ } catch (SAXException e) {
+ throw new IOException("Failed to persist config.xml", e);
}
// try to reflect the changes by reloading
@@ -667,6 +664,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
// if everything went well, commit this new version
out.commit();
SaveableListener.fireOnChange(this, getConfigFile());
+
} finally {
out.abort(); // don't leave anything behind
}
@@ -681,7 +679,6 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
*
* @since 1.556
*/
- @CLIMethod(name="reload-job")
@RequirePOST
public void doReload() throws IOException {
checkPermission(CONFIGURE);
@@ -701,8 +698,8 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
}
- /* (non-Javadoc)
- * @see hudson.model.AbstractModelObject#getSearchName()
+ /**
+ * {@inheritDoc}
*/
@Override
public String getSearchName() {
@@ -724,8 +721,11 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
@Argument(required=true,metaVar="NAME",usage="Job name") String name) throws CmdLineException {
// TODO can this (and its pseudo-override in AbstractProject) share code with GenericItemOptionHandler, used for explicit CLICommand’s rather than CLIMethod’s?
AbstractItem item = Jenkins.getInstance().getItemByFullName(name, AbstractItem.class);
- if (item==null)
- throw new CmdLineException(null,Messages.AbstractItem_NoSuchJobExists(name,AbstractProject.findNearest(name).getFullName()));
+ if (item==null) {
+ AbstractProject project = AbstractProject.findNearest(name);
+ throw new CmdLineException(null, project == null ? Messages.AbstractItem_NoSuchJobExistsWithoutSuggestion(name)
+ : Messages.AbstractItem_NoSuchJobExists(name, project.getFullName()));
+ }
return item;
}
diff --git a/core/src/main/java/hudson/model/AbstractModelObject.java b/core/src/main/java/hudson/model/AbstractModelObject.java
index 301d6d09b3765a9b1ca449c1536d4706aacbe661..847d545222a99dfba2877d92fcfbf1579b12da23 100644
--- a/core/src/main/java/hudson/model/AbstractModelObject.java
+++ b/core/src/main/java/hudson/model/AbstractModelObject.java
@@ -81,6 +81,7 @@ public abstract class AbstractModelObject implements SearchableModelObject {
* @deprecated
* Use {@link RequirePOST} on your method.
*/
+ @Deprecated
protected final void requirePOST() throws ServletException {
StaplerRequest req = Stapler.getCurrentRequest();
if (req==null) return; // invoked outside the context of servlet
diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java
index 4a8e3dd901087490ace0d33414d07520f081a2ca..d807695eb465516dc62d76ffd078e0db05817923 100644
--- a/core/src/main/java/hudson/model/AbstractProject.java
+++ b/core/src/main/java/hudson/model/AbstractProject.java
@@ -55,7 +55,6 @@ import hudson.model.queue.CauseOfBlockage;
import hudson.model.queue.QueueTaskFuture;
import hudson.model.queue.SubTask;
import hudson.model.queue.SubTaskContributor;
-import hudson.node_monitors.DiskSpaceMonitor;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
import hudson.scm.NullSCM;
@@ -106,6 +105,7 @@ import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
+import jenkins.model.BlockedBecauseOfBuildInProgress;
import jenkins.model.Jenkins;
import jenkins.model.JenkinsLocationConfiguration;
import jenkins.model.ParameterizedJobMixIn;
@@ -266,7 +266,9 @@ public abstract class AbstractProject
,R extends A
buildMixIn = createBuildMixIn();
builds = buildMixIn.getRunMap();
- if(Jenkins.getInstance()!=null && !Jenkins.getInstance().getNodes().isEmpty()) {
+ final Jenkins j = Jenkins.getInstance();
+ final List nodes = j != null ? j.getNodes() : null;
+ if(nodes!=null && !nodes.isEmpty()) {
// if a new job is configured with Hudson that already has slave nodes
// make it roamable by default
canRoam = true;
@@ -323,7 +325,11 @@ public abstract class AbstractProject
,R extends A
builds = buildMixIn.getRunMap();
triggers().setOwner(this);
for (Trigger t : triggers()) {
- t.start(this, Items.currentlyUpdatingByXml());
+ try {
+ t.start(this, Items.currentlyUpdatingByXml());
+ } catch (Throwable e) {
+ LOGGER.log(Level.WARNING, "could not start trigger while loading project '" + getFullName() + "'", e);
+ }
}
if(scm==null)
scm = new NullSCM(); // perhaps it was pointing to a plugin that no longer exists.
@@ -351,7 +357,7 @@ public abstract class AbstractProject
,R extends A
jdkTool = jdkTool.forNode(node, listener);
}
jdkTool.buildEnvVars(env);
- } else if (jdk != null && !jdk.equals(JDK.DEFAULT_NAME)) {
+ } else if (!JDK.isDefaultName(jdk)) {
listener.getLogger().println("No JDK named ‘" + jdk + "’ found");
}
@@ -465,6 +471,7 @@ public abstract class AbstractProject
,R extends A
* @since 1.401
*/
public String getBuildNowText() {
+ // For compatibility, still use the deprecated replacer if specified.
return AlternativeUiTextProvider.get(BUILD_NOW_TEXT, this, getParameterizedJobMixIn().getBuildNowText());
}
@@ -505,6 +512,7 @@ public abstract class AbstractProject
,R extends A
* If you are calling this method to serve a file from the workspace, doing a form validation, etc., then
* use {@link #getSomeWorkspace()}
*/
+ @Deprecated
public final FilePath getWorkspace() {
AbstractBuild b = getBuildForDeprecatedMethods();
return b != null ? b.getWorkspace() : null;
@@ -586,6 +594,7 @@ public abstract class AbstractProject
,R extends A
* @deprecated as of 1.319
* See {@link #getWorkspace()} for a migration strategy.
*/
+ @Deprecated
public FilePath getModuleRoot() {
AbstractBuild b = getBuildForDeprecatedMethods();
return b != null ? b.getModuleRoot() : null;
@@ -601,6 +610,7 @@ public abstract class AbstractProject
,R extends A
* @deprecated as of 1.319
* See {@link #getWorkspace()} for a migration strategy.
*/
+ @Deprecated
public FilePath[] getModuleRoots() {
AbstractBuild b = getBuildForDeprecatedMethods();
return b != null ? b.getModuleRoots() : null;
@@ -797,6 +807,7 @@ public abstract class AbstractProject
,R extends A
* @deprecated
* Use {@link #scheduleBuild(Cause)}. Since 1.283
*/
+ @Deprecated
public boolean scheduleBuild() {
return getParameterizedJobMixIn().scheduleBuild();
}
@@ -805,6 +816,7 @@ public abstract class AbstractProject
,R extends A
* @deprecated
* Use {@link #scheduleBuild(int, Cause)}. Since 1.283
*/
+ @Deprecated
public boolean scheduleBuild(int quietPeriod) {
return getParameterizedJobMixIn().scheduleBuild(quietPeriod);
}
@@ -1077,6 +1089,7 @@ public abstract class AbstractProject
,R extends A
* or if the blockBuildWhenUpstreamBuilding option is true and an upstream
* project is building, but derived classes can also check other conditions.
*/
+ @Override
public boolean isBuildBlocked() {
return getCauseOfBlockage()!=null;
}
@@ -1087,23 +1100,12 @@ public abstract class AbstractProject
,R extends A
}
/**
- * Blocked because the previous build is already in progress.
+ * @deprecated use {@link BlockedBecauseOfBuildInProgress} instead.
*/
- public static class BecauseOfBuildInProgress extends CauseOfBlockage {
- private final AbstractBuild,?> build;
-
+ @Deprecated
+ public static class BecauseOfBuildInProgress extends BlockedBecauseOfBuildInProgress {
public BecauseOfBuildInProgress(AbstractBuild, ?> build) {
- this.build = build;
- }
-
- @Override
- public String getShortDescription() {
- Executor e = build.getExecutor();
- String eta = "";
- if (e != null)
- eta = Messages.AbstractProject_ETA(e.getEstimatedRemainingTime());
- int lbn = build.getNumber();
- return Messages.AbstractProject_BuildInProgress(lbn, eta);
+ super(build);
}
}
@@ -1139,10 +1141,12 @@ public abstract class AbstractProject
,R extends A
}
}
+ @Override
public CauseOfBlockage getCauseOfBlockage() {
// Block builds until they are done with post-production
- if (isLogUpdated() && !isConcurrentBuild())
- return new BecauseOfBuildInProgress(getLastBuild());
+ if (isLogUpdated() && !isConcurrentBuild()) {
+ return new BlockedBecauseOfBuildInProgress(getLastBuild());
+ }
if (blockBuildWhenDownstreamBuilding()) {
AbstractProject,?> bup = getBuildingDownstream();
if (bup!=null)
@@ -1226,6 +1230,7 @@ public abstract class AbstractProject
,R extends A
* If you need to lock a workspace while you do some computation, see the source code of
* {@link #pollSCMChanges(TaskListener)} for how to obtain a lock of a workspace through {@link WorkspaceList}.
*/
+ @Deprecated
public Resource getWorkspaceResource() {
return new Resource(getFullDisplayName()+" workspace");
}
@@ -1259,13 +1264,7 @@ public abstract class AbstractProject
,R extends A
return true; // no SCM
FilePath workspace = build.getWorkspace();
- try {
- workspace.mkdirs();
- } catch (IOException e) {
- // Can't create workspace dir - Is slave disk full ?
- new DiskSpaceMonitor().markNodeOfflineIfDiskspaceIsTooLow(build.getBuiltOn().toComputer());
- throw e;
- }
+ workspace.mkdirs();
boolean r = scm.checkout(build, launcher, workspace, listener, changelogFile);
if (r) {
@@ -1299,6 +1298,7 @@ public abstract class AbstractProject
,R extends A
* @deprecated as of 1.346
* Use {@link #poll(TaskListener)} instead.
*/
+ @Deprecated
public boolean pollSCMChanges( TaskListener listener ) {
return poll(listener).hasChanges();
}
@@ -1460,7 +1460,8 @@ public abstract class AbstractProject
,R extends A
Launcher launcher = ws.createLauncher(listener).decorateByEnv(getEnvironment(node,listener));
WorkspaceList.Lease lease = l.acquire(ws, !concurrentBuild);
try {
- listener.getLogger().println("Polling SCM changes on " + node.getSelfLabel().getName());
+ String nodeName = node != null ? node.getSelfLabel().getName() : "[node_unavailable]";
+ listener.getLogger().println("Polling SCM changes on " + nodeName);
LOGGER.fine("Polling SCM changes of " + getName());
if (pollingBaseline==null) // see NOTE-NO-BASELINE above
calcPollingBaseline(lb,launcher,listener);
@@ -1756,6 +1757,7 @@ public abstract class AbstractProject
,R extends A
* @deprecated as of 1.489
* Inject {@link TimeDuration}.
*/
+ @Deprecated
public int getDelay(StaplerRequest req) throws ServletException {
String delay = req.getParameter("delay");
if (delay==null) return getQuietPeriod();
@@ -1872,6 +1874,7 @@ public abstract class AbstractProject
,R extends A
* @deprecated
* As of 1.261. Use {@link #buildDescribable(StaplerRequest, List)} instead.
*/
+ @Deprecated
protected final > List buildDescribable(StaplerRequest req, List extends Descriptor> descriptors, String prefix) throws FormException, ServletException {
return buildDescribable(req,descriptors);
}
@@ -1920,6 +1923,7 @@ public abstract class AbstractProject
,R extends A
/**
* Wipes out the workspace.
*/
+ @RequirePOST
public HttpResponse doDoWipeOutWorkspace() throws IOException, ServletException, InterruptedException {
checkPermission(Functions.isWipeOutPermissionEnabled() ? WIPEOUT : BUILD);
R b = getSomeBuildWithWorkspace();
@@ -2097,8 +2101,8 @@ public abstract class AbstractProject
,R extends A
}
}
return FormValidation.okWithMarkup(Messages.AbstractProject_LabelLink(
- j.getRootUrl(), l.getUrl(), l.getNodes().size() + l.getClouds().size()
- ));
+ j.getRootUrl(), l.getUrl(), l.getNodes().size(), l.getClouds().size())
+ );
}
public FormValidation doCheckCustomWorkspace(@QueryParameter String customWorkspace){
@@ -2219,11 +2223,13 @@ public abstract class AbstractProject
,R extends A
/**
* @deprecated Just use {@link #CANCEL}.
*/
+ @Deprecated
public static final Permission ABORT = CANCEL;
/**
- * Replaceable "Build Now" text.
+ * @deprecated Use {@link ParameterizedJobMixIn#BUILD_NOW_TEXT}.
*/
+ @Deprecated
public static final Message BUILD_NOW_TEXT = new Message();
/**
@@ -2233,8 +2239,11 @@ public abstract class AbstractProject
,R extends A
public static AbstractProject resolveForCLI(
@Argument(required=true,metaVar="NAME",usage="Job name") String name) throws CmdLineException {
AbstractProject item = Jenkins.getInstance().getItemByFullName(name, AbstractProject.class);
- if (item==null)
- throw new CmdLineException(null,Messages.AbstractItem_NoSuchJobExists(name,AbstractProject.findNearest(name).getFullName()));
+ if (item==null) {
+ AbstractProject project = AbstractProject.findNearest(name);
+ throw new CmdLineException(null, project == null ? Messages.AbstractItem_NoSuchJobExistsWithoutSuggestion(name)
+ : Messages.AbstractItem_NoSuchJobExists(name, project.getFullName()));
+ }
return item;
}
diff --git a/core/src/main/java/hudson/model/AdministrativeMonitor.java b/core/src/main/java/hudson/model/AdministrativeMonitor.java
index a8e555409214ac44570ffbd1384d8c53b887630d..0bdc8a97bc18b9cab09dbca8e559273e67cf2ae4 100644
--- a/core/src/main/java/hudson/model/AdministrativeMonitor.java
+++ b/core/src/main/java/hudson/model/AdministrativeMonitor.java
@@ -28,7 +28,6 @@ import hudson.ExtensionList;
import hudson.Extension;
import hudson.ExtensionPoint.LegacyInstancesAreScopedToHudson;
import hudson.triggers.SCMTrigger;
-import hudson.triggers.TimerTrigger;
import java.util.Set;
import java.io.IOException;
diff --git a/core/src/main/java/hudson/model/AperiodicWork.java b/core/src/main/java/hudson/model/AperiodicWork.java
index 56b0804da1ec2f949193a67c93d99e23db88ea70..fbd4f2c4f7a3da987d57e4f61168d453647ff667 100644
--- a/core/src/main/java/hudson/model/AperiodicWork.java
+++ b/core/src/main/java/hudson/model/AperiodicWork.java
@@ -27,11 +27,9 @@ import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.init.Initializer;
import hudson.triggers.SafeTimerTask;
-import jenkins.model.Jenkins;
import jenkins.util.Timer;
import java.util.Random;
-import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
diff --git a/core/src/main/java/hudson/model/Api.java b/core/src/main/java/hudson/model/Api.java
index c749f46d133336ef38979dae3a6a0c275a0184a8..258f48ffea95f243d6d47b2f831e356d51286f9f 100644
--- a/core/src/main/java/hudson/model/Api.java
+++ b/core/src/main/java/hudson/model/Api.java
@@ -24,6 +24,7 @@
package hudson.model;
import hudson.ExtensionList;
+import jenkins.util.xml.FilteredFunctionContext;
import jenkins.model.Jenkins;
import jenkins.security.SecureRequester;
@@ -32,6 +33,7 @@ import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
+import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.kohsuke.stapler.QueryParameter;
@@ -107,14 +109,16 @@ public class Api extends AbstractModelObject {
p.writeTo(bean,pruner,Flavor.XML.createDataWriter(bean,sw));
// apply XPath
+ FilteredFunctionContext functionContext = new FilteredFunctionContext();
Object result;
try {
Document dom = new SAXReader().read(new StringReader(sw.toString()));
-
// apply exclusions
if (excludes!=null) {
for (String exclude : excludes) {
- List list = (List)dom.selectNodes(exclude);
+ XPath xExclude = dom.createXPath(exclude);
+ xExclude.setFunctionContext(functionContext);
+ List list = (List)xExclude.selectNodes(dom);
for (org.dom4j.Node n : list) {
Element parent = n.getParent();
if(parent!=null)
@@ -126,7 +130,9 @@ public class Api extends AbstractModelObject {
if(xpath==null) {
result = dom;
} else {
- List list = dom.selectNodes(xpath);
+ XPath comp = dom.createXPath(xpath);
+ comp.setFunctionContext(functionContext);
+ List list = comp.selectNodes(dom);
if (wrapper!=null) {
Element root = DocumentFactory.getInstance().createElement(wrapper);
for (Object o : list) {
@@ -202,7 +208,7 @@ public class Api extends AbstractModelObject {
public void doJson(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
if (req.getParameter("jsonp") == null || permit(req)) {
setHeaders(rsp);
- rsp.serveExposedBean(req,bean, Flavor.JSON);
+ rsp.serveExposedBean(req,bean, req.getParameter("jsonp") == null ? Flavor.JSON : Flavor.JSONP);
} else {
rsp.sendError(HttpURLConnection.HTTP_FORBIDDEN, "jsonp forbidden; implement jenkins.security.SecureRequester");
}
diff --git a/core/src/main/java/hudson/model/AsyncAperiodicWork.java b/core/src/main/java/hudson/model/AsyncAperiodicWork.java
index f46dd33b61d835eec945fec60f76975809d9c3de..9111e5fd474d5536879c86180272737fe26aa67c 100644
--- a/core/src/main/java/hudson/model/AsyncAperiodicWork.java
+++ b/core/src/main/java/hudson/model/AsyncAperiodicWork.java
@@ -31,8 +31,6 @@ import java.io.IOException;
import java.util.logging.Level;
import jenkins.model.Jenkins;
-import org.acegisecurity.context.SecurityContext;
-import org.acegisecurity.context.SecurityContextHolder;
/**
* {@link AperiodicWork} that takes a long time to run. Similar to {@link AsyncPeriodicWork}, see {@link AsyncPeriodicWork} for
diff --git a/core/src/main/java/hudson/model/Build.java b/core/src/main/java/hudson/model/Build.java
index 99073f1b7332b5eccc737e2eea1edd55ed5f9a14..a5dbcfcf1f1958afc9cdccb54d4074d326d84470 100644
--- a/core/src/main/java/hudson/model/Build.java
+++ b/core/src/main/java/hudson/model/Build.java
@@ -119,6 +119,7 @@ public abstract class Build
,B extends Build
>
* proper execution object.
*/
@Restricted(NoExternalUse.class)
+ @Deprecated
protected Runner createRunner() {
return new BuildExecution();
}
@@ -127,6 +128,7 @@ public abstract class Build
,B extends Build
>
* @deprecated as of 1.467
* Please use {@link BuildExecution}
*/
+ @Deprecated
protected class RunnerImpl extends BuildExecution {
}
@@ -189,8 +191,12 @@ public abstract class Build
,B extends Build
>
@Override
public void cleanUp(@Nonnull BuildListener listener) throws Exception {
// at this point it's too late to mark the build as a failure, so ignore return value.
- performAllBuildSteps(listener, project.getPublishersList(), false);
- performAllBuildSteps(listener, project.getProperties(), false);
+ try {
+ performAllBuildSteps(listener, project.getPublishersList(), false);
+ performAllBuildSteps(listener, project.getProperties(), false);
+ } catch (Exception x) {
+ x.printStackTrace(listener.error(Messages.Build_post_build_steps_failed()));
+ }
super.cleanUp(listener);
}
diff --git a/core/src/main/java/hudson/model/BuildAuthorizationToken.java b/core/src/main/java/hudson/model/BuildAuthorizationToken.java
index 7ab272afdad7f2638a8befa703561aa723a44e1f..8aa53eb429da444c1a435e99abc9cd72e35f21b1 100644
--- a/core/src/main/java/hudson/model/BuildAuthorizationToken.java
+++ b/core/src/main/java/hudson/model/BuildAuthorizationToken.java
@@ -45,6 +45,7 @@ import org.kohsuke.stapler.HttpResponses;
* Use {@link ACL} and {@link Item#BUILD}. This code is only here
* for the backward compatibility.
*/
+@Deprecated
public final class BuildAuthorizationToken {
private final String token;
diff --git a/core/src/main/java/hudson/model/BuildStepListener.java b/core/src/main/java/hudson/model/BuildStepListener.java
index a37cb8f67abfa6f37622d042ff09038207f4a6dd..e0ee3efe29ce12313b1ff81abfbc11c51d7d0905 100644
--- a/core/src/main/java/hudson/model/BuildStepListener.java
+++ b/core/src/main/java/hudson/model/BuildStepListener.java
@@ -3,7 +3,6 @@ package hudson.model;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.tasks.BuildStep;
-import jenkins.model.Jenkins;
/**
* Receives events that happen as a build executes {@link BuildStep}s.
diff --git a/core/src/main/java/hudson/model/BuildVariableContributor.java b/core/src/main/java/hudson/model/BuildVariableContributor.java
index 15baad759088c83ce5deb3cde44e56e7b4103e68..83046baa5440d2c5f7c489db65c040ce54da5009 100644
--- a/core/src/main/java/hudson/model/BuildVariableContributor.java
+++ b/core/src/main/java/hudson/model/BuildVariableContributor.java
@@ -27,7 +27,6 @@ import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.tasks.Builder;
import hudson.tasks.Publisher;
-import jenkins.model.Jenkins;
import java.util.Map;
diff --git a/core/src/main/java/hudson/model/BuildableItem.java b/core/src/main/java/hudson/model/BuildableItem.java
index 17956d175b6f3d2cbdab7a7e07b8607eecfa0528..ef845e11750de8a31ae6ff4c2627ddc6594ceb3f 100644
--- a/core/src/main/java/hudson/model/BuildableItem.java
+++ b/core/src/main/java/hudson/model/BuildableItem.java
@@ -39,12 +39,14 @@ public interface BuildableItem extends Item, Task {
* @deprecated
* Use {@link #scheduleBuild(Cause)}. Since 1.283
*/
+ @Deprecated
boolean scheduleBuild();
boolean scheduleBuild(Cause c);
/**
* @deprecated
* Use {@link #scheduleBuild(int, Cause)}. Since 1.283
*/
+ @Deprecated
boolean scheduleBuild(int quietPeriod);
boolean scheduleBuild(int quietPeriod, Cause c);
}
diff --git a/core/src/main/java/hudson/model/Cause.java b/core/src/main/java/hudson/model/Cause.java
index 37dbcee4ca753fdb5fa11f88eac7626fe77d5e9f..2ee6ba7982730ee1a563224c80e75cf8a6c1270b 100644
--- a/core/src/main/java/hudson/model/Cause.java
+++ b/core/src/main/java/hudson/model/Cause.java
@@ -124,6 +124,7 @@ public abstract class Cause {
* Fall back implementation when no other type is available.
* @deprecated since 2009-02-08
*/
+ @Deprecated
public static class LegacyCodeCause extends Cause {
private StackTraceElement [] stackTrace;
public LegacyCodeCause() {
@@ -162,6 +163,7 @@ public abstract class Cause {
* @deprecated since 2009-02-28
*/
// for backward bytecode compatibility
+ @Deprecated
public UpstreamCause(AbstractBuild,?> up) {
this((Run,?>)up);
}
@@ -361,6 +363,7 @@ public abstract class Cause {
* @deprecated 1.428
* use {@link UserIdCause}
*/
+ @Deprecated
public static class UserCause extends Cause {
private String authenticationName;
public UserCause() {
diff --git a/core/src/main/java/hudson/model/CauseAction.java b/core/src/main/java/hudson/model/CauseAction.java
index afe715cef091f7832b987854a8d91cdadefe91ea..252c91c722cbba7ad0dc081bfadbf74c666862de 100644
--- a/core/src/main/java/hudson/model/CauseAction.java
+++ b/core/src/main/java/hudson/model/CauseAction.java
@@ -113,6 +113,7 @@ public class CauseAction implements FoldableAction, RunAction2 {
* @deprecated as of 1.288
* but left here for backward compatibility.
*/
+ @Deprecated
public String getShortDescription() {
if(causes.isEmpty()) return "N/A";
return causes.get(0).getShortDescription();
diff --git a/core/src/main/java/hudson/model/ChoiceParameterDefinition.java b/core/src/main/java/hudson/model/ChoiceParameterDefinition.java
index c13137d62a7b2f833497b302cb66960d74c17936..0bd4b60ab3666bb55d7331ed29333304b352b18f 100644
--- a/core/src/main/java/hudson/model/ChoiceParameterDefinition.java
+++ b/core/src/main/java/hudson/model/ChoiceParameterDefinition.java
@@ -17,20 +17,24 @@ import java.util.Arrays;
* @author huybrechts
*/
public class ChoiceParameterDefinition extends SimpleParameterDefinition {
- public static final String CHOICES_DELIMETER = "\\r?\\n";
+ public static final String CHOICES_DELIMITER = "\\r?\\n";
+
+ @Deprecated
+ public static final String CHOICES_DELIMETER = CHOICES_DELIMITER;
+
private final List choices;
private final String defaultValue;
public static boolean areValidChoices(String choices) {
String strippedChoices = choices.trim();
- return !StringUtils.isEmpty(strippedChoices) && strippedChoices.split(CHOICES_DELIMETER).length > 0;
+ return !StringUtils.isEmpty(strippedChoices) && strippedChoices.split(CHOICES_DELIMITER).length > 0;
}
@DataBoundConstructor
public ChoiceParameterDefinition(String name, String choices, String description) {
super(name, description);
- this.choices = Arrays.asList(choices.split(CHOICES_DELIMETER));
+ this.choices = Arrays.asList(choices.split(CHOICES_DELIMITER));
defaultValue = null;
}
diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java
index 2d23453fad5293ef1a35ab79dc98938cb3ab20c8..4ce4a3b7a39db9ac05bf9c7b02023a7c29662e5a 100644
--- a/core/src/main/java/hudson/model/Computer.java
+++ b/core/src/main/java/hudson/model/Computer.java
@@ -2,7 +2,8 @@
* The MIT License
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
- * Red Hat, Inc., Seiji Sogabe, Stephen Connolly, Thomas J. Black, Tom Huybrechts, CloudBees, Inc.
+ * Red Hat, Inc., Seiji Sogabe, Stephen Connolly, Thomas J. Black, Tom Huybrechts,
+ * CloudBees, Inc., Christopher Simons
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -27,6 +28,7 @@ package hudson.model;
import edu.umd.cs.findbugs.annotations.OverrideMustInvoke;
import edu.umd.cs.findbugs.annotations.When;
import hudson.EnvVars;
+import hudson.Extension;
import hudson.Launcher.ProcStarter;
import hudson.Util;
import hudson.cli.declarative.CLIMethod;
@@ -45,6 +47,7 @@ import hudson.security.AccessControlled;
import hudson.security.Permission;
import hudson.security.PermissionGroup;
import hudson.security.PermissionScope;
+import hudson.slaves.AbstractCloudSlave;
import hudson.slaves.ComputerLauncher;
import hudson.slaves.ComputerListener;
import hudson.slaves.NodeProperty;
@@ -63,8 +66,14 @@ import hudson.util.NamingThreadFactory;
import jenkins.model.Jenkins;
import jenkins.util.ContextResettingExecutorService;
import jenkins.security.MasterToSlaveCallable;
+import jenkins.security.NotReallyRoleSensitiveCallable;
+
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.DoNotUse;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.QueryParameter;
@@ -77,7 +86,9 @@ import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.args4j.Option;
import org.kohsuke.stapler.interceptor.RequirePOST;
+import javax.annotation.concurrent.GuardedBy;
import javax.servlet.ServletException;
+
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
@@ -99,6 +110,7 @@ import java.net.NetworkInterface;
import java.net.Inet4Address;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -164,12 +176,70 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
private volatile String cachedHostName;
private volatile boolean hostNameCached;
+ /**
+ * @see #getEnvironment()
+ */
+ private volatile EnvVars cachedEnvironment;
+
+
private final WorkspaceList workspaceList = new WorkspaceList();
protected transient List transientActions;
protected final Object statusChangeLock = new Object();
+ /**
+ * Keeps track of stack traces to track the tremination requests for this computer.
+ *
+ * @since 1.607
+ * @see Executor#resetWorkUnit(String)
+ */
+ private transient final List terminatedBy = Collections.synchronizedList(new ArrayList
+ ());
+
+ /**
+ * This method captures the information of a request to terminate a computer instance. Method is public as
+ * it needs to be called from {@link AbstractCloudSlave} and {@link jenkins.model.Nodes}. In general you should
+ * not need to call this method directly, however if implementing a custom node type or a different path
+ * for removing nodes, it may make sense to call this method in order to capture the originating request.
+ *
+ * @since 1.607
+ */
+ public void recordTermination() {
+ StaplerRequest request = Stapler.getCurrentRequest();
+ if (request != null) {
+ terminatedBy.add(new TerminationRequest(
+ String.format("Termination requested at %s by %s [id=%d] from HTTP request for %s",
+ new Date(),
+ Thread.currentThread(),
+ Thread.currentThread().getId(),
+ request.getRequestURL()
+ )
+ ));
+ } else {
+ terminatedBy.add(new TerminationRequest(
+ String.format("Termination requested at %s by %s [id=%d]",
+ new Date(),
+ Thread.currentThread(),
+ Thread.currentThread().getId()
+ )
+ ));
+ }
+ }
+
+ /**
+ * Returns the list of captured termination requests for this Computer. This method is used by {@link Executor}
+ * to provide details on why a Computer was removed in-between work being scheduled against the {@link Executor}
+ * and the {@link Executor} starting to execute the task.
+ *
+ * @return the (possibly empty) list of termination requests.
+ * @see Executor#resetWorkUnit(String)
+ * @since 1.607
+ */
+ public List getTerminatedBy() {
+ return new ArrayList(terminatedBy);
+ }
+
public Computer(Node node) {
setNode(node);
}
@@ -207,14 +277,25 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
/**
* This is where the log from the remote agent goes.
* The method also creates a log directory if required.
- * @see #relocateOldLogs()
+ * @see #getLogDir(), #relocateOldLogs()
*/
- public File getLogFile() {
+ public @Nonnull File getLogFile() {
+ return new File(getLogDir(),"slave.log");
+ }
+
+ /**
+ * Directory where rotated slave logs are stored.
+ *
+ * The method also creates a log directory if required.
+ *
+ * @since 1.613
+ */
+ protected @Nonnull File getLogDir() {
File dir = new File(Jenkins.getInstance().getRootDir(),"logs/slaves/"+nodeName);
if (!dir.exists() && !dir.mkdirs()) {
LOGGER.severe("Failed to create slave log directory " + dir.getAbsolutePath());
}
- return new File(dir,"slave.log");
+ return dir;
}
/**
@@ -314,6 +395,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
/**
* @deprecated since 2009-01-06. Use {@link #connect(boolean)}
*/
+ @Deprecated
public final void launch() {
connect(true);
}
@@ -397,6 +479,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* @since 1.320
*/
public Future> disconnect(OfflineCause cause) {
+ recordTermination();
offlineCause = cause;
if (Util.isOverridden(Computer.class,getClass(),"disconnect"))
return disconnect(); // legacy subtypes that extend disconnect().
@@ -411,7 +494,9 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* @deprecated as of 1.320.
* Use {@link #disconnect(OfflineCause)} and specify the cause.
*/
+ @Deprecated
public Future> disconnect() {
+ recordTermination();
if (Util.isOverridden(Computer.class,getClass(),"disconnect",OfflineCause.class))
// if the subtype already derives disconnect(OfflineCause), delegate to it
return disconnect(null);
@@ -435,13 +520,12 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
@CLIMethod(name="offline-node")
public void cliOffline(@Option(name="-m",usage="Record the note about why you are disconnecting this node") String cause) throws ExecutionException, InterruptedException {
checkPermission(DISCONNECT);
- setTemporarilyOffline(true,new ByCLI(cause));
+ setTemporarilyOffline(true, new ByCLI(cause));
}
- @CLIMethod(name="online-node")
public void cliOnline() throws ExecutionException, InterruptedException {
checkPermission(CONNECT);
- setTemporarilyOffline(false,null);
+ setTemporarilyOffline(false, null);
}
/**
@@ -464,6 +548,15 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
return nodeName != null ? nodeName : "";
}
+ /**
+ * True if this computer is a Unix machine (as opposed to Windows machine).
+ *
+ * @since 1.624
+ * @return
+ * null if the computer is disconnected and therefore we don't know whether it is Unix or not.
+ */
+ public abstract @CheckForNull Boolean isUnix();
+
/**
* Returns the {@link Node} that this computer represents.
*
@@ -494,6 +587,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
/**
* {@inheritDoc}
*/
+ @Override
public void taskAccepted(Executor executor, Queue.Task task) {
// dummy implementation
}
@@ -501,6 +595,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
/**
* {@inheritDoc}
*/
+ @Override
public void taskCompleted(Executor executor, Queue.Task task, long durationMS) {
// dummy implementation
}
@@ -508,6 +603,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
/**
* {@inheritDoc}
*/
+ @Override
public void taskCompletedWithProblems(Executor executor, Queue.Task task, long durationMS, Throwable problems) {
// dummy implementation
}
@@ -573,6 +669,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* accidentally call this method.
*/
@Exported
+ @Deprecated
public boolean isTemporarilyOffline() {
return temporarilyOffline;
}
@@ -581,6 +678,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* @deprecated as of 1.320.
* Use {@link #setTemporarilyOffline(boolean, OfflineCause)}
*/
+ @Deprecated
public void setTemporarilyOffline(boolean temporarilyOffline) {
setTemporarilyOffline(temporarilyOffline,null);
}
@@ -600,7 +698,6 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
if (node != null) {
node.setTemporaryOfflineCause(offlineCause);
}
- Jenkins.getInstance().getQueue().scheduleMaintenance();
synchronized (statusChangeLock) {
statusChangeLock.notifyAll();
}
@@ -691,6 +788,26 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* @see #onRemoved()
*/
protected void kill() {
+ // On most code paths, this should already be zero, and thus this next call becomes a no-op... and more
+ // importantly it will not acquire a lock on the Queue... not that the lock is bad, more that the lock
+ // may delay unnecessarily
+ setNumExecutors(0);
+ }
+
+ /**
+ * Called by {@link Jenkins#updateComputerList()} to notify {@link Computer} that it will be discarded.
+ *
+ *
+ * Note that at this point {@link #getNode()} returns null.
+ *
+ *
+ * Note that the Queue lock is already held when this method is called.
+ *
+ * @see #onRemoved()
+ */
+ @Restricted(NoExternalUse.class)
+ @GuardedBy("hudson.model.Queue.lock")
+ /*package*/ void inflictMortalWound() {
setNumExecutors(0);
}
@@ -801,6 +918,32 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
return new ArrayList(oneOffExecutors);
}
+ /**
+ * Used to render the list of executors.
+ * @return a snapshot of the executor display information
+ * @since 1.607
+ */
+ @Restricted(NoExternalUse.class)
+ public List getDisplayExecutors() {
+ // The size may change while we are populating, but let's start with a reasonable guess to minimize resizing
+ List result = new ArrayList(executors.size()+oneOffExecutors.size());
+ int index = 0;
+ for (Executor e: executors) {
+ if (e.isDisplayCell()) {
+ result.add(new DisplayExecutor(Integer.toString(index + 1), String.format("executors/%d", index), e));
+ }
+ index++;
+ }
+ index = 0;
+ for (OneOffExecutor e: oneOffExecutors) {
+ if (e.isDisplayCell()) {
+ result.add(new DisplayExecutor("", String.format("oneOffExecutors/%d", index), e));
+ }
+ index++;
+ }
+ return result;
+ }
+
/**
* Returns true if all the executors of this computer are idle.
*/
@@ -860,18 +1003,30 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
/**
* Called by {@link Executor} to kill excessive executors from this computer.
*/
- /*package*/ synchronized void removeExecutor(Executor e) {
- executors.remove(e);
- addNewExecutorIfNecessary();
- if(!isAlive())
- {
- AbstractCIBase ciBase = Jenkins.getInstance();
- ciBase.removeComputer(this);
+ /*package*/ void removeExecutor(final Executor e) {
+ final Runnable task = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (Computer.this) {
+ executors.remove(e);
+ addNewExecutorIfNecessary();
+ if (!isAlive()) {
+ AbstractCIBase ciBase = Jenkins.getInstance();
+ if (ciBase != null) {
+ ciBase.removeComputer(Computer.this);
+ }
+ }
+ }
+ }
+ };
+ if (!Queue.tryWithLock(task)) {
+ // JENKINS-28840 if we couldn't get the lock push the operation to a separate thread to avoid deadlocks
+ threadPoolForRemoting.submit(Queue.wrapWithLock(task));
}
}
/**
- * Returns true if any of the executors are functioning.
+ * Returns true if any of the executors are {@linkplain Executor#isActive active}.
*
* Note that if an executor dies, we'll leave it in {@link #executors} until
* the administrator yanks it out, so that we can see why it died.
@@ -887,11 +1042,17 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
/**
* Interrupt all {@link Executor}s.
+ * Called from {@link Jenkins#cleanUp}.
*/
public void interrupt() {
- for (Executor e : executors) {
- e.interrupt();
- }
+ Queue.withLock(new Runnable() {
+ @Override
+ public void run() {
+ for (Executor e : executors) {
+ e.interruptForShutdown();
+ }
+ }
+ });
}
public String getSearchUrl() {
@@ -930,16 +1091,24 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* @deprecated as of 1.292
* Use {@link #getEnvironment()} instead.
*/
+ @Deprecated
public Map getEnvVars() throws IOException, InterruptedException {
return getEnvironment();
}
/**
- * Gets the environment variables of the JVM on this computer.
+ * Returns cached environment variables (copy to prevent modification) for the JVM on this computer.
* If this is the master, it returns the system property of the master computer.
*/
public EnvVars getEnvironment() throws IOException, InterruptedException {
- return EnvVars.getRemote(getChannel());
+ EnvVars cachedEnvironment = this.cachedEnvironment;
+ if (cachedEnvironment != null) {
+ return new EnvVars(cachedEnvironment);
+ }
+
+ cachedEnvironment = EnvVars.getRemote(getChannel());
+ this.cachedEnvironment = cachedEnvironment;
+ return new EnvVars(cachedEnvironment);
}
/**
@@ -1022,11 +1191,11 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
try {
InetAddress ia = InetAddress.getByName(address);
if(!(ia instanceof Inet4Address)) {
- LOGGER.fine(address+" is not an IPv4 address");
+ LOGGER.log(Level.FINE, "{0} is not an IPv4 address", address);
continue;
}
if(!ComputerPinger.checkIsReachable(ia, 3)) {
- LOGGER.fine(address+" didn't respond to ping");
+ LOGGER.log(Level.FINE, "{0} didn't respond to ping", address);
continue;
}
cachedHostName = ia.getCanonicalHostName();
@@ -1034,7 +1203,10 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
return cachedHostName;
} catch (IOException e) {
// if a given name fails to parse on this host, we get this error
- LOGGER.log(Level.FINE, "Failed to parse "+address,e);
+ LogRecord lr = new LogRecord(Level.FINE, "Failed to parse {0}");
+ lr.setThrown(e);
+ lr.setParameters(new Object[]{address});
+ LOGGER.log(lr);
}
}
@@ -1058,27 +1230,38 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
}
private static class ListPossibleNames extends MasterToSlaveCallable,IOException> {
+ /**
+ * In the normal case we would use {@link Computer} as the logger's name, however to
+ * do that we would have to send the {@link Computer} class over to the remote classloader
+ * and then it would need to be loaded, which pulls in {@link Jenkins} and loads that
+ * and then that fails to load as you are not supposed to do that. Another option
+ * would be to export the logger over remoting, with increased complexity as a result.
+ * Instead we just use a loger based on this class name and prevent any references to
+ * other classes from being transferred over remoting.
+ */
+ private static final Logger LOGGER = Logger.getLogger(ListPossibleNames.class.getName());
+
public List call() throws IOException {
List names = new ArrayList();
Enumeration nis = NetworkInterface.getNetworkInterfaces();
while (nis.hasMoreElements()) {
NetworkInterface ni = nis.nextElement();
- LOGGER.fine("Listing up IP addresses for "+ni.getDisplayName());
+ LOGGER.log(Level.FINE, "Listing up IP addresses for {0}", ni.getDisplayName());
Enumeration e = ni.getInetAddresses();
while (e.hasMoreElements()) {
InetAddress ia = e.nextElement();
if(ia.isLoopbackAddress()) {
- LOGGER.fine(ia+" is a loopback address");
+ LOGGER.log(Level.FINE, "{0} is a loopback address", ia);
continue;
}
if(!(ia instanceof Inet4Address)) {
- LOGGER.fine(ia+" is not an IPv4 address");
+ LOGGER.log(Level.FINE, "{0} is not an IPv4 address", ia);
continue;
}
- LOGGER.fine(ia+" is a viable candidate");
+ LOGGER.log(Level.FINE, "{0} is a viable candidate", ia);
names.add(ia.getHostAddress());
}
}
@@ -1107,12 +1290,13 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
public void doRssAll( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
rss(req, rsp, " all builds", getBuilds());
}
- public void doRssFailed( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
+
+ public void doRssFailed(StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
rss(req, rsp, " failed builds", getBuilds().failureOnly());
}
private void rss(StaplerRequest req, StaplerResponse rsp, String suffix, RunList runs) throws IOException, ServletException {
- RSS.forwardToRss(getDisplayName()+ suffix, getUrl(),
- runs.newBuilds(), Run.FEED_ADAPTER, req, rsp );
+ RSS.forwardToRss(getDisplayName() + suffix, getUrl(),
+ runs.newBuilds(), Run.FEED_ADAPTER, req, rsp);
}
@RequirePOST
@@ -1190,7 +1374,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
_doScript(req, rsp, "_scriptText.jelly");
}
- protected void _doScript( StaplerRequest req, StaplerResponse rsp, String view) throws IOException, ServletException {
+ protected void _doScript(StaplerRequest req, StaplerResponse rsp, String view) throws IOException, ServletException {
Jenkins._doScript(req, rsp, req.getView(this, view), getChannel(), getACL());
}
@@ -1201,13 +1385,19 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
public void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
checkPermission(CONFIGURE);
- String name = Util.fixEmptyAndTrim(req.getSubmittedForm().getString("name"));
- Jenkins.checkGoodName(name);
+ String proposedName = Util.fixEmptyAndTrim(req.getSubmittedForm().getString("name"));
+ Jenkins.checkGoodName(proposedName);
Node node = getNode();
if (node == null) {
throw new ServletException("No such node " + nodeName);
}
+
+ if ((!proposedName.equals(nodeName))
+ && Jenkins.getActiveInstance().getNode(proposedName) != null) {
+ throw new FormException(Messages.ComputerSet_SlaveAlreadyExists(proposedName), "name");
+ }
+
Node result = node.reconfigure(req, req.getSubmittedForm());
replaceBy(result);
@@ -1246,21 +1436,23 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
/**
* Replaces the current {@link Node} by another one.
*/
- private void replaceBy(Node newNode) throws ServletException, IOException {
+ private void replaceBy(final Node newNode) throws ServletException, IOException {
final Jenkins app = Jenkins.getInstance();
- // replace the old Node object by the new one
- synchronized (app) {
- List nodes = new ArrayList(app.getNodes());
- Node node = getNode();
- int i = (node != null) ? nodes.indexOf(node) : -1;
- if(i<0) {
- throw new IOException("This slave appears to be removed while you were editing the configuration");
+ // use the queue lock until Nodes has a way of directly modifying a single node.
+ Queue.withLock(new NotReallyRoleSensitiveCallable() {
+ public Void call() throws IOException {
+ List nodes = new ArrayList(app.getNodes());
+ Node node = getNode();
+ int i = (node != null) ? nodes.indexOf(node) : -1;
+ if(i<0) {
+ throw new IOException("This slave appears to be removed while you were editing the configuration");
+ }
+ nodes.set(i, newNode);
+ app.setNodes(nodes);
+ return null;
}
-
- nodes.set(i, newNode);
- app.setNodes(nodes);
- }
+ });
}
/**
@@ -1277,7 +1469,6 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
/**
* Really deletes the slave.
*/
- @CLIMethod(name="delete-node")
@RequirePOST
public HttpResponse doDoDelete() throws IOException {
checkPermission(DELETE);
@@ -1314,7 +1505,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
* Handles incremental log.
*/
public void doProgressiveLog( StaplerRequest req, StaplerResponse rsp) throws IOException {
- getLogText().doProgressText(req,rsp);
+ getLogText().doProgressText(req, rsp);
}
/**
@@ -1387,8 +1578,8 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
if (m.matches()) {
File newLocation = new File(dir, "logs/slaves/" + m.group(1) + "/slave.log" + Util.fixNull(m.group(2)));
newLocation.getParentFile().mkdirs();
- boolean relocationSuccessfull=f.renameTo(newLocation);
- if (relocationSuccessfull) { // The operation will fail if mkdir fails
+ boolean relocationSuccessful=f.renameTo(newLocation);
+ if (relocationSuccessful) { // The operation will fail if mkdir fails
LOGGER.log(Level.INFO, "Relocated log file {0} to {1}",new Object[] {f.getPath(),newLocation.getPath()});
} else {
LOGGER.log(Level.WARNING, "Cannot relocate log file {0} to {1}",new Object[] {f.getPath(),newLocation.getPath()});
@@ -1399,6 +1590,110 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
}
}
+ /**
+ * A value class to provide a consistent snapshot view of the state of an executor to avoid race conditions
+ * during rendering of the executors list.
+ *
+ * @since 1.607
+ */
+ @Restricted(NoExternalUse.class)
+ public static class DisplayExecutor implements ModelObject {
+
+ @Nonnull
+ private final String displayName;
+ @Nonnull
+ private final String url;
+ @Nonnull
+ private final Executor executor;
+
+ public DisplayExecutor(@Nonnull String displayName, @Nonnull String url, @Nonnull Executor executor) {
+ this.displayName = displayName;
+ this.url = url;
+ this.executor = executor;
+ }
+
+ @Override
+ @Nonnull
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ @Nonnull
+ public String getUrl() {
+ return url;
+ }
+
+ @Nonnull
+ public Executor getExecutor() {
+ return executor;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DisplayExecutor{");
+ sb.append("displayName='").append(displayName).append('\'');
+ sb.append(", url='").append(url).append('\'');
+ sb.append(", executor=").append(executor);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DisplayExecutor that = (DisplayExecutor) o;
+
+ if (!executor.equals(that.executor)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Extension(ordinal = Double.MAX_VALUE)
+ @Restricted(DoNotUse.class)
+ public static class InternalComputerListener extends ComputerListener {
+ @Override
+ public void onOnline(Computer c, TaskListener listener) throws IOException, InterruptedException {
+ c.cachedEnvironment = null;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return executor.hashCode();
+ }
+ }
+
+ /**
+ * Used to trace requests to terminate a computer.
+ *
+ * @since 1.607
+ */
+ public static class TerminationRequest extends RuntimeException {
+ private final long when;
+ public TerminationRequest(String message) {
+ super(message);
+ this.when = System.currentTimeMillis();
+ }
+
+ /**
+ * Returns the when the termination request was created.
+ *
+ * @return the difference, measured in milliseconds, between
+ * the time of the termination request and midnight, January 1, 1970 UTC.
+ */
+ public long getWhen() {
+ return when;
+ }
+ }
+
public static final PermissionGroup PERMISSIONS = new PermissionGroup(Computer.class,Messages._Computer_Permissions_Title());
public static final Permission CONFIGURE = new Permission(PERMISSIONS,"Configure", Messages._Computer_ConfigurePermission_Description(), Permission.CONFIGURE, PermissionScope.COMPUTER);
/**
diff --git a/core/src/main/java/hudson/model/ComputerPanelBox.java b/core/src/main/java/hudson/model/ComputerPanelBox.java
index f7def3d04f2321cd73a179015d06508c1c593aff..94ee1aaa3ca938aa8412999f65cbae7c2cbf7337 100644
--- a/core/src/main/java/hudson/model/ComputerPanelBox.java
+++ b/core/src/main/java/hudson/model/ComputerPanelBox.java
@@ -4,7 +4,6 @@ import hudson.ExtensionList;
import hudson.ExtensionPoint;
import java.util.ArrayList;
import java.util.List;
-import jenkins.model.Jenkins;
/**
* Adds box rendered in the computer side panel.
diff --git a/core/src/main/java/hudson/model/ComputerPinger.java b/core/src/main/java/hudson/model/ComputerPinger.java
index d924ab63b4fea091b3eeccd7033044f0addb2016..ecf81abceb7a430c81e1bfa61dccd8ce413652cd 100644
--- a/core/src/main/java/hudson/model/ComputerPinger.java
+++ b/core/src/main/java/hudson/model/ComputerPinger.java
@@ -3,7 +3,6 @@ package hudson.model;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
-import jenkins.model.Jenkins;
import java.io.IOException;
import java.net.InetAddress;
diff --git a/core/src/main/java/hudson/model/ComputerSet.java b/core/src/main/java/hudson/model/ComputerSet.java
index 0502c64329059df41e606c956aa8227b3b564e6c..f887167058d74c427d43a886808218d9888b0b53 100644
--- a/core/src/main/java/hudson/model/ComputerSet.java
+++ b/core/src/main/java/hudson/model/ComputerSet.java
@@ -95,6 +95,7 @@ public final class ComputerSet extends AbstractModelObject implements Describabl
* @deprecated as of 1.301
* Use {@link #getMonitors()}.
*/
+ @Deprecated
public static List get_monitors() {
return monitors.toList();
}
@@ -290,6 +291,7 @@ public final class ComputerSet extends AbstractModelObject implements Describabl
JSONObject formData = req.getSubmittedForm();
formData.put("name", fixedName);
+ // TODO type is probably NodeDescriptor.id but confirm
Node result = NodeDescriptor.all().find(type).newInstance(req, formData);
app.addNode(result);
@@ -378,11 +380,6 @@ public final class ComputerSet extends AbstractModelObject implements Describabl
@Extension
public static class DescriptorImpl extends Descriptor {
- @Override
- public String getDisplayName() {
- return "";
- }
-
/**
* Auto-completion for the "copy from" field in the new job page.
*/
diff --git a/core/src/main/java/hudson/model/DependecyDeclarer.java b/core/src/main/java/hudson/model/DependecyDeclarer.java
index 2693515053fb9df67402c24e32a3a0bf7fd27f05..5cd1fe640091f490fad89dd2ed6a88e98a25c161 100644
--- a/core/src/main/java/hudson/model/DependecyDeclarer.java
+++ b/core/src/main/java/hudson/model/DependecyDeclarer.java
@@ -30,4 +30,5 @@ import jenkins.model.DependencyDeclarer;
* @deprecated Use {@link DependencyDeclarer} instead.
* @since 1.160
*/
+@Deprecated
public interface DependecyDeclarer extends DependencyDeclarer {}
diff --git a/core/src/main/java/hudson/model/Descriptor.java b/core/src/main/java/hudson/model/Descriptor.java
index 2b5bcef0cf608b16b8759371d0929c48406e61be..2160d27b177c14139806b6aa44e43264c1e2e8c1 100644
--- a/core/src/main/java/hudson/model/Descriptor.java
+++ b/core/src/main/java/hudson/model/Descriptor.java
@@ -72,6 +72,7 @@ import java.lang.reflect.Type;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.beans.Introspector;
+import java.util.IdentityHashMap;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
@@ -293,8 +294,15 @@ public abstract class Descriptor> implements Saveable {
/**
* Human readable name of this kind of configurable object.
+ * Should be overridden for most descriptors, if the display name is visible somehow.
+ * As a fallback it uses {@link Class#getSimpleName} on {@link #clazz}, so for example {@code MyThing} from {@code some.pkg.MyThing.DescriptorImpl}.
+ * Historically some implementations returned null as a way of hiding the descriptor from the UI,
+ * but this is generally managed by an explicit method such as {@code isEnabled} or {@code isApplicable}.
*/
- public abstract String getDisplayName();
+ @Nonnull
+ public String getDisplayName() {
+ return clazz.getSimpleName();
+ }
/**
* Uniquely identifies this {@link Descriptor} among all the other {@link Descriptor}s.
@@ -366,6 +374,7 @@ public abstract class Descriptor> implements Saveable {
* @deprecated since 1.528
* Use {@link #getCheckMethod(String)}
*/
+ @Deprecated
public String getCheckUrl(String fieldName) {
return getCheckMethod(fieldName).toCheckUrl();
}
@@ -512,6 +521,7 @@ public abstract class Descriptor> implements Saveable {
* Implement {@link #newInstance(StaplerRequest, JSONObject)} method instead.
* Deprecated as of 1.145.
*/
+ @Deprecated
public T newInstance(StaplerRequest req) throws FormException {
throw new UnsupportedOperationException(getClass()+" should implement newInstance(StaplerRequest,JSONObject)");
}
@@ -550,7 +560,7 @@ public abstract class Descriptor> implements Saveable {
* Signals a problem in the submitted form.
* @since 1.145
*/
- public T newInstance(StaplerRequest req, JSONObject formData) throws FormException {
+ public T newInstance(@CheckForNull StaplerRequest req, @Nonnull JSONObject formData) throws FormException {
try {
Method m = getClass().getMethod("newInstance", StaplerRequest.class);
@@ -565,7 +575,20 @@ public abstract class Descriptor> implements Saveable {
}
// new behavior as of 1.206
- return verifyNewInstance(req.bindJSON(clazz,formData));
+ BindInterceptor oldInterceptor = req.getBindInterceptor();
+ try {
+ NewInstanceBindInterceptor interceptor;
+ if (oldInterceptor instanceof NewInstanceBindInterceptor) {
+ interceptor = (NewInstanceBindInterceptor) oldInterceptor;
+ } else {
+ interceptor = new NewInstanceBindInterceptor(oldInterceptor);
+ req.setBindInterceptor(interceptor);
+ }
+ interceptor.processed.put(formData, true);
+ return verifyNewInstance(req.bindJSON(clazz, formData));
+ } finally {
+ req.setBindInterceptor(oldInterceptor);
+ }
}
} catch (NoSuchMethodException e) {
throw new AssertionError(e); // impossible
@@ -578,6 +601,78 @@ public abstract class Descriptor> implements Saveable {
}
}
+ /**
+ * Ensures that calls to {@link StaplerRequest#bindJSON(Class, JSONObject)} from {@link #newInstance(StaplerRequest, JSONObject)} recurse properly.
+ * {@code doConfigSubmit}-like methods will wind up calling {@code newInstance} directly
+ * or via {@link #newInstancesFromHeteroList(StaplerRequest, Object, Collection)},
+ * which consult any custom {@code newInstance} overrides for top-level {@link Describable} objects.
+ * But for nested describable objects Stapler would know nothing about {@code newInstance} without this trick.
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static class NewInstanceBindInterceptor extends BindInterceptor {
+
+ private final BindInterceptor oldInterceptor;
+ private final Map processed = new IdentityHashMap<>();
+
+ NewInstanceBindInterceptor(BindInterceptor oldInterceptor) {
+ LOGGER.log(Level.FINER, "new interceptor delegating to {0}", oldInterceptor);
+ this.oldInterceptor = oldInterceptor;
+ }
+
+ private boolean isApplicable(Class type, JSONObject json) {
+ if (Modifier.isAbstract(type.getModifiers())) {
+ LOGGER.log(Level.FINER, "ignoring abstract {0} {1}", new Object[] {type.getName(), json});
+ return false;
+ }
+ if (!Describable.class.isAssignableFrom(type)) {
+ LOGGER.log(Level.FINER, "ignoring non-Describable {0} {1}", new Object[] {type.getName(), json});
+ return false;
+ }
+ if (Boolean.TRUE.equals(processed.put(json, true))) {
+ LOGGER.log(Level.FINER, "already processed {0} {1}", new Object[] {type.getName(), json});
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public Object instantiate(Class actualType, JSONObject json) {
+ if (isApplicable(actualType, json)) {
+ LOGGER.log(Level.FINE, "switching to newInstance {0} {1}", new Object[] {actualType.getName(), json});
+ try {
+ return Jenkins.getActiveInstance().getDescriptor(actualType).newInstance(Stapler.getCurrentRequest(), json);
+ } catch (Exception x) {
+ LOGGER.log(Level.WARNING, "falling back to default instantiation " + actualType.getName() + " " + json, x);
+ // If nested objects are not using newInstance, bindJSON will wind up throwing the same exception anyway,
+ // so logging above will result in a duplicated stack trace.
+ // However if they *are* then this is the only way to find errors in that newInstance.
+ // Normally oldInterceptor.instantiate will just return DEFAULT, not actually do anything,
+ // so we cannot try calling the default instantiation and then decide which problem to report.
+ }
+ }
+ return oldInterceptor.instantiate(actualType, json);
+ }
+
+ @Override
+ public Object onConvert(Type targetType, Class targetTypeErasure, Object jsonSource) {
+ if (jsonSource instanceof JSONObject) {
+ JSONObject json = (JSONObject) jsonSource;
+ if (isApplicable(targetTypeErasure, json)) {
+ LOGGER.log(Level.FINE, "switching to newInstance {0} {1}", new Object[] {targetTypeErasure.getName(), json});
+ try {
+ return Jenkins.getActiveInstance().getDescriptor(targetTypeErasure).newInstance(Stapler.getCurrentRequest(), json);
+ } catch (Exception x) {
+ LOGGER.log(Level.WARNING, "falling back to default instantiation " + targetTypeErasure.getName() + " " + json, x);
+ }
+ }
+ } else {
+ LOGGER.log(Level.FINER, "ignoring non-object {0}", jsonSource);
+ }
+ return oldInterceptor.onConvert(targetType, targetTypeErasure, jsonSource);
+ }
+
+ }
+
/**
* Look out for a typical error a plugin developer makes.
* See http://hudson.361315.n4.nabble.com/Help-Hint-needed-Post-build-action-doesn-t-stay-activated-td2308833.html
@@ -684,6 +779,7 @@ public abstract class Descriptor> implements Saveable {
* @deprecated
* As of 1.239, use {@link #configure(StaplerRequest, JSONObject)}.
*/
+ @Deprecated
public boolean configure( StaplerRequest req ) throws FormException {
return true;
}
@@ -909,14 +1005,35 @@ public abstract class Descriptor> implements Saveable {
if (formData!=null) {
for (Object o : JSONArray.fromObject(formData)) {
JSONObject jo = (JSONObject)o;
- String kind = jo.optString("$class", null);
- if (kind == null) {
- // Legacy: Remove once plugins have been staged onto $class
- kind = jo.getString("kind");
+ Descriptor d = null;
+ // 'kind' and '$class' are mutually exclusive (see class-entry.jelly), but to be more lenient on the reader side,
+ // we check them both anyway. 'kind' (which maps to ID) is more unique than '$class', which can have multiple matching
+ // Descriptors, so we prefer 'kind' if it's present.
+ String kind = jo.optString("kind", null);
+ if (kind != null) {
+ // Only applies when Descriptor.getId is overridden.
+ // Note that kind is only supported here,
+ // *not* inside the StaplerRequest.bindJSON which is normally called by newInstance
+ // (since Descriptor.newInstance is not itself available to Stapler).
+ // If you merely override getId for some reason, but use @DataBoundConstructor on your Describable,
+ // there is no problem; but you can only rely on newInstance being called at top level.
+ d = findById(descriptors, kind);
+ }
+ if (d == null) {
+ kind = jo.optString("$class");
+ if (kind != null) { // else we will fall through to the warning
+ // This is the normal case.
+ d = findByDescribableClassName(descriptors, kind);
+ if (d == null) {
+ // Deprecated system where stapler-class was the Descriptor class name (rather than Describable class name).
+ d = findByClassName(descriptors, kind);
+ }
+ }
}
- Descriptor d = find(descriptors, kind);
if (d != null) {
items.add(d.newInstance(req, jo));
+ } else {
+ LOGGER.log(Level.WARNING, "Received unexpected form data element: {0}", jo);
}
}
}
@@ -925,22 +1042,58 @@ public abstract class Descriptor> implements Saveable {
}
/**
- * Finds a descriptor from a collection by its class name.
+ * Finds a descriptor from a collection by its ID.
+ * @param id should match {@link #getId}
+ * @since 1.610
+ */
+ public static @CheckForNull T findById(Collection extends T> list, String id) {
+ for (T d : list) {
+ if(d.getId().equals(id))
+ return d;
+ }
+ return null;
+ }
+
+ /**
+ * Finds a descriptor from a collection by the class name of the {@link Descriptor}.
+ * This is useless as of the introduction of {@link #getId} and so only very old compatibility code needs it.
*/
- public static @CheckForNull T find(Collection extends T> list, String className) {
+ private static @CheckForNull T findByClassName(Collection extends T> list, String className) {
for (T d : list) {
if(d.getClass().getName().equals(className))
return d;
}
- // Since we introduced Descriptor.getId(), it is a preferred method of identifying descriptor by a string.
- // To make that migration easier without breaking compatibility, let's also match up with the id.
+ return null;
+ }
+
+ /**
+ * Finds a descriptor from a collection by the class name of the {@link Describable} it describes.
+ * @param className should match {@link Class#getName} of a {@link #clazz}
+ * @since 1.610
+ */
+ public static @CheckForNull T findByDescribableClassName(Collection extends T> list, String className) {
for (T d : list) {
- if(d.getId().equals(className))
+ if(d.clazz.getName().equals(className))
return d;
}
return null;
}
+ /**
+ * Finds a descriptor from a collection by its class name or ID.
+ * @deprecated choose between {@link #findById} or {@link #findByDescribableClassName}
+ */
+ public static @CheckForNull T find(Collection extends T> list, String string) {
+ T d = findByClassName(list, string);
+ if (d != null) {
+ return d;
+ }
+ return findById(list, string);
+ }
+
+ /**
+ * @deprecated choose between {@link #findById} or {@link #findByDescribableClassName}
+ */
public static @CheckForNull Descriptor find(String className) {
return find(ExtensionList.lookup(Descriptor.class),className);
}
diff --git a/core/src/main/java/hudson/model/DescriptorVisibilityFilter.java b/core/src/main/java/hudson/model/DescriptorVisibilityFilter.java
index 0a435c6fda0777b070e0b3b24814767bbd800fe1..bd5cbfd36ebd0649c0f115a50c60946a59e3eb2f 100644
--- a/core/src/main/java/hudson/model/DescriptorVisibilityFilter.java
+++ b/core/src/main/java/hudson/model/DescriptorVisibilityFilter.java
@@ -4,7 +4,6 @@ import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.scm.SCMDescriptor;
import jenkins.ExtensionFilter;
-import jenkins.model.Jenkins;
import java.util.ArrayList;
import java.util.List;
diff --git a/core/src/main/java/hudson/model/DirectlyModifiableView.java b/core/src/main/java/hudson/model/DirectlyModifiableView.java
index bdc651341b59219520c6ebe8baaa277aab34aa52..b799d2bd9e978d97b06b21f62aca9790deec7b90 100644
--- a/core/src/main/java/hudson/model/DirectlyModifiableView.java
+++ b/core/src/main/java/hudson/model/DirectlyModifiableView.java
@@ -23,7 +23,6 @@
*/
package hudson.model;
-import hudson.util.HttpResponses;
import java.io.IOException;
diff --git a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
index cf803fcdc5f8e4648a7aaef5779818024eb15609..8be60142d09d60c29fabe4e57881e50b611bfcf8 100644
--- a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
+++ b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
@@ -47,6 +47,8 @@ import jenkins.util.VirtualFile;
import org.apache.commons.io.IOUtils;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
@@ -75,6 +77,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
* @deprecated as of 1.297
* Use {@link #DirectoryBrowserSupport(ModelObject, FilePath, String, String, boolean)}
*/
+ @Deprecated
public DirectoryBrowserSupport(ModelObject owner, String title) {
this(owner, (VirtualFile) null, title, null, false);
}
@@ -147,6 +150,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
* Instead of calling this method explicitly, just return the {@link DirectoryBrowserSupport} object
* from the {@code doXYZ} method and let Stapler generate a response for you.
*/
+ @Deprecated
public void serveFile(StaplerRequest req, StaplerResponse rsp, FilePath root, String icon, boolean serveDirIndex) throws IOException, ServletException, InterruptedException {
serveFile(req, rsp, root.toVirtualFile(), icon, serveDirIndex);
}
@@ -307,6 +311,17 @@ public final class DirectoryBrowserSupport implements HttpResponse {
// pseudo file name to let the Stapler set text/plain
rsp.serveFile(req, in, lastModified, -1, length, "plain.txt");
} else {
+ String csp = System.getProperty(DirectoryBrowserSupport.class.getName() + ".CSP");
+ if (csp == null) {
+ // default value unless overridden with system property
+ csp = DEFAULT_CSP_VALUE;
+ }
+ if (!csp.trim().equals("")) {
+ // allow users to prevent sending this header by setting empty system property
+ for (String header : new String[]{"Content-Security-Policy", "X-WebKit-CSP", "X-Content-Security-Policy"}) {
+ rsp.setHeader(header, csp);
+ }
+ }
rsp.serveFile(req, in, lastModified, -1, length, baseFile.getName() );
}
}
@@ -574,4 +589,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
private static final Logger LOGGER = Logger.getLogger(DirectoryBrowserSupport.class.getName());
+
+ @Restricted(NoExternalUse.class)
+ public static final String DEFAULT_CSP_VALUE = "sandbox; default-src 'none'; img-src 'self'; style-src 'self';";
}
diff --git a/core/src/main/java/hudson/model/DownloadService.java b/core/src/main/java/hudson/model/DownloadService.java
index 707491dbc18939c70ec4f5d107e1b4f0c9625436..0bbf7a87542a48647cf243c06e5781f39fedd909 100644
--- a/core/src/main/java/hudson/model/DownloadService.java
+++ b/core/src/main/java/hudson/model/DownloadService.java
@@ -25,8 +25,11 @@ package hudson.model;
import hudson.Extension;
import hudson.ExtensionList;
+import hudson.ExtensionListListener;
import hudson.ExtensionPoint;
import hudson.ProxyConfiguration;
+import hudson.init.InitMilestone;
+import hudson.init.Initializer;
import hudson.util.FormValidation;
import hudson.util.FormValidation.Kind;
import hudson.util.QuotedStringTokenizer;
@@ -35,8 +38,12 @@ import static hudson.util.TimeUnit2.DAYS;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.DownloadSettings;
import jenkins.model.Jenkins;
@@ -66,7 +73,7 @@ public class DownloadService extends PageDecorator {
* Builds up an HTML fragment that starts all the download jobs.
*/
public String generateFragment() {
- if (!DownloadSettings.get().isUseBrowser()) {
+ if (!DownloadSettings.usePostBack()) {
return "";
}
if (neverUpdate) return "";
@@ -170,6 +177,69 @@ public class DownloadService extends PageDecorator {
}
}
+ /**
+ * Loads JSON from a JSON-with-{@code postMessage} URL.
+ * @param src a URL to a JSON HTML file (typically including {@code id} and {@code version} query parameters)
+ * @return the embedded JSON text
+ * @throws IOException if either downloading or processing failed
+ */
+ @Restricted(NoExternalUse.class)
+ public static String loadJSONHTML(URL src) throws IOException {
+ InputStream is = ProxyConfiguration.open(src).getInputStream();
+ try {
+ String jsonp = IOUtils.toString(is, "UTF-8");
+ String preamble = "window.parent.postMessage(JSON.stringify(";
+ int start = jsonp.indexOf(preamble);
+ int end = jsonp.lastIndexOf("),'*');");
+ if (start >= 0 && end > start) {
+ return jsonp.substring(start + preamble.length(), end).trim();
+ } else {
+ throw new IOException("Could not find JSON in " + src);
+ }
+ } finally {
+ is.close();
+ }
+ }
+
+ /**
+ * This installs itself as a listener to changes to the Downloadable extension list and will download the metadata
+ * for any newly added Downloadables.
+ */
+ @Restricted(NoExternalUse.class)
+ public static class DownloadableListener extends ExtensionListListener {
+
+ /**
+ * Install this listener to the Downloadable extension list after all extensions have been loaded; we only
+ * care about those that are added after initialization
+ */
+ @Initializer(after = InitMilestone.EXTENSIONS_AUGMENTED)
+ public static void installListener() {
+ ExtensionList.lookup(Downloadable.class).addListener(new DownloadableListener());
+ }
+
+ /**
+ * Look for Downloadables that have no data, and update them.
+ */
+ @Override
+ public void onChange() {
+ for (Downloadable d : Downloadable.all()) {
+ TextFile f = d.getDataFile();
+ if (f == null || !f.exists()) {
+ LOGGER.log(Level.FINE, "Updating metadata for " + d.getId());
+ try {
+ d.updateNow();
+ } catch (IOException e) {
+ LOGGER.log(Level.WARNING, "Failed to update metadata for " + d.getId(), e);
+ }
+ } else {
+ LOGGER.log(Level.FINER, "Skipping update of metadata for " + d.getId());
+ }
+ }
+ }
+
+ private static final Logger LOGGER = Logger.getLogger(DownloadableListener.class.getName());
+ }
+
/**
* Represents a periodically updated JSON data file obtained from a remote URL.
*
@@ -234,6 +304,24 @@ public class DownloadService extends PageDecorator {
return Jenkins.getInstance().getUpdateCenter().getDefaultBaseUrl()+"updates/"+url;
}
+ /**
+ * URLs to download from.
+ */
+ public List getUrls() {
+ List updateSites = new ArrayList();
+ for (UpdateSite site : Jenkins.getActiveInstance().getUpdateCenter().getSiteList()) {
+ String siteUrl = site.getUrl();
+ int baseUrlEnd = siteUrl.indexOf("update-center.json");
+ if (baseUrlEnd != -1) {
+ String siteBaseUrl = siteUrl.substring(0, baseUrlEnd);
+ updateSites.add(siteBaseUrl + "updates/" + url);
+ } else {
+ LOGGER.log(Level.WARNING, "Url {0} does not look like an update center:", siteUrl);
+ }
+ }
+ return updateSites;
+ }
+
/**
* How often do we retrieve the new image?
*
@@ -283,9 +371,7 @@ public class DownloadService extends PageDecorator {
* This is where the browser sends us the data.
*/
public void doPostBack(StaplerRequest req, StaplerResponse rsp) throws IOException {
- if (!DownloadSettings.get().isUseBrowser()) {
- throw new IOException("not allowed");
- }
+ DownloadSettings.checkPostBackAccess();
long dataTimestamp = System.currentTimeMillis();
due = dataTimestamp+getInterval(); // success or fail, don't try too often
@@ -299,15 +385,6 @@ public class DownloadService extends PageDecorator {
}
private FormValidation load(String json, long dataTimestamp) throws IOException {
- JSONObject o = JSONObject.fromObject(json);
-
- if (signatureCheck) {
- FormValidation e = new JSONSignatureValidator("downloadable '"+id+"'").verifySignature(o);
- if (e.kind!= Kind.OK) {
- return e;
- }
- }
-
TextFile df = getDataFile();
df.write(json);
df.file.setLastModified(dataTimestamp);
@@ -317,7 +394,72 @@ public class DownloadService extends PageDecorator {
@Restricted(NoExternalUse.class)
public FormValidation updateNow() throws IOException {
- return load(loadJSON(new URL(getUrl() + "?id=" + URLEncoder.encode(getId(), "UTF-8") + "&version=" + URLEncoder.encode(Jenkins.VERSION, "UTF-8"))), System.currentTimeMillis());
+ List jsonList = new ArrayList<>();
+ for (String site : getUrls()) {
+ String jsonString;
+ try {
+ jsonString = loadJSONHTML(new URL(site + ".html?id=" + URLEncoder.encode(getId(), "UTF-8") + "&version=" + URLEncoder.encode(Jenkins.VERSION, "UTF-8")));
+ } catch (Exception e) {
+ LOGGER.log(Level.WARNING, "Could not load json from " + site, e );
+ continue;
+ }
+ JSONObject o = JSONObject.fromObject(jsonString);
+ if (signatureCheck) {
+ FormValidation e = new JSONSignatureValidator("downloadable '"+id+"'").verifySignature(o);
+ if (e.kind!= Kind.OK) {
+ continue;
+ }
+ }
+ jsonList.add(o);
+ }
+ if (jsonList.size() == 0) {
+ return FormValidation.warning("None of the Update Sites passed the signature check");
+ }
+ JSONObject reducedJson = reduce(jsonList);
+ return load(reducedJson.toString(), System.currentTimeMillis());
+ }
+
+ /**
+ * Function that takes multiple JSONObjects and returns a single one.
+ * @param jsonList to be processed
+ * @return a single JSONObject
+ */
+ public JSONObject reduce(List jsonList) {
+ return jsonList.get(0);
+ }
+
+ /**
+ * check if the list of update center entries has duplicates
+ * @param genericList list of entries coming from multiple update centers
+ * @param comparator the unique ID of an entry
+ * @param the generic class
+ * @return true if the list has duplicates, false otherwise
+ */
+ public static boolean hasDuplicates (List genericList, String comparator) {
+ if (genericList.isEmpty()) {
+ return false;
+ }
+ Field field;
+ try {
+ field = genericList.get(0).getClass().getDeclaredField(comparator);
+ } catch (NoSuchFieldException e) {
+ LOGGER.warning("comparator: " + comparator + "does not exist for " + genericList.get(0).getClass() + ", " + e);
+ return false;
+ }
+ for (int i = 0; i < genericList.size(); i ++ ) {
+ T data1 = genericList.get(i);
+ for (int j = i + 1; j < genericList.size(); j ++ ) {
+ T data2 = genericList.get(j);
+ try {
+ if (field.get(data1).equals(field.get(data2))) {
+ return true;
+ }
+ } catch (IllegalAccessException e) {
+ LOGGER.warning("could not access field: " + comparator + ", " + e);
+ }
+ }
+ }
+ return false;
}
/**
diff --git a/core/src/main/java/hudson/model/EnvironmentContributor.java b/core/src/main/java/hudson/model/EnvironmentContributor.java
index d6d630bcb56c47eab192fed8f09a078255757301..77ea159b309b3efa7fcd55a81b811ffaf8e5f4b3 100644
--- a/core/src/main/java/hudson/model/EnvironmentContributor.java
+++ b/core/src/main/java/hudson/model/EnvironmentContributor.java
@@ -28,7 +28,6 @@ import hudson.Extension;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.scm.SCM;
-import jenkins.model.Jenkins;
import java.io.IOException;
import javax.annotation.Nonnull;
diff --git a/core/src/main/java/hudson/model/Executor.java b/core/src/main/java/hudson/model/Executor.java
index 574cfb748608382d4c1b4121baa6b3712482b75b..817166c60ef6e7e8db54252d58cb5532014d4315 100644
--- a/core/src/main/java/hudson/model/Executor.java
+++ b/core/src/main/java/hudson/model/Executor.java
@@ -46,34 +46,45 @@ import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.interceptor.RequirePOST;
+import javax.annotation.concurrent.GuardedBy;
import javax.servlet.ServletException;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Vector;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import static hudson.model.queue.Executables.*;
+import java.util.Collection;
import static java.util.logging.Level.*;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
+import jenkins.model.queue.AsynchronousExecution;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
* Thread that executes builds.
* Since 1.536, {@link Executor}s start threads on-demand.
- * The entire logic should use {@link #isActive()} instead of {@link #isAlive()}
- * in order to check if the {@link Executor} it ready to take tasks.
+ *
Callers should use {@link #isActive()} instead of {@link #isAlive()}.
* @author Kohsuke Kawaguchi
*/
@ExportedBean
public class Executor extends Thread implements ModelObject {
protected final @Nonnull Computer owner;
private final Queue queue;
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+ @GuardedBy("lock")
private long startTime;
/**
* Used to track when a job was last executed.
@@ -87,29 +98,41 @@ public class Executor extends Thread implements ModelObject {
/**
* {@link hudson.model.Queue.Executable} being executed right now, or null if the executor is idle.
*/
- private volatile Queue.Executable executable;
+ @GuardedBy("lock")
+ private Queue.Executable executable;
+
+ /**
+ * Used to mark that the execution is continuing asynchronously even though {@link Executor} as {@link Thread}
+ * has finished.
+ */
+ @GuardedBy("lock")
+ private AsynchronousExecution asynchronousExecution;
/**
* When {@link Queue} allocates a work for this executor, this field is set
* and the executor is {@linkplain Thread#start() started}.
*/
- private volatile WorkUnit workUnit;
+ @GuardedBy("lock")
+ private WorkUnit workUnit;
private Throwable causeOfDeath;
private boolean induceDeath;
- private volatile boolean started;
+ @GuardedBy("lock")
+ private boolean started;
/**
* When the executor is interrupted, we allow the code that interrupted the thread to override the
* result code it prefers.
*/
+ @GuardedBy("lock")
private Result interruptStatus;
/**
* Cause of interruption. Access needs to be synchronized.
*/
+ @GuardedBy("lock")
private final List causes = new Vector