2013-11-14

Initial contribution from Jason Dillon (in confluence format)

New page

<h1><ac:macro ac:name="toc" /></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>

<h2>Annotations</h2>

<p>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.</p>

<table>

<tbody>

<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<String,String></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></tbody></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>Prefer the standard <code>javax.inject</code> versions.</p></ac:rich-text-body></ac:macro>

<h2>References</h2>

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

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

<h1>@Component</h1>

<p>Plexus <code>@Component</code> annotations are replaced by standard <code>@Named</code>, <code>@Singleton</code>, etc annotations.</p>

<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")

public class MyComponent

implements Component

{

}]]></ac:plain-text-body></ac:macro>

<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")

@Singleton

public class MyComponent

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>

<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;

@Named

@Singleton

public class MyComponent

implements Component

{

}]]></ac:plain-text-body></ac:macro>

<p>Results in a component with implicit name of "<code>components.MyComponent</code>"</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)

public class DefaultComponent

implements Component

{

}]]></ac:plain-text-body></ac:macro>

<p>The <code>Default</code> prefix on the component implementation class causes the binding to be translated to <code>@Named("default")</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;

@Named

@Singleton

public class DefaultComponent

implements Component

{

}]]></ac:plain-text-body></ac:macro>

<p><strong>BUT</strong> the name bound for this component would be "<code>default</code>".</p></ac:rich-text-body></ac:macro>

<h3>Instance</h3>

<p>By default in Plexus, components are singletons, but this is not the case for every component.  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")

public class MyComponent

implements Component

{

}]]></ac:plain-text-body></ac:macro>

<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")

public class MyComponent

implements Component

{

}]]></ac:plain-text-body></ac:macro>

<p>Notice this is the same as the example above, except with-out the <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>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> </p>

<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.  To specify the explicit binding type use the <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")

@Typed(Component.class)

public class MyComponent

extends SomeSupportClassHardToGuessTypeFrom

{

}]]></ac:plain-text-body></ac:macro>

<h3>Descriptions</h3>

<p>In some cases component descriptions are required.  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")

public class MyComponent

implements Component

{

}]]></ac:plain-text-body></ac:macro>

<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")

@Singleton

@Description("My custom component")

public class MyComponent

implements Component

{

}]]></ac:plain-text-body></ac:macro>

<h1>@Requirement</h1>

<h3>Basics</h3>

<p><code>@Requirement</code> defines injection points for legacy Plexus components.  These more-or-less line-up directly with replacement with <code>@Inject</code>, though there are more options available as <code>@Inject</code> is support for fields, constructors and methods, where <code>@Requirement</code> only worked with fields.  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")

public class MyComponent

implements Component

{

@Requirement

private AnotherComponent another;

}]]></ac:plain-text-body></ac:macro>

<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")

@Singleton

public class MyComponent

implements Component

{

private final AnotherComponent another;

@Inject

public MyComponent(final AnotherComponent another) {

this.another = another;

}

}]]></ac:plain-text-body></ac:macro>

<p>Use of constructor injection in this fashion has some impact on replacing legacy Plexus lifecycle <code>Initializable</code> and <code>Contextualizable</code>  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><ac:macro ac:name="code"><ac:parameter ac:name="title">converted using field injection</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")

@Singleton

public class MyComponent

implements Component

{

@Inject

private AnotherComponent another;

}]]></ac:plain-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> </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")

@Singleton

public class MyComponent

implements Component

{

private AnotherComponent another;

@Inject

public setAnotherComponent(final AnotherComponent another) {

this.another = another;

}

}]]></ac:plain-text-body></ac:macro>

<h3>Optional</h3>

<p>Optional components are configured to be <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")

public class MyComponent

implements Component

{

@Requirement(optional=true)

private AnotherComponent another;

}]]></ac:plain-text-body></ac:macro>

<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")

@Singleton

public class MyComponent

implements Component

{

private final AnotherComponent another;

@Inject

public MyComponent(final @Nullable AnotherComponent another) {

this.another = another;

}

}]]></ac:plain-text-body></ac:macro>

<h3>Names and Hints</h3>

