Chapter 18. Simple Add-Ons

This chapter will provide an introduction to Spring Roo add-on development. The intention is to provide a step-by-step guide that walks the developer from zero code to a fully deployed and published add-on that is immediately available to all Spring Roo users. With the release of Spring Roo 1.1, a new set of commands is available that are designed to provide a fast introduction to add-on development, as well as easy access to registered add-ons by Spring Roo 1.1 users.

A new add-on named 'Add-On Creator' has been developed that facilitates the creation of a new Spring Roo add-on. Furthermore, it offers out of the box support for the Subversion integration provided by Google Code as well as zero setup for hosting the add-on in a public Maven repository hosted as part of a Google Code project. In order to register the add-on with RooBot - a Spring Roo add-on registration service - the add-on is also required to be OSGi compliant, needs to be signed with PgP keys and the addon bundle needs to be registered through the httppgp protocol. Add-on developers get all these features automatically configured if they use the new 'Add-On Creator' feature that ships with Spring Roo 1.1.

The following sections will present a complete step-by-step guide demonstrating how to bootstrap a new Spring Roo add-on, publish and release it as your own Google Code project, and register it with the RooBot service.

18.1. Project Setup

In addition to the general installation steps discussed in the development process chapter (section 4), you should also follow the following project specific steps:

  1. Create a new project in Google Code: Sign in with your Google Account and navigate to http://code.google.com/hosting/createProject where you can create your project:

    • Project Name - a meaningful name such as spring-roo-addon-mvc-i18n-french

    • Project Summary - a summary of your project such as 'Spring Roo Add-On to provide French translation for Spring MVC scaffolding'

    • Project Description - description that could include a version compatibility matrix for your add-on

    • Version control system - Subversion

    • Source code license - GNU General Public License v3

    • Project Labels - Spring Roo, Java, Add-On

  2. By default, SVN hosting in Google Code will give you a trunk, tags, branches and a wiki folder. In order to host a Maven repository in your Google code project, you should also create a repo folder as root for the new repository:

    $ svn mkdir -m "create maven repository" https://<project-name>.googlecode.com/svn/repo --username <username> --password <password>
  3. Check out your newly created project from SVN:

    $ svn checkout https://<project-name>.googlecode.com/svn/trunk/ <project-name> --username <username>
  4. (optional) Enter your Google Code SVN credentials into your local maven repository settings.xml:

    <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
      <servers>
        <server>
          <id>Google Code</id>
          <username>myusername</username>
          <password>mypassword</password>
        </server>
      </servers>
    </settings>

18.2. Fast Creation

Once you have installed Java, Maven, PGP, and SVN tools, and have created and checked out your Google Code project, you can change into the <project-name> directory, which at this stage should contain only the .svn directory. In the <project-name> directory, you can start the Spring Roo shell and use one of the new commands for add-on creation:

roo> addon create simple --topLevelPackage com.foo --projectName <project-name>

The addon create simple command will scaffold a number of artefacts:

[1] pom.xml
[2] readme.txt
[3] legal/LICENSE.TXT
[4] src/main/java/com/foo/batch/BatchCommands.java
[5] src/main/java/com/foo/batch/BatchOperations.java
[5] src/main/java/com/foo/batch/BatchOperationsImpl.java
[6] src/main/java/com/foo/batch/BatchPropertyName.java
[7] src/main/assembly/assembly.xml

This newly created add-on project can be imported into the SpringSource Tool Suite via File > Import > Maven > Existing Maven projects. Let's discuss some of these artefacts in more detail:

  1. pom.xml - This is the Maven project configuration. This configuration ships with a number of preinstalled Maven plugins that facilitate the PGP artefact signing process as well as the project release process (including tagging etc). It also adds the OSGi and Felix dependencies needed for the addon to run in the Roo Shell. Furthermore, several commonly used Spring Roo modules are preinstalled. These modules provide functionalities such as file system monitoring, Roo shell command registration, etc. More information about these functionalities is provided in the following sections.

    The add-on developer should open up the pom.xml file and modify some project specific references and documentation (marked in bold font):

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <project [...]>
      [...]
      <name>com-foo-batch</name>
      <organization>
        <name>Your project/company name goes here (used in copyright and vendor information in the manifest)</name>
      </organization>
      [...]
      <description>An add-on created by Spring Roo's addon creator feature.</description>
      <url>http://www.some.company</url>
      <properties>

    Some of these properties can also be provided when issuing the addon create command.

  2. readme.txt - You can provide any setup or installation information about your add-on in this file. This file is used by other developers who checkout your add-on source code from the SVN repository.

  3. legal/LICENSE.TXT - Copy the appropriate license text for your add-on into this file.

  4. src/main/java/com/foo/batch/BatchCommands.java - This is a fully working code example demonstrating how to register commands offered by your addon into the Spring Roo Shell (more detailed information in the next section).

  5. src/main/java/com/foo/batch/BatchOperations.java & BatchOperationsImpl.java - These artefacts are used to perform operations triggered by a command (more information in the next sections).

  6. src/main/java/com/foo/batch/BatchPropertyName.java - This type provides a simple example demonstrating the use of static command completion options for the Spring Roo Shell. An example of static command completion options are for example the database selection options as part of the jpa setup command.

  7. src/main/assembly/assembly.xml - This artefact defines configurations used for the packaging of the add-on.

