JSR-330 component model for plugins (aka Smoothie).
Smoothie is a replacement for the classic Hudson plugin strategy, and extension finding mechanism, that allows Hudson components to be implemented using JSR-330 annotations and injection.
This integration is done via standard documented Hudson features:
A smoothie component is similar to a standard Hudson extension, except that instead of being adorned by @hudson.Extension is adorned by an annotation which is adorned by @javax.inject.Qualifier, such as @javax.inject.Named.
Smoothie components can live along-side standard Hudson extensions. Any Hudson hudson.ExtensionPoint may be defined as a Smoothie component. Standard extensions can discover Smoothie components just as they would for their standard counter-parts.
The significant difference, aside for the annotation semantics, is that Smoothie components are created by the container, where possible, and will be subject to JSR-330 injection. Some components are not able to be created by the container, for cases that uses Stapler for construction, and will not be able to use constructor-injection, but will benefit from member injection after the instance has been constructed.
To implement a new link on the Manage Hudson page, a plugin needs to define a sub-class of hudson.model.ManagementLink.
In standard Hudson, this could look like:
The Smoothie equivalent component would look like:
The major advantage to the Smoothie version of this component, is that if the implementation depends on other components, those components can be injected dynamically, instead of having to use the static-singleton-lookup method which is all that is available to standard extensions.
When starting the augmented hudson.war, configure the strategy (This is the default as of 2.0.0)
The DelegatingPluginStrategy here might look odd here. But what it does is provide a simple delegate model for the real strategy instance that will be used and uses the container to construct the delegate instance.
Code up your components, using @Named instead of @Extension.
For some types, where the container can not figure out what extension type you are trying to declare, additional @javax.enterprise.inject.Typed annotation is required.
For example, using Hudson's common Descriptor/Describable pattern requires that the Descriptor implementation be explicitly typed:
When using the Smoothie plugin strategy, other non-smoothie extensions/plugins should function 100% the same as they did with the classic plugin strategy.
So... any existing plugin can work in a smoothie-enabled Hudson, but smoothie-enabled plugins will not work in a standard Hudson.
Describable instances created by Descriptor components are not directly constructed by the container. They can be constructed via new or by using Stapler's @DataBoundConstructor facility. In any case, Smoothie will perform member injection after the instance is created, or deserialized from disk.
Deserialization from disk works with the standard XStream instances that Hudson uses, such as:
Hudson's @Extension provides an ordinal member to indicate the sorting-order for extension points of the same type when used in a list context. Smoothie provides the @Priority annotation which provides the same mechanism.
The following are equivalent with respect to sorting order:
Some components instances currently must be created outside of the container, such as Describable instances created via Stapler using @DataBoundConstructor. These instances can not have constructor-based injection, but will instead receive member injection via the magical Injectomatic component.
Allows any object to become aware of the Injectomatic component when its instance is created, regardless of how the instance was created (by the container or otherwise).
Allows any object to receive member injection after the instance was created, regardless of how the instance was created (by the container or otherwise).
This is where the magic for handling injection on deserialization occurs. The default set of XStream instances are automatically registered. To add inject-on-deserialize behavior for custom streams, register the stream with this handler.
Aspects are required currently to implement a few of the more magically features, like post Describable instance creation member injection. Use of aspects is limited ATM, but as a side-effect, all plugins (Smoothie or classic) are subject to load-time-weaving.
The performance impact is negligible, costing a few parts of a second more when the plugin is loaded.