<p>Legacy Plexus component <em>hints</em> become <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")

public class MyComponent

implements Component

{

@Requirement(hint="foo")

private AnotherComponent another;

}]]></ac:plain-text-body></ac:macro>

<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")

@Singleton

public class MyComponent

implements Component

{

private final AnotherComponent another;

@Inject

public MyComponent(final @Named("foo") AnotherComponent another) {

this.another = another;

}

}]]></ac:plain-text-body></ac:macro>

<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")

public class MyComponent

implements Component

{

@Requirement(role=AnotherComponent.class)

private List<AnotherComponent> components;

}]]></ac:plain-text-body></ac:macro>

<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")

@Singleton

public class MyComponent

implements Component

{

private final List<AnotherComponent> components;

@Inject

public MyComponent(final List<AnotherComponent> components) {

this.components = components;

}

}]]></ac:plain-text-body></ac:macro>

<h1>@Configuration</h1>

<p>Plexus configuration injection is handled by <code>@Inject</code> <code>@Named("${expression}")</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")

public class MyComponent

implements Component

{

@Configuration(name="configDir")

private File configDir;

}]]></ac:plain-text-body></ac:macro>

<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")

@Singleton

public class MyComponent

implements Component

{

private final File configDir;

@Inject

public MyComponent(final @Named("${configDir}") configDir) {

this.configDir = configDir;

}

}]]></ac:plain-text-body></ac:macro>

<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")

public class MyComponent

implements Component

{

@Configuration(name="configDir", value="defaultDir")

private File configDir;

}]]></ac:plain-text-body></ac:macro>

<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")

@Singleton

public class MyComponent

implements Component

{

private final File configDir;

@Inject

public MyComponent(final @Named("${configDir:-defaultDir}") configDir) {

this.configDir = configDir;

}

}]]></ac:plain-text-body></ac:macro>

<h1>Lifecycle Support</h1>

<p>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.</p>

<table>

<tbody>

<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></tbody></table>

<h2>Initializable</h2>

<p>By and far this can be replaced by using constructor-inject, and performing the <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")

public class MyComponent

implements Component, Initializable

{

@Requirement

private AnotherComponent another;

public initialize() throws InitializationException {

another.init();

}

}]]></ac:plain-text-body></ac:macro>

<p>becomes:</p><ac:macro ac:name="code"><ac:parameter ac:name="title">converted</ac:parameter><ac:plain-text-body><![CDATA[@Named("my")

@Singleton

public class MyComponent

implements Component

{

private final AnotherComponent another;

@Inject

public MyComponent(final AnotherComponent another) {

this.another = another;

another.init();

}

}]]></ac:plain-text-body></ac:macro>

<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")

@Singleton

public class MyComponent

implements Component

{

@Inject

public MyComponent(final @Parameters Map<String,String> params) {

// do something with _context_ params

}

} ]]></ac:plain-text-body></ac:macro>

<h2>Startable</h2>

<p>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.</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")

public class MyComponent

implements Component, Startable

{

public start() throws StartingException {

// do something to "start"

}

public stop() throws StoppingException {

// do something to "stop"

}

}]]></ac:plain-text-body></ac:macro>

<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;

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

}

}]]></ac:plain-text-body></ac:macro>

<h2><span style="line-height: 1.5;">Disposable</span></h2>

<p>Similar to <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")

public class MyComponent

implements Component, Disposable

{

public dispose() {

// do something to "dispose"

}

}]]></ac:plain-text-body></ac:macro>

<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;

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

}

}]]></ac:plain-text-body></ac:macro>

<h1>Custom Bindings</h1>

<p>Plugins which require additional custom bindings can provide a <code>@Named</code> Guice module to configure components bindings further.</p>

<p>Sisu will automatically load modules which are <code>@Named</code> and apply them to the injectors bindings.  These modules are really no different than normal Guice modules, except that they need to have the <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

public class MyPluginModule

extends com.google.inject.AbstractModule

{

public void configure() {

bind(Component.class).to(MyComponent.class);

}

}]]></ac:plain-text-body></ac:macro>

Show more