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"

(Initial contribution from Jason Dillon (in confluence format))
 
m
Line 1: Line 1:
<h1><ac:macro ac:name="toc" /></h1>
 
 
<h1>Overview</h1>
 
<h1>Overview</h1>
<p>This document provides some details on how to convert Sonatype Nexus legacy Plexus components into modern Sisu/Guice JSR-330 components.</p>
+
<p>This document provides some details on how to convert legacy Plexus components into modern JSR-330 components.</p>
 +
 
 
<h2>Annotations</h2>
 
<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>
 
<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>
 
<table>
<tbody>
 
 
<tr>
 
<tr>
 
<th>Annotation</th>
 
<th>Annotation</th>
Line 49: Line 49:
 
<td colspan="1"><code>@Nullable</code></td>
 
<td colspan="1"><code>@Nullable</code></td>
 
<td colspan="1">javax.annotation.Nullable</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></tbody></table><ac:macro ac:name="warning"><ac:parameter ac:name="title">javax.inject vs. com.google.inject</ac:parameter><ac:rich-text-body>
+
<td colspan="1">Standard JSR-305 annotation to mark field, parameter, result value as potentially returning null value</td></tr>
 +
</table>
 +
 
 +
<ac:macro ac:name="warning"><ac:parameter ac:name="title">javax.inject vs. com.google.inject</ac:parameter><ac:rich-text-body>
 +
 
 
<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 which should <strong>NOT</strong> be used.</code></p>
 
<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 which should <strong>NOT</strong> be used.</code></p>
 
<p>Prefer the standard <code>javax.inject</code> versions.</p></ac:rich-text-body></ac:macro>
 
<p>Prefer the standard <code>javax.inject</code> versions.</p></ac:rich-text-body></ac:macro>
Line 58: Line 62:
 
<p>Plexus&nbsp;<code>@Component</code> annotations are replaced by standard&nbsp;<code>@Named</code>,&nbsp;<code>@Singleton</code>, etc annotations.</p>
 
<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>
 
<h3>Singletons</h3>
<p>For example this Plexus component:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my")
+
<p>For example this Plexus component:</p>
 +
 
 +
; Plexus
 +
<syntaxhighlight lang="java">
 +
@Component(role=Component.class, hint="my")
 
public class MyComponent
 
public class MyComponent
 
     implements Component
 
     implements Component
 
