The goal of this blog is to show an updated and more involved example then what is currently located on the plexus site. This will cover creating a couple of components and explain the different ways to inject your dependencies. This example assumes you are using maven 2 to make your life easier.
The first task to creating a component is to define its role. In Java, this usually takes the form of defining an interface with the functionality the component will expose.
Note: Plexus does not strictly require you use an interface to define the role, however it is strongly recommended to help improve your application design.
package example; public interface Cheese { /** * Slices the cheese for apportioning onto crackers. * @param slices the number of slices */ void slice( int slices ); /** * Get the description of the aroma of the cheese. * @return the aroma */ String getAroma(); }
That is it, nothing special, just an interface. We will use the class name as the role, in this example it will be Cheese.class
Once an interface is declared, you need to create one or more implementations of the functionality declared by it.
package example; import org.codehaus.plexus.component.annotations.Component; /** * All we need to tell Plexus that this is a component. */ @Component( role = Cheese.class, hint = "parmesan" ) public class ParmesanCheese implements Cheese { public void slice( int slices ) { throw new UnsupportedOperationException( "No can do" ); } public String getAroma() { return "strong"; } }
All you need to do is add the @Component
annotation, to define the role (the interface name from above) and a hint (to identify a unique implementation).
Other then telling you how to create it, and what it is for, I am not going to tell you much about it. This file will be generated for you in META-INF/plexus/components.xml
inside your jar.
The component descriptor describes the component and its dependencies (we will get to that next). All you need to do is configure the plexus-component-metadata plugin in your pom. And of course you need to add your maven dependencies.
<project> ... <dependencies> <!-- The Plexus annotations --> <dependency> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-component-annotations</artifactId> <version>1.0-beta-3.0.5</version> </dependency> <!-- The plexus container --> <dependency> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-container-default</artifactId> <version>1.0-beta-3.0.5</version> </dependency> ... </dependencies> <build> ... <plugins> ... <!-- Needed to process the annotations to create an xml file --> <plugin> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-component-metadata</artifactId> <version>1.0-beta-3.0.5</version> <executions> <execution> <id>process-classes</id> <goals> <goal>generate-metadata</goal> </goals> </execution> <execution> <id>process-test-classes</id> <goals> <goal>generate-test-metadata</goal> </goals> </execution> </executions> </plugin> ... <plugins> </project>
The above is all great and stuff but so far we haven't injected anything yet.
First we will add another Cheese. This will be the default implementation.
package example; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.logging.Logger; /** * AmericanCheese is the default cheese in most places. So here we are using it as the default component. Note: no * <code>hint</code> is specified. */ @Component( role = Cheese.class ) public class AmericanCheese implements Cheese { @Requirement Logger logger; public void slice( int slices ) { this.logger.info( "Slicing the cheese: " + slices + " times." ); } public String getAroma() { return "plastic"; } }
Then we will create another interface/component. Something simple:
package example; public interface Plate { void printItems(); }
And now for the fun!
Create a Plate component
package example; public class CheesePlate implements Plate { ... public void printItems() { ... } }
To inject the default component, you just add the @Requirement
to you field. Plexus assumes the role is the type of the field. In this case the field is a Cheese
@Requirement private Cheese americanCheese;
This does the same as the above snippet, but is more verbose.
@Requirement( hint = "default" ) private Cheese moreAmericanCheese;
To inject a specific implementation use the component`s hint
.
@Requirement( hint = "parmesan" ) private Cheese parmesanCheese;
To inject all the implementations of a component, just define the role
.
@Requirement( role = Cheese.class ) private List<cheese> cheeses;
You can also inject the components as a Map, the key of the entry is the component`s hint
.
@Requirement( role = Cheese.class ) private Map<string, Cheese> cheeseMap;
The following class demonstrates the different dependency techniques.
package example; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; @Component( role = Plate.class ) public class CheesePlate implements Plate { /** * Inject the default cheese. */ @Requirement private Cheese americanCheese; /** * Inject the default cheese, this does the same as the previous line. */ @Requirement( hint = "default" ) private Cheese moreAmericanCheese; /** * Inject the ParmesanCheese. */ @Requirement( hint = "parmesan" ) private Cheese parmesanCheese; /** * Inject all the cheese. The container automatically add all the components defined with the role <code>Cheese.class</code>. */ @Requirement( role = Cheese.class ) private List<cheese> cheeses; /** * You can also inject the components as a Map, where the key is the Components <code>hint</code>. */ @Requirement( role = Cheese.class ) private Map<string, Cheese> cheeseMap; public void printItems() { System.out.println( "americanCheese smells like: " + americanCheese.getAroma() ); System.out.println( "moreAmericanCheese smells like: " + moreAmericanCheese.getAroma() ); System.out.println( "parmesanCheese smells : " + parmesanCheese.getAroma() ); System.out.println( "\nThe List of cheeses has:" ); for ( Cheese cheese : this.cheeses ) { System.out.println( "cheese: " + cheese.getClass().getSimpleName() ); } System.out.println( "\nThe Map contains:" ); for ( Entry<string, Cheese> entry : this.cheeseMap.entrySet() ) { System.out.println( "hint: " + entry.getKey() + ", value: " + entry.getValue().getClass().getSimpleName() ); } } }
The final step is to execute the application that uses this component. In this example, you will use an container from a standard Java class with a main() method.
Creating the container is very simple:
package example; import org.codehaus.plexus.DefaultPlexusContainer; import org.codehaus.plexus.PlexusContainer; public class Example { public static void main( String[] args ) throws Exception { // create a new container PlexusContainer container = new DefaultPlexusContainer(); ... // stop the components and container container.dispose(); } }
That's all there is to it: create the container, and start it. Defaults and the current classloader will be used.
To retrieve the Plate component from the container and execute it's printItems() method, add the following lines after those that start the container:
Plate plate = container.lookup( Plate.class ); plate.printItems();
That's all there is to getting started with Plexus. Congratulations!