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

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