{
 
{
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p>can be converted to:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
 
 +
<p>can be converted to:</p>
 +
 
 +
; Converted
 +
<syntaxhighlight lang="java">
 +
@Named("my")
 
@Singleton
 
@Singleton
 
public class MyComponent
 
public class MyComponent
 
     implements Component
 
     implements Component
 
{
 
{
}]]></ac:plain-text-body></ac:macro><ac:macro ac:name="tip"><ac:parameter ac:name="title">Naming Varients</ac:parameter><ac:rich-text-body>
+
}</syntaxhighlight>
<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><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[package components;
+
 
 +
<ac:macro ac:name="tip"><ac:parameter ac:name="title">Naming Varients</ac:parameter><ac:rich-text-body>
 +
 
 +
<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
 
@Named
Line 76: Line 96:
 
     implements Component
 
     implements Component
 
{
 
{
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<p>Results in a component with implicit name of &quot;<code>components.MyComponent</code>&quot;</p>
 
<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><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class)
+
<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
 
public class DefaultComponent
 
     implements Component
 
     implements Component
 
{
 
{
}]]></ac:plain-text-body></ac:macro>
+
}</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><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[package components;
+
 
 +
<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
 
@Named
Line 90: Line 120:
 
     implements Component
 
     implements Component
 
{
 
{
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<p><strong>BUT</strong> the name bound for this component would be &quot;<code>default</code>&quot;.</p></ac:rich-text-body></ac:macro>
 
<p><strong>BUT</strong> the name bound for this component would be &quot;<code>default</code>&quot;.</p></ac:rich-text-body></ac:macro>
 
<h3>Instance</h3>
 
<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><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my", instantiationStrategy="per-lookup")
+
<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
 
public class MyComponent
 
     implements Component
 
     implements Component
 
{
 
{
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p>and is converted to:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
 
 +
<p>and is converted to:</p>
 +
 
 +
; Converted
 +
<syntaxhighlight lang="java">
 +
@Named("my")
 
public class MyComponent
 
public class MyComponent
 
     implements Component
 
     implements Component
 
{
 
{
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<p>Notice this is the same as the example above, except with-out the&nbsp;<code>@Singleton</code> annotation.</p><ac:macro ac:name="warning"><ac:rich-text-body>
 
<p>Notice this is the same as the example above, except with-out the&nbsp;<code>@Singleton</code> annotation.</p><ac:macro ac:name="warning"><ac:rich-text-body>
 
<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>
 
<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>
 
<p>Additionally other Plexus-specific component configuration such as lifecycle-handlers, factories, composer, configurator, alias, version, profile, isolatedRealm are <strong>NOT</strong> supported.</p></ac:rich-text-body></ac:macro>
 
<p>Additionally other Plexus-specific component configuration such as lifecycle-handlers, factories, composer, configurator, alias, version, profile, isolatedRealm are <strong>NOT</strong> supported.</p></ac:rich-text-body></ac:macro>
 
<p>&nbsp;</p>
 
<p>&nbsp;</p>
 +
 
<h3>Type Override</h3>
 
<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><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
<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)
 
@Typed(Component.class)
 
public class MyComponent
 
public class MyComponent
 
     extends SomeSupportClassHardToGuessTypeFrom
 
     extends SomeSupportClassHardToGuessTypeFrom
 
{
 
{
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h3>Descriptions</h3>
 
<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><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my", description="My custom component")
+
<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
 
public class MyComponent
 
     implements Component
 
     implements Component
 
{
 
{
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
 
 +
<p>becomes:</p>
 +
 
 +
; Converted
 +
<syntaxhighlight lang="java">
 +
@Named("my")
 
@Singleton
 
@Singleton
 
@Description("My custom component")
 
@Description("My custom component")
Line 126: Line 182:
 
     implements Component
 
     implements Component
 
{
 
{
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h1>@Requirement</h1>
 
<h1>@Requirement</h1>
 
<h3>Basics</h3>
 
<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><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my")
+
<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
 
public class MyComponent
 
     implements Component
 
     implements Component
Line 135: Line 196:
 
     @Requirement
 
     @Requirement
 
     private AnotherComponent another;
 
     private AnotherComponent another;
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted using recommended constructor injection</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
 
 +
<p>becomes:</p>
 +
 
 +
; Converted using recommended constructor injection
 +
<syntaxhighlight lang="java">
 +
@Named("my")
 
@Singleton
 
@Singleton
 
public class MyComponent
 
public class MyComponent
Line 147: Line 213:
 
         this.another = another;
 
         this.another = another;
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</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>
 
<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>
 
<h3>Alternatives</h3>
<p>Other options for conversions using field injection:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted using field injection</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
<p>Other options for conversions using field injection:</p>
 +
 
 +
; Converted using field injection
 +
<syntaxhighlight lang="java">
 +
@Named("my")
 
@Singleton
 
@Singleton
 
public class MyComponent
 
public class MyComponent
Line 157: Line 229:
 
     @Inject
 
     @Inject
 
     private AnotherComponent another;
 
     private AnotherComponent another;
}]]></ac:plain-text-body></ac:macro><ac:macro ac:name="warning"><ac:rich-text-body>
+
}</syntaxhighlight>
<p>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></ac:rich-text-body></ac:macro>
+
 
 +
<ac:macro ac:name="warning"><ac:rich-text-body>
 +
 
 +
<p>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>
 +
 
 +
</ac:rich-text-body></ac:macro>
 +
 
 
<p>&nbsp;</p>
 
<p>&nbsp;</p>
<p>or method injection:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted using method injection</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
<p>or method injection:</p>
 +
 
 +
; Converted using method injection
 +
<syntaxhighlight lang="java">
 +
@Named("my")
 
@Singleton
 
@Singleton
 
public class MyComponent
 
public class MyComponent
Line 171: Line 253:
 
         this.another = another;
 
         this.another = another;
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h3>Optional</h3>
 
<h3>Optional</h3>
<p>Optional components are configured to be&nbsp;<code>@Nullable</code></p><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my")
+
<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
 
public class MyComponent
 
     implements Component
 
     implements Component
Line 179: Line 266:
 
     @Requirement(optional=true)
 
     @Requirement(optional=true)
 
     private AnotherComponent another;
 
     private AnotherComponent another;
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
 
 +
<p>becomes:</p>
 +
 
 +
; Converted
 +
<syntaxhighlight lang="java">
 +
@Named("my")
 
@Singleton
 
@Singleton
 
public class MyComponent
 
public class MyComponent
Line 191: Line 283:
 
         this.another = another;
 
         this.another = another;
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h3>Names and Hints</h3>
 
<h3>Names and Hints</h3>
<p>Legacy Plexus component&nbsp;<em>hints</em> become&nbsp;<code>@Named</code>:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my")
+
<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
 
public class MyComponent
 
     implements Component
 
     implements Component
Line 199: Line 296:
 
     @Requirement(hint="foo")
 
     @Requirement(hint="foo")
 
     private AnotherComponent another;
 
     private AnotherComponent another;
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
 
 +
<p>becomes:</p>
 +
 
 +
; Converted
 +
<syntaxhighlight lang="java">
 +
@Named("my")
 
@Singleton
 
@Singleton
 
public class MyComponent
 
public class MyComponent
Line 211: Line 313:
 
         this.another = another;
 
         this.another = another;
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h3>Types</h3>
 
<h3>Types</h3>
<p>Legacy Plexus component roles, which are normally only used for collection types are generally not needed:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my")
+
<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
 
public class MyComponent
 
     implements Component
 
     implements Component
Line 219: Line 326:
 
     @Requirement(role=AnotherComponent.class)
 
     @Requirement(role=AnotherComponent.class)
 
     private List<AnotherComponent> components;
 
     private List<AnotherComponent> components;
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
 
 +
<p>becomes:</p>
 +
 
 +
; Converted
 +
<syntaxhighlight lang="java">
 +
@Named("my")
 
@Singleton
 
@Singleton
 
public class MyComponent
 
public class MyComponent
Line 231: Line 343:
 
         this.components = components;
 
         this.components = components;
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h1>@Configuration</h1>
 
<h1>@Configuration</h1>
 
<p>Plexus configuration injection is handled by&nbsp;<code>@Inject</code>&nbsp;<code>@Named(&quot;${expression}&quot;)</code> injection.</p>
 
<p>Plexus configuration injection is handled by&nbsp;<code>@Inject</code>&nbsp;<code>@Named(&quot;${expression}&quot;)</code> injection.</p>
<h3>Basics</h3><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my")
+
<h3>Basics</h3>
 +
 
 +
; Plexus
 +
<syntaxhighlight lang="java">
 +
@Component(role=Component.class, hint="my")
 
public class MyComponent
 
public class MyComponent
 
     implements Component
 
     implements Component
Line 240: Line 357:
 
     @Configuration(name="configDir")
 
     @Configuration(name="configDir")
 
     private File configDir;
 
     private File configDir;
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p><span style="background-color: transparent;color: rgb(51,51,51);font-size: 14.0px;line-height: 1.4285715;">becomes:</span></p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
 
 +
<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
 
@Singleton
 
public class MyComponent
 
public class MyComponent
Line 252: Line 374:
 
         this.configDir = configDir;
 
         this.configDir = configDir;
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h3>Defaults</h3>
 
<h3>Defaults</h3>
<p>Default values are provided by expression syntax.</p><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my")
+
<p>Default values are provided by expression syntax.</p>
 +
 
 +
; Plexus
 +
<syntaxhighlight lang="java">
 +
@Component(role=Component.class, hint="my")
 
public class MyComponent
 
public class MyComponent
 
     implements Component
 
     implements Component
Line 260: Line 387:
 
     @Configuration(name="configDir", value="defaultDir")
 
     @Configuration(name="configDir", value="defaultDir")
 
     private File configDir;
 
     private File configDir;
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
 
 +
<p>becomes:</p>
 +
 
 +
; Converted
 +
<syntaxhighlight lang="java">
 +
@Named("my")
 
@Singleton
 
@Singleton
 
public class MyComponent
 
public class MyComponent
Line 272: Line 404:
 
         this.configDir = configDir;
 
         this.configDir = configDir;
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h1>Lifecycle Support</h1>
 
<h1>Lifecycle Support</h1>
 
<p>This section is specific to how to adapter legacy Plexus component lifecycle interfaces to use inside of Sonatype Nexus. &nbsp;There is no hard-fast way to adapt these, but there are some guidelines to follow.</p>
 
<p>This section is specific to how to adapter legacy Plexus component lifecycle interfaces to use inside of Sonatype Nexus. &nbsp;There is no hard-fast way to adapt these, but there are some guidelines to follow.</p>
 +
 
<table>
 
<table>
<tbody>
 
 
<tr>
 
<tr>
 
<th>Interface</th>
 
<th>Interface</th>
Line 298: Line 431:
 
<td colspan="1"><code>Disposable</code></td>
 
<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"><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></tbody></table>
+
<td colspan="1">Hook used to inform a component that it is no longer available in the container.</td></tr>
 +
</table>
 +
 
 
<h2>Initializable</h2>
 
<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><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my")
+
<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
 
public class MyComponent
 
     implements Component, Initializable
 
     implements Component, Initializable
Line 310: Line 449:
 
         another.init();
 
         another.init();
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
 
 +
<p>becomes:</p>
 +
 
 +
; Converted
 +
<syntaxhighlight lang="java">
 +
@Named("my")
 
@Singleton
 
@Singleton
 
public class MyComponent
 
public class MyComponent
Line 323: Line 467:
 
         another.init();
 
         another.init();
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h2>Contextualizable</h2>
 
<h2>Contextualizable</h2>
<p>Similar to <code>Initializable</code>, though if the context is needed you can inject the container context parameters with:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")
+
<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
 
@Singleton
 
public class MyComponent
 
public class MyComponent
Line 334: Line 483:
 
    // do something with _context_ params
 
    // do something with _context_ params
 
     }
 
     }
} ]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h2>Startable</h2>
 
<h2>Startable</h2>
<p>There is no natural correlating container feature in Sisu presently (or maybe ever) which supports this Plexus lifecycle. &nbsp;To work around, use the Nexus EventBus and handle events to replace start/stop behavior.</p><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my")
+
<p>There is no natural correlating container feature in Sisu presently (or maybe ever) which supports this Plexus lifecycle. &nbsp;To work around, use the Nexus EventBus and handle events to replace start/stop behavior.</p>
 +
 
 +
; Plexus
 +
<syntaxhighlight lang="java">
 +
@Component(role=Component.class, hint="my")
 
public class MyComponent
 
public class MyComponent
 
     implements Component, Startable
 
     implements Component, Startable
Line 347: Line 501:
 
         // do something to "stop"
 
         // do something to "stop"
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p><span style="line-height: 1.5;">becomes:</span></p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[import org.sonatype.sisu.goodies.eventbus.EventBus;
+
 
 +
<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;
 
import com.google.common.eventbus.Subscribe;
 
   
 
   
Line 375: Line 534:
 
         eventBus.unregister(this);
 
         eventBus.unregister(this);
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h2><span style="line-height: 1.5;">Disposable</span></h2>
 
<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><ac:macro ac:name="code"><ac:parameter ac:name="title">plexus</ac:parameter><ac:plain-text-body><![CDATA[@Component(role=Component.class, hint="my")
+
<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
 
public class MyComponent
 
     implements Component, Disposable
 
     implements Component, Disposable
Line 384: Line 548:
 
         // do something to "dispose"
 
         // do something to "dispose"
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[import org.sonatype.sisu.goodies.eventbus.EventBus;
+
 
 +
<p>becomes:</p>
 +
 
 +
; Converted
 +
<syntaxhighlight lang="java">
 +
import org.sonatype.sisu.goodies.eventbus.EventBus;
 
import com.google.common.eventbus.Subscribe;
 
import com.google.common.eventbus.Subscribe;
 
   
 
   
Line 407: Line 576:
 
         eventBus.unregister(this);
 
         eventBus.unregister(this);
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>
 +
 
 
<h1>Custom Bindings</h1>
 
<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>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><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named
+
<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
 
public class MyPluginModule
 
     extends com.google.inject.AbstractModule
 
     extends com.google.inject.AbstractModule
Line 417: Line 591:
 
         bind(Component.class).to(MyComponent.class);
 
         bind(Component.class).to(MyComponent.class);
 
     }
 
     }
}]]></ac:plain-text-body></ac:macro>
+
}</syntaxhighlight>

Revision as of 21:01, 13 November 2013

Overview

This document provides some details on how to convert legacy Plexus components into modern JSR-330 components.

Annotations

For brevity of examples imports are omitted.  The following table defines the meanings and fully-qualified-class-names of the annotations references in the following examples.

Annotation Class Description
@Component org.codehaus.plexus.component.annotations.Component Legacy Plexus component annotation
@Requirement org.codehaus.plexus.component.annotations.Requirement Legacy Plexus injection annotation
@Configuration org.codehaus.plexus.component.annotations.Configuration Legacy Plexus configuration annotation
@Named javax.inject.Named Standard JSR-330 annotation to provide component name
@Singleton javax.inject.Singleton Standard JSR-330 annotation to mark component as singleton
@Typed javax.enterprise.inject.Typed JavaEE annotation to mark component type
@Description org.sonatype.inject.Description Sisu-specific annotation to provide a description for a component
@Parameters org.sonatype.inject.Parameters Sisu-specific annotation to mark Map<String,String> injection as container context parameters.
@Inject javax.inject.Inject Standard JSR-330 annotation to mark field, parameter, method for injection
@Nullable javax.annotation.Nullable Standard JSR-305 annotation to mark field, parameter, result value as potentially returning null value

<ac:macro ac:name="warning"><ac:parameter ac:name="title">javax.inject vs. com.google.inject</ac:parameter><ac:rich-text-body>

There are com.google.inject flavors of @Inject, @Named and @Singleton which should NOT be used.

Prefer the standard javax.inject versions.

</ac:rich-text-body></ac:macro>

References

<a href="http://www.jcp.org/en/jsr/detail?id=330">JSR-330</a>

<a href="http://www.jcp.org/en/jsr/detail?id=330">JSR-305</a>

@Component

Plexus @Component annotations are replaced by standard @Named@Singleton, etc annotations.

Singletons

For example this Plexus component:

Plexus
@Component(role=Component.class, hint="my")
public class MyComponent
    implements Component
{
}

can be converted to:

Converted
@Named("my")
@Singleton
public class MyComponent
    implements Component
{
}

<ac:macro ac:name="tip"><ac:parameter ac:name="title">Naming Varients</ac:parameter><ac:rich-text-body>

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 @Named and the full-qualified-class-name of the component will be used as the name instead.

Converted
package components;
 
@Named
@Singleton
public class MyComponent
    implements Component
{
}

Results in a component with implicit name of "components.MyComponent"

Additionally the Sisu Plexus integration has special handling for Plexus default components. Many Plexus applications define objects like:

Plexus
@Component(role=Component.class)
public class DefaultComponent
    implements Component
{
}

The Default prefix on the component implementation class causes the binding to be translated to @Named("default"), so the conversion would look like:

Converted
package components;
 
@Named
@Singleton
public class DefaultComponent
    implements Component
{
}

BUT the name bound for this component would be "default".

</ac:rich-text-body></ac:macro>

Instance

By default in Plexus, components are singletons, but this is not the case for every component.  This Plexus component is not a singleton:

Plexus
@Component(role=Component.class, hint="my", instantiationStrategy="per-lookup")
public class MyComponent
    implements Component
{
}

and is converted to:

Converted
@Named("my")
public class MyComponent
    implements Component
{
}

Notice this is the same as the example above, except with-out the @Singleton annotation.

<ac:macro ac:name="warning"><ac:rich-text-body>

Only per-lookup and singleton instantiation strategies have reasonable mappings into Sisu. The keep-alive and poolable strategies are not supported.

Additionally other Plexus-specific component configuration such as lifecycle-handlers, factories, composer, configurator, alias, version, profile, isolatedRealm are NOT supported.

</ac:rich-text-body></ac:macro>

 

Type Override

By default the type of the component is determined automatically, though in some rare cases an explicit type is required.  To specify the explicit binding type use the @Typed annotation:

Converted
@Named("my")
@Typed(Component.class)
public class MyComponent
    extends SomeSupportClassHardToGuessTypeFrom
{
}

Descriptions

In some cases component descriptions are required.  There is no standard annotation to provide this, however Sisu provides a custom annotation for this.

Plexus
@Component(role=Component.class, hint="my", description="My custom component")
public class MyComponent
    implements Component
{
}

becomes:

Converted
@Named("my")
@Singleton
@Description("My custom component")
public class MyComponent
    implements Component
{
}

@Requirement

Basics

@Requirement defines injection points for legacy Plexus components.  These more-or-less line-up directly with replacement with @Inject, though there are more options available as @Inject is support for fields, constructors and methods, where @Requirement only worked with fields.  The recommended option is to replace legacy Plexus injection with constructor injection where possible.

Plexus
@Component(role=Component.class, hint="my")
public class MyComponent
    implements Component
{
    @Requirement
    private AnotherComponent another;
}

becomes:

Converted using recommended constructor injection
@Named("my")
@Singleton
public class MyComponent
    implements Component
{
    private final AnotherComponent another;
 
    @Inject
    public MyComponent(final AnotherComponent another) {
        this.another = another;
    }
}

Use of constructor injection in this fashion has some impact on replacing legacy Plexus lifecycle Initializable and Contextualizable  interfaces, which often only exist to perform setup once injection is performed.

Alternatives

Other options for conversions using field injection:

Converted using field injection
@Named("my")
@Singleton
public class MyComponent
    implements Component
{
    @Inject
    private AnotherComponent another;
}

<ac:macro ac:name="warning"><ac:rich-text-body>

This is not 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 NOT BE USED.

</ac:rich-text-body></ac:macro>

 

or method injection:

Converted using method injection
@Named("my")
@Singleton
public class MyComponent
    implements Component
{
    private AnotherComponent another;
 
    @Inject
    public setAnotherComponent(final AnotherComponent another) {
        this.another = another;
    }
}

Optional

Optional components are configured to be @Nullable

Plexus
@Component(role=Component.class, hint="my")
public class MyComponent
    implements Component
{
    @Requirement(optional=true)
    private AnotherComponent another;
}

becomes:

Converted
@Named("my")
@Singleton
public class MyComponent
    implements Component
{
    private final AnotherComponent another;
 
    @Inject
    public MyComponent(final @Nullable AnotherComponent another) {
        this.another = another;
    }
}

Names and Hints

Legacy Plexus component hints become @Named:

Plexus
@Component(role=Component.class, hint="my")
public class MyComponent
    implements Component
{
    @Requirement(hint="foo")
    private AnotherComponent another;
}

becomes:

Converted
@Named("my")
@Singleton
public class MyComponent
    implements Component
{
    private final AnotherComponent another;
 
    @Inject
    public MyComponent(final @Named("foo") AnotherComponent another) {
        this.another = another;
    }
}

Types

Legacy Plexus component roles, which are normally only used for collection types are generally not needed:

Plexus
@Component(role=Component.class, hint="my")
public class MyComponent
    implements Component
{
    @Requirement(role=AnotherComponent.class)
    private List<AnotherComponent> components;
}

becomes:

Converted
@Named("my")
@Singleton
public class MyComponent
    implements Component
{
    private final List<AnotherComponent> components;
 
    @Inject
    public MyComponent(final List<AnotherComponent> components) {
        this.components = components;
    }
}

@Configuration

Plexus configuration injection is handled by @Inject @Named("${expression}") injection.

Basics

Plexus
@Component(role=Component.class, hint="my")
public class MyComponent
    implements Component
{
    @Configuration(name="configDir")
    private File configDir;
}

becomes:

Converted
@Named("my")
@Singleton
public class MyComponent
    implements Component
{
    private final File configDir;
 
    @Inject
    public MyComponent(final @Named("${configDir}") configDir) {
        this.configDir = configDir;
    }
}

Defaults

Default values are provided by expression syntax.

Plexus
@Component(role=Component.class, hint="my")
public class MyComponent
    implements Component
{
    @Configuration(name="configDir", value="defaultDir")
    private File configDir;
}

becomes:

Converted
@Named("my")
@Singleton
public class MyComponent
    implements Component
{
    private final File configDir;
 
    @Inject
    public MyComponent(final @Named("${configDir:-defaultDir}") configDir) {
        this.configDir = configDir;
    }
}

Lifecycle Support

This section is specific to how to adapter legacy Plexus component lifecycle interfaces to use inside of Sonatype Nexus.  There is no hard-fast way to adapt these, but there are some guidelines to follow.

Interface Class Description
Initializable org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable

Hook was used to inform a component once its injection has been performed.

Contextualizable org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable

Similar to Initializable but passes in the container context.

Startable org.codehaus.plexus.personality.plexus.lifecycle.phase.Startable Allows components to be started and stopped.
Disposable org.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable Hook used to inform a component that it is no longer available in the container.

Initializable

By and far this can be replaced by using constructor-inject, and performing the initialize() at the end of the constructor.

Plexus
@Component(role=Component.class, hint="my")
public class MyComponent
    implements Component, Initializable
{
    @Requirement
    private AnotherComponent another;
 
    public initialize() throws InitializationException {
        another.init();
    }
}

becomes:

Converted
@Named("my")
@Singleton
public class MyComponent
    implements Component
{
    private final AnotherComponent another;
 
    @Inject
    public MyComponent(final AnotherComponent another) {
        this.another = another;
        another.init();
    }
}

Contextualizable

Similar to Initializable, though if the context is needed you can inject the container context parameters with:

Converted
@Named("my")
@Singleton
public class MyComponent
    implements Component
{
    @Inject
    public MyComponent(final @Parameters Map<String,String> params) {
	    // do something with _context_ params
    }
}

Startable

There is no natural correlating container feature in Sisu presently (or maybe ever) which supports this Plexus lifecycle.  To work around, use the Nexus EventBus and handle events to replace start/stop behavior.

Plexus
@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"
    }
}

becomes:

Converted
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);
    }
}

Disposable

Similar to Startable use of events are used to handle replacement for Disposable components.

Plexus
@Component(role=Component.class, hint="my")
public class MyComponent
    implements Component, Disposable
{
    public dispose() {
        // do something to "dispose"
    }
}

becomes:

Converted
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);
    }
}

Custom Bindings

Plugins which require additional custom bindings can provide a @Named Guice module to configure components bindings further.

Sisu will automatically load modules which are @Named and apply them to the injectors bindings.  These modules are really no different than normal Guice modules, except that they need to have the @Named annotation on them so that Sisu can locate them when initializing.

Converted
@Named
public class MyPluginModule
    extends com.google.inject.AbstractModule
{
    public void configure() {
        bind(Component.class).to(MyComponent.class);
    }
}

Back to the top