Deprecating Extension.optional#11201
Conversation
| * @author Basil Crow | ||
| */ | ||
| @Restricted(NoExternalUse.class) | ||
| @Extension(optional = true) |
There was a problem hiding this comment.
Unclear why this was “optional”. Of course this would only make sense on servers with systemd. But it would get loaded as an extension in all cases.
| * @since 1.254 | ||
| */ | ||
| public abstract class Lifecycle implements ExtensionPoint { | ||
| public abstract class Lifecycle { |
There was a problem hiding this comment.
And Lifecycle does not even use the extension loader to begin with! Traditionally ExtensionPoint was just used as a very hand-wavy indicator for “things you might want to pay attention to in Javadoc as a plugin author”, rather than specifically meaning “a type which can be subtyped and marked with @Extension to register”. (A broad category of such mistakes is placing ExtensionPoint on a Describable. It is the Descriptor which is the extension point.)
There was a problem hiding this comment.
Traditionally
ExtensionPointwas just used as a very hand-wavy indicator for “things you might want to pay attention to in Javadoc as a plugin author”, rather than specifically meaning “a type which can be subtyped and marked with@Extensionto register”.
Isn't that an indicator this should stay?
See also class Javadoc for PluginServletFilter in which this quirk is made explicit.
There was a problem hiding this comment.
jenkins/core/src/main/java/hudson/util/PluginServletFilter.java
Lines 57 to 58 in c9eaa5d
There was a problem hiding this comment.
The point stands, it's not just tradition, but also documented as such.
There was a problem hiding this comment.
Then we can correct the documentation: #11210
There was a problem hiding this comment.
If the idea is that ExtensionPoint is only for things intended to be @Extension'ed, then
jenkins/core/src/main/java/hudson/ExtensionPoint.java
Lines 35 to 41 in c97fecc
PluginServletFilter), only adding a mention of @Extension.
| * @author Kohsuke Kawaguchi | ||
| */ | ||
| @Extension(optional = true) @Symbol("hsErrPid") | ||
| // TODO why would an extension using a built-in extension point need to be marked optional? |
timja
left a comment
There was a problem hiding this comment.
/label ready-for-merge
This PR is now ready for merge, after ~24 hours, we will merge it if there's no negative feedback.
Thanks!
Plugin developers often grab for this flag because it is easy to find, but it is almost always the wrong choice. It means the extension loader will still try to load the extension; it will just be less noisy (still printing a warning to the log, one line rather than a stack trace!) if it fails. And it is actually tricky to get it to fail. You have to arrange for the initialization of the extension class (and perhaps also the constructor, I do not recall) to fail. So if your plugin has an
optionaldep on a pluginxxx-apiand you writethen that is fine, because types from
xxx-apiare required in the signature ofMyImpl, so theNoClassDefFoundErrorwill be caught by the extension loader and this extension will be skipped. But people often try to do something like the following:This will be quietly registered even when
xxx-apiis disabled—and then throwNoClassDefFoundErrors every time it is used! That is because the HotSpot linker is lazy: even though some method bodies refer to an inaccessible type, they are not linked until the first time the method is run.You can use
optional = truecorrectly if you think carefully about what you are doing, and write tests withRealJenkinsRule.omitPluginsto prove that it behaves as expected. But you might as well just use@OptionalExtensionwhich is much easier to reason about (and avoids the one-line warning to boot).Testing done
None.
Proposed changelog entries
Extension.optionalattribute.Proposed changelog category
/label developer
Proposed upgrade guidelines
N/A
Maintainer checklist
upgrade-guide-neededlabel is set and there is a Proposed upgrade guidelines section in the pull request title (see example).lts-candidateto be considered (see query).