18.3. Shell Interaction

Spring Roo provides an easy way for external add-ons to contribute new commands to the Roo Shell. Looking at the code extract below, there are really only two artefacts needed in your command type to register a new command in the Roo Shell; your type needs to implement the CommandMarker interface, and you need to create a method annotated with @CliCommand. Let us review some details:

[1] @Component
[1] @Service
[2] public class BatchCommands implements CommandMarker {

[3]    @Reference private BatchOperations operations;
       @Reference private StaticFieldConverter staticFieldConverter;

[4]    protected void activate(ComponentContext context) {
          staticFieldConverter.add(BatchPropertyName.class);
       }

[4]    protected void deactivate(ComponentContext context) {
          staticFieldConverter.remove(BatchPropertyName.class);
       }

[5]    @CliAvailabilityIndicator("welcome property")
       public boolean isPropertyAvailable() {
          return operations.isProjectAvailable();  
       }

[6]    @CliCommand(value="welcome property", help="Obtains a pre-defined system property")
[7]    public String property(@CliOption(key="name", mandatory=false, specifiedDefaultValue="USERNAME", unspecifiedDefaultValue="USERNAME", help="The property name you'd like to display") BatchPropertyName propertyName) {
          return operations.getProperty(propertyName);
       }

There are a few artefacts of interest when developing Spring Roo add-ons:

  1. To register components and services in the Roo shell, the type needs to be annotated with the @Component & @Service annotations provided by Felix. These components can be injected into other add-ons (more interesting for functionalities exposed by operations types).

  2. The command type needs to implement the CommandMarker interface, which Spring Roo scans for in order to detect classes that contribute commands to the Roo Shell.

  3. The Felix @Reference annotations are used to inject services and components offered by other Spring Roo core components or even other add-ons. In this example, we are injecting a reference to the add-on's own BatchOperations interface and the StaticFieldConverter component offered by the Roo Shell OSGi bundle. The Felix @Reference annotation is similar in purpose to Spring's @Autowired and @Inject annotations.

  4. The activate and deactivate methods can optionally be implemented to get access to the lifecycle of the addon's bundle as managed by the underlying OSGi container. Roo add-on developers can use these lifecycle hooks for registration and deregistration of converters (typically in command types) or for the registration of metadata dependencies (typically in ITD-providing add-ons) or any other component initialization activities.

  5. The optional @CliAvailabilityIndicator annotation allows you to limit when a command is available in the Spring Roo Shell. Methods thus annotated should return a boolean to indicate whether a command should be visible to the Roo Shell. For example, many commands are hidden before a project has been created.

  6. The @CliCommand annotation plays a central role for Roo add-on developers. It allows the registration of new commands for the Roo Shell. Methods annotated with @CliCommand can optionally return a String value to contribute a log statement to the Spring Roo Shell. Another, more flexible, option to provide log statements in the Roo Shell is to register a standard JDK logger, which allows the developer to present color-coded messages to the user in the Roo shell, with the color coding being dependent on the log level (warning, info, error, etc).

  7. The optional @CliOption annotation can be used to annotate method parameters. These parameters define command attributes that are presented as part of a command. Roo will attempt to automatically convert user-entered values into the Java type of the annotated method parameter. In the example above, Roo will convert the user-entered String to a BatchPropertyName. By default, Roo offers converters for common number types, String, Date, Enum, Locale, boolean and Character. See the org.springframework.roo.shell.converters package for examples if you need to implement a custom converter.

18.4. Operations

Almost all Spring Roo add-ons provide operations types. These types do most of the work behind Roo's passive generation principle (active generation is taken care of by AspectJ Intertype declarations (ITDs) - more about that later). Methods offered by the operations types provided by the add-on are typically invoked by the accompanying "command" type. Alternatively, operations types can also be invoked by other add-ons (this is a rather unusual case).

Implementations of the Operations interface need to be annotated with the Felix @Component and @Service annotations to make their functionality available within Roo's OSGi container. Dependencies can be injected into operations types via the Felix @Reference annotation. If the dependency exists in a package that is not yet registered in the add-on's pom.xml, you need to add the dependency there to add the relevant bundle to the add-on's classpath.

The Add-On Creator generated project includes example code which uses Roo's source path abstractions, file manager and various Util classes that take care of project file management.

Typical functionality offered by operations types include:

  • Adding new dependencies, plugins, & repositories to the Maven project pom.xml.

  • Copying static artefacts from the add-on jar into the user project (i.e. CSS, images, tagx, configuration files, etc).

  • Configuring application contexts, web.xml, and other config artefacts.

  • Managing properties files in the user project.

  • Creating new Java source types in the user project.

  • Adding trigger (or other) annotations to target types (most common), fields or methods.

Spring Roo offers a wide range of abstractions and metadata types that support these use cases. For example, the following services are offered:

  • org.springframework.roo.process.manager.FileManager

    • use file manager for all file system operations in project (offers automatic undo on exception)

  • org.springframework.roo.project.PathResolver

    • offers abstraction over common project paths

  • org.springframework.roo.metadata.MetadataService

    • offers access to Roo metadata bean info metadata for mutators/accessors of target type

  • org.springframework.roo.project.ProjectMetadata

    • project name, top level package read access to project dependencies, repositories, etc

  • org.springframework.roo.project.ProjectOperations

    • add, remove project Maven dependencies, plugins, repositories, filters, properties, etc

In addition the org.springframework.roo.support bundle provides a number of useful utils classes:

  • org.springframework.roo.support.util.Assert

    • similar to Spring’s Assert, exceptions thrown by Assert will cause Roo's File manager abstraction to roll back.

  • org.springframework.roo.support.util.FileCopyUtils

    • useful for copying resources from add-on into project

  • org.springframework.roo.support.util.TemplateUtils

    • useful for obtaining InputStream of resources in bundle

  • org.springframework.roo.support.util.XmlUtils

    • hides XML ugliness

      • writeXml methods

      • Xpath abstraction & cache

      • XML Transformer setup

18.5. Packaging & Distribution

Once your add-on is complete, you can test its functionality locally by generating an OSGi-compliant jar bundle and installing it in the Spring Roo Shell:

<project-name>$ mvn clean install

This will generate your add-on OSGi bundle in the project's target directory. In a separate directory, you can start the Spring Roo Shell and use the following command to test your new add-on:

roo> osgi start --url file:///<path-to-addon-project/target/<addon-bundle-name>.<version>.jar

This should install and activate your new Spring Roo Add-On. For troubleshooting, Roo offers the following OSGi commands:

  • osgi ps - Displays OSGi bundle information & status. This should list your add-on as active.

  • osgi log - Access OSGi container logs. This could identify possible issues occurring during add-on activation.

  • osgi scr list - Lists all currently registered services and components. This should list your add-on's command, metadata provider, and operations types.

  • osgi scr info - Info about a specific component. This can be used to identify possible unresolved dependencies.

  • osgi start - install a new add-on directly from a local or remote location.

  • help osgi - Help on Roo's ~20 osgi commands.

Once you have tested the add-on successfully in your development environment, you can release the add-on source code to your Google Code project, create a tag, and install all relevant artifacts in the project's Maven repository:

<project-name>$ svn add pom.xml src/ legal/ readme.txt

<project-name>$ svn commit -m "initial commit"

<project-name>$ mvn release:prepare release:perform 

The Maven release plugin will ask for tag and release artefact names. Roo follows the OSGi convention of using the major, minor and micro version numbers followed by a textual identifier, e.g. 0.1.1.RELEASE, 0.1.2.BUILD-SNAPSHOT, etc.

Deployment for bundles created with Roo's "wrapping" command can be deployed rather than released. For example, to create a wrapped bundle of the PostgreSQL JDBC driver, use this command:

roo> addon create wrapper --topLevelPackage com.foo.wrapper --projectName spring-roo-postgres-wrapper --artifactId postgresql \
--groupId postgresql --version 9.0-801.jdbc3 --description "Postgres #jdbcdriver driverclass:org.postgresql.Driver." \
--licenseUrl http://jdbc.postgresql.org/license.html --docUrl http://jdbc.postgresql.org/ --vendorName "The PostgreSQL Global Development Group"

This can then be deployed to a Google code project (set up in the same way as described above) with a simple deploy command:

<project-name>$ mvn deploy

18.6. Publishing to RooBot

Once the release is complete, check your Google Code project to see that your add-on's pom.xml has been updated to the new version (e.g. 0.1.2.BUILD-SNAPSHOT), that a new tag has been committed to the tags directory, and that the repo directory has been populated with all the artifacts seen in a typical Maven repository. All artefacts have been signed with your private PGP key, and your public key is available in the relevant .asc files. In the repo directory, you should also find the repository.xml file which contains all relevant information for an OSGi OBR repository.

The URL to the raw (see sidebar) repository.xml artefact can then be registered with RooBot:

Register your new add-on repository by sending an email to s2-roobot@vmware.com where the subject line MUST be the raw URL to OSGi repository.xml. The email body is not currently used (but you can send greetings to the Roo team ;-). Other registration methods are being considered (web front-end, Roo shell command, etc).

RooBot verifies a few aspects before publishing your new add-on to the community:

  • The provided repository.xml must be a valid OSGi repository

  • The resource URI must use the httppgp prefix i.e.: <resource uri="httppgp://fr-test.googlecode.com/svn/…/>

  • The bundle referenced in the repository has a corresponding .asc file containing the PgP public key

  • The public PGP key of the add-on signer needs to be available at http://keyserver.ubuntu.com/ A guide to PGP key management can be found here. Make sure to publish your key with this command:

    gpg --send-keys --keyserver keyserver.ubuntu.com <your-key-id>
  • RooBot will retrieve publicly accessible key information (key owner name, email) from public key server

  • The referenced bundle contains an OSGi-compliant manifest.mf file. For example, it will verify that the add-on version defined in your repository.xml matches the version defined in the manifest of your add-on.

  • [Important] To ensure your repository is valid, RooBot will download all defined resources in the repository. To do that, it will read the uri attribute and perform an HTTP GET request against the defined URL (after replacing the httppgp:// protocol handler with http://). Should the download or verification of any of the defined resources in the respository fail, RooBot will abort the processing of the entire repository and try again later.

If all tests pass, RooBot will publish your add-on in a publicly accessible XML registry http://spring-roo-repository.springsource.org/roobot/roobot.xml. This registry is available to the RooBot client integrated into the Spring Roo Shell.

Once you have sent your email to s2-roobot@vmware.com, you should receive a response from RooBot indicating that the processing of your repository has started. If successful, you will see your add-on listed at http://spring-roo-repository.springsource.org/roobot/roobot.xml within a few hours. If this does not happen, you can visit the RooBot error log at http://spring-roo-repository.springsource.org/roobot/roobot-log.txt, which is refreshed every 5 minutes.

Once RooBot has published your add-on sucessfully, it will periodically process your repository to verify its ongoing validity. As part of this periodic processing, it will also automatically pick up new versions (add-on releases) in your repository.xml. Therefore it should not be necessary to explicitly notify RooBot of any changes in your repository.

18.7. Upgrading Spring Roo Add-Ons from 1.0.x to 1.1.0

As OSGi is the runtime platform for Roo 1.1.0 onwards, porting addons from a previous version will require some small tweaks to your code. Here's a step-by-step guide on what you need to do:

  1. Change packaging of your project to bundle

    As your plugin will result in an OSGi bundle, you need to change the packaging from jar to bundle. This will cause the Maven bundle plugin to create the necessary metadata for you out of the box.

  2. Change the type of the dependencies to bundle

    Similar to the point above, you need to reference dependencies as bundles. Again, let the Maven bundle plugin do its job.

  3. Sync the build section of your pom with the one provided in the addon template

    Compare your add-on's original pom.xml with a pom.xml generated by the addon create command (see below). This is mostly related to the Maven bundle plugin as well as the Maven SCR plugin (see next point for details).

    Example 18.1. Creating a Roo addon project

    addon create simple --topLevelPackage com.mycompany.myproject.roo.addon

    The easiest way to do so is simply creating a dummy addon project using the template and copying the plugin configuration into your pom.


  4. Replace @ScopeDevelopment annotations with @Component and @Service

    Roo uses Apache Felix as OSGi runtime and thus uses @Component and @Service annotations in combination with the Maven SCR plugin[1] to create descriptors for the OSGi declarative services infrastructure.

    Example 18.2. Component declaration with Apache Felix annotations

    @Service
    @Component
    public class MyCommands implements CommandMarker {
    
      @Reference MyOperations operations;
    
      // Your code goes here
    }

    So every @ScopeDevelopment annotation you used in your command and operations classes has to be replaced by @Service and @Component. If you had injected other services into your command or operations class, you can use @Reference to wire them into your component instance. Note that your class will have to implement at least one interface under which Felix can publish the component instance. Check the output of the Maven SCR plugin for errors to see whether any further tweaks are necessary.