Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-19508

Replace @Extension(optional=true) and optional dependencies with bridge plugins

      Optional dependencies are extremely fragile.

      You must take care to mark each @Extension pertaining to the dependency with optional=true. But this only actually works if a NoClassDefFoundError would be thrown when trying to load the extension with the dependency missing. Sometimes this happens reliably, for example if the extension point is defined in that dependency, but in other cases it only happens incidentally via something like a static initializer. That means that a seemingly minor refactoring can cause the extension to suddenly start getting loaded even when the dependency is missing, causing serious linkage errors later on. http://release-notes.cloudbees.com/release/Folders/3.11 is an example. Even in the best of cases, a missing dependency leads to a warning in the log file which can be alarming to a user.

      Another problem with optional dependencies is testing: JenkinsRule-based functional tests have the dependency enabled, so you are never testing the lack of the dependency. If the code does dynamic checks for the existence of the dependency using Jenkins.getInstance().getPlugin("shortName"), one code branch is not fully covered.

      There is also an inherent asymmetry. If plugins A and B need to interact, should A have an optional dependency on B, or should B have an optional dependency on A? The decision is often political, i.e. if A is more widely used or "fundamental" then probably B will be given the dependency on A. But in many cases the direction is not at all clear, e.g. interactions with Job Config History, Parameterized Trigger, etc. Sometimes the dependency is simply added to the more actively maintained plugin. The problem gets murkier when one of the plugins becomes deprecated, as in https://github.com/jenkinsci/join-plugin/pull/6—should the dependency be removed to "clean house", even though this would remove functionality for the handful of users still running the deprecated plugin?

      Since creating and publishing plugins is pretty cheap, it would make more sense to me to factor out such an integration between two plugins into a third "bridge" plugin with hard dependencies on each (and an extension point implementation providing the integration). This avoids the need for any conditional branches, optional=true, or asymmetry. The only thing missing is a way for users to be guided to install the bridge plugin. Perhaps one or both of the main plugins could include a manifest header saying that they "recommend" the bridge (in case the other main plugin is also enabled). The update center UI would automatically schedule the installation of the bridge if the user requested both of the main plugins to be installed (or already had one installed and requested the other). Compare "eager" modules in NetBeans: http://bits.netbeans.org/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/api.html#enablement

          [JENKINS-19508] Replace @Extension(optional=true) and optional dependencies with bridge plugins

          Code changed in jenkins
          User: Jesse Glick
          Path:
          src/main/java/com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider.java
          http://jenkins-ci.org/commit/cloudbees-folder-plugin/acf46b49786d501416b1bb1b306e5692bdc3ea45
          Log:
          Yet another JENKINS-19508 workaround.

          SCM/JIRA link daemon added a comment - Code changed in jenkins User: Jesse Glick Path: src/main/java/com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider.java http://jenkins-ci.org/commit/cloudbees-folder-plugin/acf46b49786d501416b1bb1b306e5692bdc3ea45 Log: Yet another JENKINS-19508 workaround.

          Jesse Glick added a comment -

          Another problem is that an optional extension which is initially broken due to a missing optional dependency does not get added when that dependency is installed—you have to restart Jenkins.

          Jesse Glick added a comment - Another problem is that an optional extension which is initially broken due to a missing optional dependency does not get added when that dependency is installed—you have to restart Jenkins.

          Code changed in jenkins
          User: Cyrille Le Clerc
          Path:
          jenkins-plugin/pom.xml
          jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/listeners/DownstreamPipelineTriggerRunListener.java
          http://jenkins-ci.org/commit/pipeline-maven-plugin/f12c104daa1464b8e759a50b1c2e5aaa441e1516
          Log:
          Merge pull request #83 from jglick/deps

          JENKINS-19508 Eliminate some optional deps

          Compare: https://github.com/jenkinsci/pipeline-maven-plugin/compare/895db724818f...f12c104daa14

          SCM/JIRA link daemon added a comment - Code changed in jenkins User: Cyrille Le Clerc Path: jenkins-plugin/pom.xml jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/listeners/DownstreamPipelineTriggerRunListener.java http://jenkins-ci.org/commit/pipeline-maven-plugin/f12c104daa1464b8e759a50b1c2e5aaa441e1516 Log: Merge pull request #83 from jglick/deps JENKINS-19508 Eliminate some optional deps Compare: https://github.com/jenkinsci/pipeline-maven-plugin/compare/895db724818f...f12c104daa14

          James Nord added a comment -

          James Nord added a comment - FYI: now possible with RealJenkinsRule.omitPLugins(String...)

            Unassigned Unassigned
            jglick Jesse Glick
            Votes:
            2 Vote for this issue
            Watchers:
            10 Start watching this issue

              Created:
              Updated: