Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Sisu/PlexusMigration"

m
(Replaced content with "This page has been moved to == https://github.com/eclipse/sisu.plexus/wiki/Plexus-to-JSR330 ==")
 
(4 intermediate revisions by 2 users not shown)
Line 1: Line 1:
<h1>Overview</h1>
+
This page has been moved to
<p>This document provides some details on how to convert legacy Plexus components into modern JSR-330 components.</p>
+
== https://github.com/eclipse/sisu.plexus/wiki/Plexus-to-JSR330 ==
 
+
<h2>Annotations</h2>
+
<p>For brevity of examples imports are omitted. &nbsp;The following table defines the meanings and fully-qualified-class-names of the annotations references in the following examples.</p>
+
 
+
<table border="1" style="border-collapse:collapse;padding:5px 10px;color:black;" cellspacing="0" cellpadding="5">
+
<tr>
+
<th>Annotation</th>
+
<th>Class</th>
+
<th>Description</th></tr>
+
<tr>
+
<td colspan="1"><code>@Component</code></td>
+
<td colspan="1">org.codehaus.plexus.component.annotations.Component</td>
+
<td colspan="1">Legacy Plexus component annotation</td></tr>
+
<tr>
+
<td colspan="1"><code>@Requirement</code></td>
+
<td colspan="1">org.codehaus.plexus.component.annotations.Requirement</td>
+
<td colspan="1">Legacy Plexus injection annotation</td></tr>
+
<tr>
+
<td colspan="1"><code>@Configuration</code></td>
+
<td colspan="1">org.codehaus.plexus.component.annotations.Configuration</td>
+
<td colspan="1">Legacy Plexus configuration annotation</td></tr>
+
<tr>
+
<td colspan="1"><code>@Named</code></td>
+
<td colspan="1">javax.inject.Named</td>
+
<td colspan="1">Standard JSR-330 annotation to provide component name</td></tr>
+
<tr>
+
<td colspan="1"><code>@Singleton</code></td>
+
<td colspan="1"><span>javax.inject.Singleton</span></td>
+
<td colspan="1">Standard JSR-330 annotation to mark component as singleton</td></tr>
+
<tr>
+
<td colspan="1"><code>@Typed</code></td>
+
<td colspan="1">javax.enterprise.inject.Typed</td>
+
<td colspan="1">JavaEE annotation to mark component type</td></tr>
+
<tr>
+
<td colspan="1"><code>@Description</code></td>
+
<td colspan="1">org.sonatype.inject.Description</td>
+
<td colspan="1">Sisu-specific annotation to provide a description for a component</td></tr>
+
<tr>
+
<td colspan="1"><code>@Parameters</code></td>
+
<td colspan="1">org.sonatype.inject.Parameters</td>
+
<td colspan="1">Sisu-specific annotation to mark <code>Map&lt;String,String&gt;</code> injection as container context parameters.</td></tr>
+
<tr>
+
<td colspan="1"><code>@Inject</code></td>
+
<td colspan="1">javax.inject.Inject</td>
+
<td colspan="1">Standard JSR-330 annotation to mark field, parameter, method for injection</td></tr>
+
<tr>
+
<td colspan="1"><code>@Nullable</code></td>
+
<td colspan="1">javax.annotation.Nullable</td>
+
<td colspan="1">Standard JSR-305 annotation to mark field, parameter, result value as potentially returning null value</td></tr>
+
</table>
+
 
+
{{Warning|
+
; javax.inject vs. com.google.inject
+
<p><span>There are </span><code>com.google.inject</code><span> flavors of </span><code>@Inject</code><span>, </span><code>@Named</code><span> and </span><code>@Singleton</code> which should <strong>NOT</strong> be used.</p>
+
Prefer the standard <code>javax.inject</code> versions.}}
+
 
+
<h2>References</h2>
+
<p>http://www.jcp.org/en/jsr/detail?id=330</p>
+
<p>http://www.jcp.org/en/jsr/detail?id=305</p>
+
 
+
<h1>@Component</h1>
+
<p>Plexus&nbsp;<code>@Component</code> annotations are replaced by standard&nbsp;<code>@Named</code>,&nbsp;<code>@Singleton</code>, etc annotations.</p>
+
<h3>Singletons</h3>
+
<p>For example this Plexus component:</p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my")
+
public class MyComponent
+
    implements Component
+
{
+
}</syntaxhighlight>
+
 
+
<p>can be converted to:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
}</syntaxhighlight>
+
 
+
{{Message|
+
; Naming Varients
+
<p>Components which are not directly looked up by names, or otherwise used in a context where the name is important you can omit the value for <code>@Named</code> and the full-qualified-class-name of the component will be used as the name instead.</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
package components;
+
+
@Named
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
}</syntaxhighlight>
+
 
+
<p>Results in a component with implicit name of &quot;<code>components.MyComponent</code>&quot;</p>
+
<p>Additionally the Sisu Plexus integration has special handling for Plexus <em>default</em> components. Many Plexus applications define objects like:</p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class)
+
public class DefaultComponent
+
    implements Component
+
{
+
}</syntaxhighlight>
+
 
+
<p>The <code>Default</code> prefix on the component implementation class causes the binding to be translated to <code>@Named(&quot;default&quot;)</code>, so the conversion would look like:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
package components;
+
+
@Named
+
@Singleton
+
public class DefaultComponent
+
    implements Component
+
{
+
}</syntaxhighlight>
+
<p></p>
+
<strong>BUT</strong> the name bound for this component would be &quot;<code>default</code>&quot;.}}
+
 
+
<h3>Instance</h3>
+
<p>By default in Plexus, components are singletons, but this is not the case for every component. &nbsp;This Plexus component is not a singleton:</p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my", instantiationStrategy="per-lookup")
+
public class MyComponent
+
    implements Component
+
{
+
}</syntaxhighlight>
+
 
+
<p>and is converted to:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named("my")
+
public class MyComponent
+
    implements Component
+
{
+
}</syntaxhighlight>
+
 
+
<p>Notice this is the same as the example above, except with-out the&nbsp;<code>@Singleton</code> annotation.</p>
+
 
+
{{Warning|
+
<p>Only <code>per-lookup</code> and <code>singleton</code> instantiation strategies have reasonable mappings into Sisu. The <code>keep-alive</code> and <code>poolable</code> strategies are not supported.</p>
+
Additionally other Plexus-specific component configuration such as lifecycle-handlers, factories, composer, configurator, alias, version, profile, isolatedRealm are <strong>NOT</strong> supported.}}
+
 
+
<h3>Type Override</h3>
+
<p>By default the type of the component is determined automatically, though in some rare cases an explicit type is required. &nbsp;To specify the explicit binding type use the&nbsp;<code>@Typed</code> annotation:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Typed(Component.class)
+
public class MyComponent
+
    extends SomeSupportClassHardToGuessTypeFrom
+
{
+
}</syntaxhighlight>
+
 
+
<h3>Descriptions</h3>
+
<p>In some cases component descriptions are required. &nbsp;There is no standard annotation to provide this, however Sisu provides a custom annotation for this.</p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my", description="My custom component")
+
public class MyComponent
+
    implements Component
+
{
+
}</syntaxhighlight>
+
 
+
<p>becomes:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
@Description("My custom component")
+
public class MyComponent
+
    implements Component
+
{
+
}</syntaxhighlight>
+
 
+
<h1>@Requirement</h1>
+
<h3>Basics</h3>
+
<p><code>@Requirement</code> defines injection points for legacy Plexus components. &nbsp;These more-or-less line-up directly with replacement with&nbsp;<code>@Inject</code>, though there are more options available as&nbsp;<code>@Inject</code> is support for fields, constructors and methods, where&nbsp;<code>@Requirement</code> only worked with fields. &nbsp;The recommended option is to replace legacy Plexus injection with constructor injection where possible.</p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my")
+
public class MyComponent
+
    implements Component
+
{
+
    @Requirement
+
    private AnotherComponent another;
+
}</syntaxhighlight>
+
 
+
<p>becomes:</p>
+
 
+
; Converted using recommended constructor injection
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    private final AnotherComponent another;
+
+
    @Inject
+
    public MyComponent(final AnotherComponent another) {
+
        this.another = another;
+
    }
+
}</syntaxhighlight>
+
 
+
<p>Use of constructor injection in this fashion has some impact on replacing legacy Plexus lifecycle&nbsp;<code>Initializable</code> and&nbsp;<code>Contextualizable</code> &nbsp;interfaces, which often only exist to perform setup once injection is performed.</p>
+
 
+
<h3>Alternatives</h3>
+
<p>Other options for conversions using field injection:</p>
+
 
+
; Converted using field injection
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    @Inject
+
    private AnotherComponent another;
+
}</syntaxhighlight>
+
 
+
{{Warning|
+
This is <strong>not</strong> recommended, as it makes it difficult to UNIT test the code w/o a full container to provide injection, which in itself can be problematic for UNIT testing. We highly recommend this form of injection <strong>NOT BE USED</strong>.}}
+
 
+
<p>or method injection:</p>
+
 
+
; Converted using method injection
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    private AnotherComponent another;
+
+
    @Inject
