The contents of this document are a work in progress

Plexus Component Descriptor

We are discussing the component descriptors for components which are compatible with Plexus Personality. We show a limited picture of what can be done with components and component descriptors in Plexus.

Nevertheless what you learn here should be sufficient to write even a large Plexus application!

A component descriptor describes the properties of a component required by the container in order to manage the lifecycle of that component. Let's take a look at the simplest example of a component descriptor:

<component-sets>
  <components>
    <component>
      <role>foo.Foo</role>
      <implementation>foo.DefaultFoo</implementation>
    </component>
  </components>
</component-sets>

It starts off with a component tag, then values for the role and implementation tags are defined. The role tag defines the interface provided by this component. It is usually the name of the Java interface. Names of the classes (including abstract) are also allowed. You are not allowed to use arbitrary strings. The implementation tag tells Plexus what you want to use to implement the specified role. In a normal case you will have to specify the name of the java class (foo.DefaultFoo in the example above), which implements the given interface (role).

If there is more than one component which provides an implementation of a given role you can differentiate them with the help of the role-hint attribute:

...
<component>
  <role>foo.SomeComponent</role>
  <role-hint>componentA</role-hint>
  <implementation>foo.FooA</implementation>
<component>

<component>
  <role>foo.SomeComponent</role>
  <role-hint>componentB</role-hint>
  <implementation>foo.FooB</implementation>
<component>
...

The role and the role-hint are what defines component identity and what you use to look up your component from Plexus. The role-hint tag is optional. It serves as an extra id qualifier that allows you to differentiate components of the same type (role). You can use any arbitrary string you like as the value of the role-hint attribute. Note that you can deploy the same implementation of the component two or more times with different role-hints e.g.:

...
<component>
  <role>foo.SomeComponent</role>
  <role-hint>instance-one</role-hint>
  <implementation>foo.FooA</implementation>
<component>

<component>
  <role>foo.SomeComponent</role>
  <role-hint>instance-two</role-hint>
  <implementation>foo.FooA</implementation>
<component>
...

Requirements

Components aren't very useful as isolated entities nor are they always ready to use standalone. They become useful when we connect them together with other components and provide configuration settings to them. There are several different ways to compose your system from components.

The default, and the simplest of them, is a part of Plexus Personality This uses a strategy called 'field injection' for wiring components. This means that Plexus takes selected objects and assigns them to fields of component objects.

How does Plexus know into which fields requirements should be injected and what those requirements are? There is a requirements section in the component descriptor where you declare what components you depend on:

...
<component>
  ...
  <requirements>
    <requirement>
      ...
    </requirement>
    <requirement>
      ...
    </requirement>
  </requirements>
</component>
...

In a simple case the declaration of a dependency looks like this:

  ...
  <requirement>
    <role>org.codehaus.plexus.ComponentA</role>
  </requirement>
  ...

In the case when you want to declare a dependecy on a component which has specified both role and role-hint attributes you use:

  ...
  <requirement>
    <role>org.codehaus.plexus.ComponentB</role>
    <role-hint>foo</role-hint>
  </requirement>
  ...

In any case Component Composer will try to find a matching field and property for the given requirements. In the standard case the component composer will try to find a field in the component class (this is usually a private field), which has a type which matches the requirement's role.

For example in the case of this Java class:

package foo;
public class SomeComponentImpl
{
     // this is a "requirement" of this component
     org.codehaus.plexus.ComponentA  a;

     // this is ordinary field
     int  b;
}

You will need to prepare the following component descriptor which lists ComponentA as a requirement:

...
<component>
  <role>foo.SomeComponent</role>
  <implementation>foo.SomeComponentImpl</implementation>
  <requirements>
    <requirement>
      <role>org.codehaus.plexus.ComponentA</role>
    </requirement>
  </requirements>
<component>
...

Collections

Plexus can also inject Maps, List or arrays of dependent components. In the case of arrays of components the same approach is applied as in the case of "singular" dependencies. The only difference is that all visible implementations of a given role will be used.

Java:

package foo;
public class SomeComponentImpl
{
     org.codehaus.plexus.ComponentA  a[];
}

XML Descriptor:

...
<component>
  <role>foo.SomeComponent</role>
  <implementation>foo.SomeComponentImpl</implementation>
  <requirements>
    <requirement>
      <role>org.codehaus.plexus.ComponentA</role>
    </requirement>
  </requirements>
</component>

...

<component>
  <role>org.codehaus.plexus.ComponentA</role>
  <role-hint>A</role-hint>
  ...
</component>
<component>
  <role>org.codehaus.plexus.ComponentA</role>
  <role-hint>B</role-hint>
  ...
</component>
...

In case of a List, or a Map you must explicitly define where dependencies should be injected. You can do this with the help of the field-name tag:

XML Descriptor:

...
<component>
  ...
  <requirements>
    <requirement>
      <role>org.codehaus.plexus.ComponentB</role>
      <role-hint>foo</role-hint>
      <field-name>mapA</field-name>
    </requirement>
    <requirement>
      <role>org.codehaus.plexus.ComponentB</role>
      <role-hint>bar</role-hint>
      <field-name>listB</field-name>
    </requirement>
  </requirements>
</component>
...

Java:

package foo;
public class SomeComponentImpl
{
       private Map mapA;
       private List listB;
}

Note that when using a Map the value of a component's role-hint is used as the key and the component instance is used as the value.

You can also use the field tag for a "singular" component's requirements.

XML Descriptor:

...
<component>
  ...
  <requirements>
    <requirement>
      <role>org.codehaus.plexus.ComponentB</role>
      <role-hint>foo</role-hint>
      <field>b1</field>
    </requirement>
    <requirement>
      <role>org.codehaus.plexus.ComponentB</role>
      <role-hint>bar</role-hint>
      <field>b2</field>
    </requirement>
  </requirements>
<component>
...

Java:

package foo;
public class SomeComponentImpl
{
    //(component with role-hint = "foo" will be injected here)
    org.codehaus.plexus.ComponentB  b1;

    //(component with role-hint = "bar" will be injected here)
    org.codehaus.plexus.ComponentB  b2;
}

Explicit specification into which field dependencies should be injected is considered to be a good pattern and might even be required in future versions of Plexus.

Configuration

Lastly, there is an optional configuration section which is used to configure your component.

...
<component>
  ...
  <configuration>
    <a>bleh</a>
    <b>
      <x>1</x>
      <y>2.0f</y>
    </b>
  </configuration>
<component>
...

You may read more about it here.

And that's all you need to know about component configuration to get you started!

Important remark: Component configuration and component requirements (definition of dependencies on some other components) are separated in Plexus. In some popular containers like Spring this is not the case. The reason for this is that component requirements are constant - components always needs to use the same set of services provided by other components in order to function properly. But configuration of a component is more dynamic. For example components which provide an implementation of a jdbc database connection pool must always be configured in each application which uses it.

If you want to learn more about more advanced options you can read Component Descriptor.