+
    public setAnotherComponent(final AnotherComponent another) {
+
        this.another = another;
+
    }
+
}</syntaxhighlight>
+
 
+
<h3>Optional</h3>
+
<p>Optional components are configured to be&nbsp;<code>@Nullable</code></p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my")
+
public class MyComponent
+
    implements Component
+
{
+
    @Requirement(optional=true)
+
    private AnotherComponent another;
+
}</syntaxhighlight>
+
 
+
<p>becomes:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    private final AnotherComponent another;
+
+
    @Inject
+
    public MyComponent(final @Nullable AnotherComponent another) {
+
        this.another = another;
+
    }
+
}</syntaxhighlight>
+
 
+
<h3>Names and Hints</h3>
+
<p>Legacy Plexus component&nbsp;<em>hints</em> become&nbsp;<code>@Named</code>:</p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my")
+
public class MyComponent
+
    implements Component
+
{
+
    @Requirement(hint="foo")
+
    private AnotherComponent another;
+
}</syntaxhighlight>
+
 
+
<p>becomes:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    private final AnotherComponent another;
+
+
    @Inject
+
    public MyComponent(final @Named("foo") AnotherComponent another) {
+
        this.another = another;
+
    }
+
}</syntaxhighlight>
+
 
+
<h3>Types</h3>
+
<p>Legacy Plexus component roles, which are normally only used for collection types are generally not needed:</p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my")
+
public class MyComponent
+
    implements Component
+
{
+
    @Requirement(role=AnotherComponent.class)
+
    private List<AnotherComponent> components;
+
}</syntaxhighlight>
+
 
+
<p>becomes:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    private final List<AnotherComponent> components;
+
+
    @Inject
+
    public MyComponent(final List<AnotherComponent> components) {
+
        this.components = components;
+
    }
+
}</syntaxhighlight>
+
 
+
<h1>@Configuration</h1>
+
<p>Plexus configuration injection is handled by&nbsp;<code>@Inject</code>&nbsp;<code>@Named(&quot;${expression}&quot;)</code> injection.</p>
+
<h3>Basics</h3>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my")
+
public class MyComponent
+
    implements Component
+
{
+
    @Configuration(name="configDir")
+
    private File configDir;
+
}</syntaxhighlight>
+
 
+
<p><span style="background-color: transparent;color: rgb(51,51,51);font-size: 14.0px;line-height: 1.4285715;">becomes:</span></p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    private final File configDir;
+
+
    @Inject
+
    public MyComponent(final @Named("${configDir}") configDir) {
+
        this.configDir = configDir;
+
    }
+
}</syntaxhighlight>
+
 
+
<h3>Defaults</h3>
+
<p>Default values are provided by expression syntax.</p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my")
+
public class MyComponent
+
    implements Component
+
{
+
    @Configuration(name="configDir", value="defaultDir")
+
    private File configDir;
+
}</syntaxhighlight>
+
 
+
<p>becomes:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    private final File configDir;
+
+
    @Inject
+
    public MyComponent(final @Named("${configDir:-defaultDir}") configDir) {
+
        this.configDir = configDir;
+
    }
+
}</syntaxhighlight>
+
 
+
<h1>Lifecycle Support</h1>
+
<p>This section is specific to how to adapt legacy Plexus component lifecycle interfaces. &nbsp;There is no hard-fast way to adapt these, but there are some guidelines to follow.</p>
+
 
+
<table border="1" style="border-collapse:collapse;padding:5px 10px;color:black;" cellspacing="0" cellpadding="5">
+
<tr>
+
<th>Interface</th>
+
<th>Class</th>
+
<th>Description</th></tr>
+
<tr>
+
<td colspan="1"><code>Initializable</code></td>
+
<td colspan="1">org.codehaus.plexus.personality.plexus.lifecycle.phase.<span>Initializable</span></td>
+
<td colspan="1">
+
<p>Hook was used to inform a component once its injection has been performed.</p></td></tr>
+
<tr>
+
<td colspan="1"><code>Contextualizable</code></td>
+
<td colspan="1">org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable</td>
+
<td colspan="1">
+
<p>Similar to <span>Initializable but passes in the container context.</span></p></td></tr>
+
<tr>
+
<td colspan="1"><code><span>Startable</span></code></td>
+
<td colspan="1">org.codehaus.plexus.personality.plexus.lifecycle.phase.Startable</td>
+
<td colspan="1">Allows components to be started and stopped.</td></tr>
+
<tr>
+
<td colspan="1"><code>Disposable</code></td>
+
<td colspan="1"><span>org.codehaus.plexus.personality.plexus.lifecycle.phase.<span>Disposable</span></span></td>
+
<td colspan="1">Hook used to inform a component that it is no longer available in the container.</td></tr>
+
</table>
+
 
+
<h2>Initializable</h2>
+
<p>By and far this can be replaced by using constructor-inject, and performing the&nbsp;<code>initialize()</code> at the end of the constructor.</p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my")
+
public class MyComponent
+
    implements Component, Initializable
+
{
+
    @Requirement
+
    private AnotherComponent another;
+
+
    public initialize() throws InitializationException {
+
        another.init();
+
    }
+
}</syntaxhighlight>
+
 
+
<p>becomes:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    private final AnotherComponent another;
+
+
    @Inject
+
    public MyComponent(final AnotherComponent another) {
+
        this.another = another;
+
        another.init();
+
    }
+
}</syntaxhighlight>
+
 
+
<h2>Contextualizable</h2>
+
<p>Similar to <code>Initializable</code>, though if the context is needed you can inject the container context parameters with:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    @Inject
+
    public MyComponent(final @Parameters Map<String,String> params) {
+
    // do something with _context_ params
+
    }
+
}</syntaxhighlight>
+
 
+
<h2>Startable</h2>
+
<p>Support for JSR250 lifecycle annotations is still being worked on (see {{Bug|386446}}). &nbsp; Until that feature is ready applications can use other techniques to handle start/stop behavior. The examples below show the solution used in Sonatype Nexus, which relies on a modified implementation of the Google-Guava EventBus to manage lifecycle events.</p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my")
+
public class MyComponent
+
    implements Component, Startable
+
{
+
    public start() throws StartingException {
+
        // do something to "start"
+
    }
+
+
    public stop() throws StoppingException {
+
        // do something to "stop"
+
    }
+
}</syntaxhighlight>
+
 
+
<p><span style="line-height: 1.5;">becomes:</span></p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
import org.sonatype.sisu.goodies.eventbus.EventBus;
+
import com.google.common.eventbus.Subscribe;
+
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    private final EventBus eventBus;
+
+
    @Inject
+
    public MyComponent(final EventBus eventBus) {
+
        this.eventBus = eventBus;
+
        eventBus.register(this);
+
    }
+
+
    @Subscribe
+
    public on(final NexusStartedEvent event) throws Exception {
+
        // do something to "start"
+
    }
+
+
    @Subscribe
+
    public on(final NexusStoppedEvent event) throws Exception {
+
        // do something to "stop"
+
+
        eventBus.unregister(this);
+
    }
+
}</syntaxhighlight>
+
 
+
<h2><span style="line-height: 1.5;">Disposable</span></h2>
+
<p>Similar to&nbsp;<code>Startable</code> use of events are used to handle replacement for Disposable components.</p>
+
 
+
; Plexus
+
<syntaxhighlight lang="java">
+
@Component(role=Component.class, hint="my")
+
public class MyComponent
+
    implements Component, Disposable
+
{
+
    public dispose() {
+
        // do something to "dispose"
+
    }
+
}</syntaxhighlight>
+
 
+
<p>becomes:</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
import org.sonatype.sisu.goodies.eventbus.EventBus;
+
import com.google.common.eventbus.Subscribe;
+
+
@Named("my")
+
@Singleton
+
public class MyComponent
+
    implements Component
+
{
+
    private final EventBus eventBus;
+
+
    @Inject
+
    public MyComponent(final EventBus eventBus) {
+
        this.eventBus = eventBus;
+
        eventBus.register(this);
+
    }
+
+
    @Subscribe
+
    public on(final NexusStoppedEvent event) throws Exception {
+
        // do something to "dispose"
+
+
        eventBus.unregister(this);
+
    }
+
}</syntaxhighlight>
+
 
+
<h1>Custom Bindings</h1>
+
<p>Plugins which require additional custom bindings can provide a&nbsp;<code>@Named</code> Guice module to configure components bindings further.</p>
+
<p>Sisu will automatically load modules which are&nbsp;<code>@Named</code> and apply them to the injectors bindings. &nbsp;These modules are really no different than normal Guice modules, except that they need to have the&nbsp;<code>@Named</code> annotation on them so that Sisu can locate them when initializing.</p>
+
 
+
; Converted
+
<syntaxhighlight lang="java">
+
@Named
+
public class MyPluginModule
+
    extends com.google.inject.AbstractModule
+
{
+
    public void configure() {
+
        bind(Component.class).to(MyComponent.class);
+
    }
+
}</syntaxhighlight>
+

Latest revision as of 13:51, 17 February 2022

This page has been moved to

https://github.com/eclipse/sisu.plexus/wiki/Plexus-to-JSR330

Back to the top