Developing a simple add-on

Roo provides an add-on creator add-on which simplifies developing custom add-ons. You can either create a simple or an advanced add-on using the commands exposed by the add-on creator.

A simple add-on is meant to add project dependencies in the pom.xml file or to add configuration artifacts to the project. For instance, in Chapter 4, Web Application Development with Spring Web MVC we saw that Roo installs JSP custom tags when scaffolding a Spring Web MVC application. Instead of using Roo-installed JSP custom tags, you can create a simple Roo add-on which replaces Roo-installed JSP custom tags with the tags that you have tailored based on your application requirements.

An advanced add-on, on the other hand, is required in scenarios in which you want to create new Java classes, interfaces, and AspectJ ITD files. For instance, a Portlet add-on will scaffold controllers and JSPs from JPA entities.

In this recipe, we'll look at the addon create simple command, which creates a simple Roo add-on that replaces some of the tags installed by Roo for a Spring MVC application with custom tags. We'll also see how we can use the newly created add-on in a Roo project.

As mentioned in Chapter 1, Getting Started with Spring Roo, Roo is built on top of the Apache Felix OSGi container and Roo add-ons represent OSGi bundles. As we go through Roo add-on development recipes in this chapter, we'll touch upon some of the OSGi concepts you need to know to understand how add-ons work.

Getting ready

Create a new directory C: oo-cookbookch07-simple-add-on in your system and start the Roo shell from the ch07-simple-add-on directory.

How to do it...

The following steps will demonstrate how to develop a simple add-on:

  1. Execute the addon create simple command, as shown here, to create a com.roo.addon.mysimple add-on project:
    roo> addon create simple --topLevelPackage com.roo.addon.mysimple --description "Mysimple addon" --projectName "Mysimple addon"
    Created ROOTpom.xml
    Created ROOT
    eadme.txt
    Created ROOTlegal
    Created ROOTlegalLICENSE.TXT
    
    Created SRC_MAIN_JAVA...MysimpleCommands.java
    Created SRC_MAIN_JAVAcom...MysimpleOperations.java
    Created SRC_MAIN_JAVA...MysimpleOperationsImpl.java
    Created SRC_MAIN_JAVA...MysimplePropertyName.java
    Created ROOTsrcmainassemblyassembly.xml
    Created SRC_MAIN_RESOURCEScom
    ooaddonmysimpleinfo.tagx
    Created SRC_MAIN_RESOURCEScom
    ooaddonmysimpleshow.tagx
    
  2. Execute the perform eclipse command to create Eclipse IDE-specific configuration files:
    roo> perform eclipse
    
  3. Now, import the com.roo.addon.mysimple Eclipse project into the Eclipse IDE.

How it works...

The addon create simple command creates a Roo add-on, which contributes commands to the Roo shell and defines operations which are invoked in response to the execution of these commands. The package argument specifies the top-level package of the add-on and is also used as the name of the add-on project. The following classes and interfaces are generated by the addon create simple command, and the <last-part-of-top-level-package> refers to the text after the last index of '.' in the value of topLevelPackage argument. In the case of our example, the topLevelPackage argument value is com.roo.addon.mysimple, which makes value of <last-part-of-top-level-package> as mysimple:

  • <last-part-of-top-level-package> Commands.java class: defines methods that are contributed to the Roo shell by the add-on
  • <last-part-of-top-level-package> Operations.java interface: defines methods that contains the majority of processing logic corresponding to Roo commands
  • <last-part-of-top-level-package> OperationsImpl.java class: implements the *Operations.java interface
  • <last-part-of-top-level-package> PropertyName.java enum type: defines the possible values for an argument passed to a Roo command

The add-on generated via the addon create simple command gives you the starting point for custom add-on development. The generated add-on doesn't do much, except show the classes and interfaces that you'll typically create in an add-on. You'll need to modify the generated add-on to perform functions specific to your requirements.

Let's begin with looking at the Java classes and interfaces (created by addon create simple command) which define commands and operations for the com.roo.addon.mysimple add-on.

MysimpleCommands class

The following code listing shows the MysimpleCommands class, which defines the commands that the add-on contributes to the Roo shell:

import java.util.logging.Logger;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.shell.converters.StaticFieldConverter;

@Component
@Service
public class MysimpleCommands implements CommandMarker { 
	
  private Logger log = Logger.getLogger(getClass().getName());

  @Reference private MysimpleOperations operations; 
	
  @Reference private StaticFieldConverter 
     staticFieldConverter;

  protected void activate(ComponentContext context) {
    staticFieldConverter.add(MysimplePropertyName.class);
  }

  protected void deactivate(ComponentContext context) {
    staticFieldConverter.remove(MysimplePropertyName.class);
  }
  ...
}

The following are some of the important points to note about the MysimpleCommands class:

  • MysimpleCommands class defines the Roo commands that the mysimple add-on contributes to the Roo shell via @CliCommand annotated methods. We'll discuss commands contributed by a mysimple add-on later in this section. When a Roo command is executed from the shell, it results in execution of the corresponding @CliCommand annotated method in the MysimpleCommands class.
  • MysimpleCommands class is annotated with @Component and @Service Apache Felix annotations. @Component and @Service annotations have source-level retention and are used by Apache Felix Maven SCR Plugin (http://felix.apache.org/site/apache-felix-maven-scr-plugin.html) to generate XML configuration required by OSGi's Service Component Runtime (SCR) - responsible for managing the lifecycle of the MysimpleCommands component and registering it as a service with OSGi service registry. Annotating MysimpleCommands with @Component and @Service annotations ensures that you can access MysimpleCommands object (using @Reference Apache Felix annotation) from other Roo add-ons, if required.
  • MysimpleCommands class implements Roo's CommandMarker interface. CommandMarker is a marker interface, that is, it doesn't declare any methods. Roo looks for components implementing CommandMarker interface to identify components that contribute commands to the Roo shell.
  • @Reference annotation of Apache Felix is like @Autowired annotation of Spring, and is used to resolve service dependencies of a component. MysimpleOperations and StaticFieldConverter are service dependencies of the MysimpleCommands component. MysimpleOperations and StaticFieldConverter services are accessible to MysimpleCommands via the @Reference annotation because the classes implementing MysimpleOperations and StaticFieldConverter interfaces are also annotated with @Service and @Component annotations—making them accessible to other Roo add-ons.
  • MysimpleOperations defines methods that implement the major part of the functionality performed by Roo commands contributed by the add-on. These methods are invoked by the methods defined in the MysimpleCommands class.
  • StaticFieldConverter represents a Spring Converter that provides type-safety for the argument values that are passed from the Roo shell to the corresponding @CliCommand annotated methods in the MysimpleCommands class.
  • As Roo add-ons are deployed as OSGi bundles on the underlying Apache Felix OSGi container, the activate and deactivate methods represent lifecycle methods that are called by the OSGi container to activate and deactivate the Roo add-on, respectively. In the case of the mysimple add-on, the activate method adds MysimplePropertyName enum type to the StaticFieldConverter implementation. In the deactivate method, the mysimple add-on removes the MysimplePropertyName enum type from the StaticFieldConverter implementation.
  • Now that we see the big picture about the role played by the MysimpleCommands class in mysimple add-on, let's look at the methods in the MysimpleCommands class that register commands with the Roo shell and process these Roo commands when they are executed from the Roo shell.

Defining Roo commands

The following code listing shows the methods of the MysimpleCommands class that define Roo commands exposed by the mysimple add-on:

import org.springframework.roo.shell.CliCommand;
import org.springframework.roo.shell.CliOption;
...

@Reference private MysimpleOperations operations; 

@CliCommand(value = "say hello", 
  help = "Prints welcome message to the Roo shell")
public void sayHello(
   @CliOption(key = "name", mandatory = true, 
     help = "State your name") String name, 
   @CliOption(key = "countryOfOrigin", mandatory = false, 
     help = "Country of orgin") MysimplePropertyName country) {

    log.info("Welcome " + name + "!");
   ...
}

@CliCommand(value = "web mvc install tags", 
  help="Replace default Roo MVC tags used for scaffolding")
public void installTags() {
   .installTags();
}

In the preceding code, we can see that:

  • The @Reference annotation performs autowiring by type and binds the reference to the service that implements the MysimpleOperations interface. As MysimpleOperationsImpl implements the MysimpleOperations interface, reference to the MysimpleOperationsImpl object is injected into the MysimpleCommands instance.
  • @CliCommand is a method-level Roo annotation which identifies methods which contribute commands to the Roo shell. The value attribute specifies the name of the command that is contributed by the add-on to the Roo shell. For instance, the mysimple add-on contributes say hello and web mvc install tags commands to the Roo shell. The help attribute specifies the help text that is displayed against the command when you execute the help Roo command. When a Roo command is executed from the Roo shell, the corresponding @CliCommand annotated method is executed by Roo. For instance, if you execute the say hello command from the Roo shell, Roo executes the sayHello method of the MysimpleCommands class, which prints a welcome message on the Roo shell using Java Logging API. Similarly, if you execute the web mvc install tags command, Roo executes the installTags method of the MysimpleCommands class. The installTags method invokes the installTags method of the MysimpleOperationsImpl class, which copies info.tagx and show.tagx tag files into your Roo project. Later in this recipe we'll look in detail at the installTags method of the MysimpleOperationsImpl class.
  • The @CliOption method-parameter-level Roo annotation specifies the arguments that a Roo command accepts. The key attribute specifies the name of the command argument, the mandatory attribute specifies if the argument is mandatory or optional, and the help attribute specifies the help text associated with the argument. For instance, the say hello command accepts two arguments—name and countryOfOrigin. The name argument is mandatory and countryOfOrigin is optional.

The Java type of an argument can be a simple String or it could be a complex type. In the case of the say hello command, the name argument is of type String and countryOfOrigin is of type MysimplePropertyName. Roo provides converters for common Java types, such as String, Date, Enum, Locale, boolean, and so on. You can also create your custom converters and register them with Roo as an OSGi service. Roo makes use of registered converters to convert the value specified for the argument into the Java type expected by the method. In the case of the mysimple add-on, Roo converts the value entered for the countryOfOrigin argument of the say hello Roo command to the MysimplePropertyName type. We'll see later in this recipe that using MysimplePropertyName (an enum) as the Java type of the countryOfOrigin Roo argument provides tab-completion feature for the argument value. We saw earlier that the MysimplePropertyName class is added to the StaticFieldConverter instance in the activate method. This is to allow StaticFieldConverter to convert the value of the countryOfOrigin argument in the say hello Roo command to the MysimplePropertyName type.

Let's now look at how to make a Roo command unavailable to the Roo shell if certain pre-conditions are not met.

Making Roo commands unavailable

If certain pre-conditions are not met, you may want to make a Roo command unavailable to the Roo shell. For instance, if you have not yet created a Roo project using the project command, then Roo doesn't allow you to set up a JPA persistence provider using the persistence setup command.

  • The @CliAvailabilityIndicator is a method-level Roo annotation that lets you specify the pre-conditions that must be met for a Roo command to be available to the Roo shell. The following code shows the methods in the MysimpleCommands class that define the availability conditions for the say hello and web mvc install tags commands:
    import org.springframework.roo.shell.CliAvailabilityIndicator;
    ...
    
    @Reference private MysimpleOperations operations;
    ...
    
    @CliAvailabilityIndicator("say hello")
    public boolean isSayHelloAvailable() {
      return true; 
    }
    
    @CliAvailabilityIndicator("web mvc install tags")
    public boolean isInstallTagsCommandAvailable() {
      return operations.isInstallTagsCommandAvailable();
    }

In the code, @CliAvailabilityIndicator annotated methods define the availability of the say hello and web mvc install tags commands. The value specified in the @CliAvailabilityIndicator annotation identifies the name of the Roo command for which the method is executed to determine the command's availability. For instance, the isSayHelloAvailable method defines the availability of the say hello command and the isInstallTagsCommandAvailable method defines the availability of the web mvc install tags command. The return type of @CliAvailabilityIndicator annotated methods is boolean and the method must be a public method which doesn't accept any arguments. If the value returned by the @CliAvailabilityIndicator annotated method is true, then it means that the corresponding command is available, else it is unavailable.

As the isSayHelloAvailable method always returns true, the say hello command is always available to the Roo shell. On the other hand, the isInstallTagsCommandAvailable method consults the MysimpleOperations implementation to determine the availability of the web mvc install tags command.

Let's now look at the MysimpleOperationsImpl class, which defines the majority of the logic executed when the mysimple add-on Roo commands are executed from the Roo shell.

The MysimpleOperations interface and MysimpleOperationsImpl class

The MysimpleOperations interface defines three methods as described in the following table:

Method

Description

boolean isInstallTagsCommandAvailable()

Checks if the tags sub-directory exists in the SRC_MAIN_WEBAPP/WEB-INF directory of your Roo project. Returns true if the directory exists.

String getProperty(String)

Accepts a system property as an argument and returns its value.

void installTags()

Copies info.tagx and show.tagx tag files from the mysimple add-on to your Roo project.

The MysimpleOperationsImpl class implements the MysimpleOperations interface. The methods defined in the MysimpleCommands class mainly delegate processing of logic to the implementation of the MysimpleOperations interface. The following code shows the MysimpleOperationsImpl class (methods have not been shown for brevity):

import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.project.ProjectOperations;

@Component
@Service
public class MysimpleOperationsImpl 
   implements MysimpleOperations {
  private static final char SEPARATOR = File.separatorChar;

  @Reference private FileManager fileManager;

  @Reference private ProjectOperations projectOperations;
  ...
}

The MysimpleOperationsImpl class is annotated with @Component and @Service Apache Flex annotations, which means OSGi's SCR is responsible for managing the lifecycle of the MysimpleOperationsImpl component and registering it as a service with the OSGi service registry. Like MysimpleCommands, you can access the MysimpleOperationsImpl instance from other add-ons using the @Reference Apache Felix annotation.

Roo provides many built-in services which simplify add-on development. FileManager and ProjectOperations types represent services provided by Roo for managing files (like creating, reading, updating files, undo capability, and so on) and performing actions on the Roo project (like adding dependencies to the pom.xml file, updating project type, and so on), respectively. FileManager service and ProjectOperations are provided by the Process Manager and Project core modules of Roo, respectively. It is important to note that add-ons are different from core modules in Roo. The core modules provide vital features of the Spring Roo tool, like file system monitoring, registering commands with the Roo shell, and so on. Roo commands provided by add-ons are executed by Spring Roo users for code generation but Roo commands provided by core modules are primarily meant for accessing internal features of Spring Roo, like obtaining metadata, setting polling speed, and so on.

We saw earlier that the isInstallTagsCommandAvailable method of MysimpleOperations is invoked by the isInstallTagsCommandAvailable method of the MysimpleCommands class to check the availability of the web mvc install tags command. The following code shows the isInstallTagsCommandAvailable method of MysimpleOperationsImpl:

public boolean isInstallTagsCommandAvailable() {
    return 
     projectOperations.isProjectAvailable() && 
     fileManager.exists(projectOperations.getProjectMetadata()
     .getPathResolver().getIdentifier(Path.SRC_MAIN_WEBAPP,
     "WEB-INF" + SEPARATOR + "tags"));
}

In the code, the isInstallTagsCommandAvailable method makes use of ProjectOperation services to check if a Roo project exists. The method also makes use of the FileManager service to check if a tags sub-directory exists in the SRC_MAIN_WEBAPP/WEB-INF directory of your Roo project. If a tags directory doesn't exist or you haven't yet created a Roo project, then the method returns false. This means the web mvc install tags Roo command is not available to the Roo shell if you haven't yet created a Roo project which contains a tags sub-directory inside the SRC_MAIN_WEBAPP/WEB-INF directory.

We saw earlier that the installTags method of MysimpleCommands invokes the installTags method of MysimpleOperations. The following code shows the installTags method as implemented by the MysimpleOperationsImpl class:

import org.springframework.roo.process.manager.MutableFile;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.PathResolver;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.support.util.FileCopyUtils;
import org.springframework.roo.support.util.TemplateUtils;
...
public void installTags() {
  PathResolver pathResolver = 
    projectOperations.getProjectMetadata().getPathResolver();

    createOrReplaceFile(..., "info.tagx");
    createOrReplaceFile(..., "show.tagx");
}

private void createOrReplaceFile(String path, String fileName) 
{
  String targetFile = path + SEPARATOR + fileName;

  MutableFile mutableFile = fileManager.exists(targetFile) ? 
   fileManager.updateFile(targetFile) : 
   fileManager.createFile(targetFile);
  try {
    FileCopyUtils.copy(TemplateUtils.getTemplate(getClass(),
    fileName), mutableFile.getOutputStream());
  } catch (IOException e) {
    throw new IllegalStateException(e);
 }
}

In the code, the installTags method copies info.tagx and show.tagx files from the SRC_MAIN_RESOURCE/com/roo/addon/mysimple directory of the mysimple add-on to the SRC_MAN_WEBAPP/WEB-INF/tags directory of your Roo project. The createOrReplace method is the method, which is used by the installTags method to copy the files. The following table describes the classes used by the installTags and createOrReplace methods for copying tag files:

Class

Description

PathResolver

Used to locate files and directories in your Roo project. You can use the ProjectOperations service to obtain reference to PathResolver.

MutableFile

Represents a file in your Roo project, which you want to create, modify, or delete. You can use the FileManager service to obtain reference to the MutableFile instance.

FileCopyUtils

Utility class that provides methods for copying resources from the add-on to your Roo project.

TemplateUtils

Utility class that is used to resolve template files in the add-on project. We'll discuss templates in detail later in this recipe.

Let's now look at the MysimplePropertyName enum type, which defines constants for the countryOfOrigin argument of web mvc install tags command.

MysimpleNameProperty enum type

The following code shows the MysimpleNameProperty enum type, which defines constants for the countryOfOrigin argument value of the web mvc install tags command:

public enum MysimplePropertyName {
    AUSTRALIA("Australia"),
    UNITED_STATES("United States"),
    GERMANY("Germany"),
    NOT_SPECIFIED("None of your business!");
    
    private String propertyName;
    
    private MysimplePropertyName(String propertyName) {
        Assert.hasText(propertyName, "Property name required");
    this.propertyName = propertyName;
    }
   ...
}

In the code, constant AUSTRALIA is associated with value Australia, UNITED_STATES is associated with value United States, and so on. We saw earlier that the countryOfOrigin argument is of type MysimplePropertyName . We can only pass String type values for an argument from the Roo shell, so what we should specify as the value of the countryOfOrigin argument, and how it'll get converted to MysimplePropertyName. When you enter a partial value for the countryOfOrigin argument and press the Tab key, Roo internally refers to MysimplePropertyName to find a matching constant. For instance, if you enter au as the value of the countryOfOrigin argument, Roo attempts to find the constant that matches au in MysimpleNameProperty and auto-completes the value. As the matching is case-insensitive, the value au of the countryOfOrigin argument is completed by the Roo shell as AUSTRALIA.

The following diagram summarizes how a simple add-on works:

MysimpleNameProperty enum type

The figure shows that CommandMarker is an interface provided by Roo, and it is implemented by the MysimpleCommands class. The MysimpleCommands class invokes methods of the MysimpleOperationsImpl class to process the commands exposed by the mysimple add-on. The MysimpleCommands and MysimpleOperationsImpl classes use services provided by Roo to perform the desired functionality.

There's more...

In this section we'll look at:

  • How to locally deploy the mysimple add-on for testing Roo commands
  • How tab-completion support is implemented for Roo commands using constants defined in an enum type and a Java class
  • How the @CliAvailabilityIndicator annotation can be used for a method to define availability of multiple Roo commands exposed by the *Command class
  • What templates are in add-ons and how they are typically used
  • Plugins and dependency configuration in the pom.xml file of an add-on

Deploying and running mysimple add-on

Once you have created an add-on, you may want to test its functionality, before making the add-on available to other developers. In this section, we'll look at how to locally deploy and test the mysimple add-on that we created using the create addon simple command.

To use the mysimple add-on, you need to convert it into an OSGi-compliant JAR bundle. To do so, execute mvn clean install from the directory which contains your mysimple add-on project, as shown here:

C:
oo-cookbookch07-simple-add-on> mvn clean install -Dgpg.passphrase=<thephrase>

Here, <thephrase> is the password phrase that you provide for signing add-ons using GnuPG (also referred to as GPG). Refer to the Setting up GnuPG for add-on development recipe for information on how to set up GnuPG and create a password phrase for signing add-ons.

Executing the mvn clean install command creates a com.roo.addon.mysimple-0.1.0.BUILD-SNAPSHOT.jar add-on OSGi bundle in the target directory of the mysimple add-on project. Now, let's look at how to use the mysimple add-on in a Roo project.

Using the mysimple add-on in a Roo project

The following steps will demonstrate how to use an add-on:

  1. Create a new directory C: oo-cookbookch07-addon-test in your system and start the Roo shell from this directory.
  2. Execute the ch07_web_app.roo script to create a flight-app Spring Web MVC project.
  3. Execute the osgi start command to install and activate the mysimple add-on, as shown here:
    roo> osgi start --url file:///C:/roo-cookbook/ch07-simple-add-on/target/com.roo.addon.mysimple-0.1.0.BUILD-SNAPSHOT.jar
    

    Here the url argument specifies the location of the add-on OSGi bundle you want to install and activate.

    The osgi start command installs the mysimple add-on. This command is also used to download and install Roo add-ons that are located on a website by specifying the http:// or httppgp:// URL to the add-on JAR file as the value of the url argument.

  4. To verify that the add-on was successfully installed, type say at the Roo shell and press the Tab key. Roo should autocomplete the command to say hello, as shown here:
    roo> say hello
    
  5. Press the Tab key again to let Roo show the mandatory name argument of the say hello command, as shown here:
    roo> say hello --name
    
  6. Enter Ron as the value of name argument and type -- followed by the Tab key to view the optional arguments of the say hello command:
    roo> say hello --name Ron --
    
  7. As the only optional argument of the say hello command is countryOfOrigin, it is displayed on the Roo shell:
    roo> say hello --name Ron --countryOfOrigin
    
  8. Now, press Tab again to view the argument values that can be passed to the countryOfOrigin argument. You'll see the following output:
    roo> say hello --name Ron --countryOfOrigin
    
    AUSTRALIA        GERMANY          NOT_SPECIFIED    UNITED_STATES
    

    The output shows that countryOfOrigin can accept only one of the four possible values: AUSTRALIA, GERMANY, NOT_SPECIFIED, and UNITED_STATES.

  9. Enter aus as the value of the countryOfOrigin argument and press the Tab key to let Roo perform autocompletion of the value, as shown here:
    roo> say hello --name Ron --countryOfOrigin aus
    
    roo> say hello --name Ron --countryOfOrigin AUSTRALIA
    

    As shown, Roo performs autocompletion of value for the countryOfOrigin argument. The possible values for the countryOfOrigin argument come from the MysimplePropertyName enum type.

  10. Now, press Enter to let the mysimple add-on process the say hello command. You'll see an output like the following:
    ~.web.controller roo> say hello --name Ron --countryOfOrigin AUSTRALIA
    
    Welcome Ron!
    Country of origin: Australia
    It seems you are a running JDK 1.6.0_23
    You can use the default JDK logger anywhere in your add-on to send messages to the Roo shell
    

    When the say hello command is executed, it is processed by the sayHello method of the MysimpleCommands class.

  11. Now, execute web mvc install tags of the mysimple add-on to install the info.tagx and show.tagx files to the flight-app Roo project:
    roo> web mvc install tags
    
    Created SRC_MAIN_WEBAPPWEB-INF	agsutilinfo.tagx
    Updated SRC_MAIN_WEBAPPWEB-INF	agsformshow.tagx
    

    The output of executing web mvc install tags shows that the info.tagx file is added to the flight-app project and the show.tagx file is replaced. The web mvc install tags command is processed by the installTags method of the MysimpleCommands class, which delegates to the installTags method of the MysimpleOperationsImpl class.

If you make modifications to the mysimple add-on and want to re-deploy it to Spring Roo, then use the osgi update command, as shown here:

roo> osgi update --url file:///C:/roo-cookbook/ch07-simple-add-on/target/com.roo.addon.mysimple-0.1.0.BUILD-SNAPSHOT.jar

If you want to uninstall the mysimple add-on, then use the osgi uninstall command, as shown here:

roo> osgi uninstall --bundleSymbolicName com.roo.addon.mysimple

The bundleSymbolicName argument identifies the name of the add-on to be uninstalled from Spring Roo. Once an add-on is uninstalled, Roo commands exposed by that add-on are no longer available to the Roo shell.

Tab-completion feature with constant values

The MysimplePropertyName enum type defines constants, which represent the possible values that an argument of a Roo command can accept. You are not limited to using enum types to define constants for argument values. Let's say that instead of using the MySimplePropertyName enum type we want to use a Country class that defines constants for countries. The following code shows the Country class that can be used in place of the MySimplePropertyName enum type:

public class Country {
 public static final Country AUSTRALIA = 
     new Country("Australia");
 public static final Country NOT_SPECIFIED = 
     new Country("None of your business!");
 public static final Country UNITED_STATES = 
     new Country("United States");
 public static final Country GERMANY = 
     new Country("Germany");
 
 private String countryName;
 
 public Country(String countryName) {
  this.countryName = countryName;
 }
 
 public String getCountryName() {
  return countryName;
 }
}

The preceding code shows that the Country class defines constants for each country representing a possible value of the countryOfOrigin argument. Now, to use the Country class instead of the MysimplePropertyName enum, all you need to do is to replace references to it with Country in the MysimpleCommands class, as shown here:

public class MysimpleCommands implements CommandMarker { 
 @Reference private StaticFieldConverter staticFieldConverter;

 protected void activate(ComponentContext context) {
  staticFieldConverter.add(Country.class);
 }

 protected void deactivate(ComponentContext context) {
  staticFieldConverter.remove(Country.class);
 }
 
 @CliCommand(value = "say hello", 
    help = "Prints welcome message to the Roo shell")
 public void sayHello(..., @CliOption(key = "countryOfOrigin", 
     mandatory = false, 
     help = "Country of orgin") Country country) {
  
  log.info("Welcome " + name + "!");
  log.warning("Country of origin: " + (country == null ? 
     Country.NOT_SPECIFIED.getCountryName() : 
     country.getCountryName()));
  ...
 }
 ...
}

The preceding code shows that the Country class must be added to the StaticFieldConverter service and must be specified as the type of countryOfOrigin argument in sayHello method.

Multiple command availability using @CliAvailabilityIndicator

In mysimple add-on, separate @CliAvailabilityIndicator annotated methods are used to indicate availability of the say hello and web mvc install tags commands. If you want to use a single @CliAvailabilityIndicator annotated method to indicate availability of multiple Roo commands offered by the mysimple add-on, then specify the command array in the @CliAvailabilityIndicator annotation. For instance, you can define the following method in MysimpleCommands to indicate that the say hello and web mvc install tags commands are always available to the Roo shell:

@CliAvailabilityIndicator({"say hello", 
  "web mvc install tags"})
public boolean isCommandAvailable() {
     return true; 
}

In the preceding code the @CliAvailabilityIndicator annotation specifies an array of Roo commands (say hello and web mvc install tags) whose availability is checked by the isCommandAvailable method.

Templates in Roo add-ons

Templates in an add-on project are resources that are copied to the Roo project when one or more commands of the add-on are executed. For instance, when you execute the web mvc install tags command, the info.tagx and show.tagx files are copied from add-on to the Roo project. Templates can also be images, XML files, properties files, and so on, which the add-on commands copy to the Roo project.

Templates are located inside the SRC_MAIN_RESOURCES directory of an add-on project. For instance, in the case of the mysimple add-on, the info.tagx and show.tagx files are located in the SRC_MAIN_RESOURCES/com/roo/addon/mysimple directory. Add-ons access templates using the TemplateUtils class and then copy it to the Roo project using the FileCopyUtils class. TemplateUtils defines the following two static methods which are used to access templates in the add-on:

  • String getTemplatePath(Class<?> clazz, String templateFilename): this method returns the path to the template file specified via the templateFilename argument. The clazz argument's package information is used to obtain the sub-directory inside the SRC_MAIN_RESOURCES directory that contains the template file.

    For instance, if the clazz argument represents a class whose package name is com.roo.addon.mysimple and the templateFileName argument value is show.tagx, the getTemplatePath method returns the path to the SRC_MAIN_RESOURCES/com/roo/addon/mysimple/show.tagx file. You can also specify the relative path to the template file as the value of the templateFilename argument. For instance, if you specify the value of templateFilename as WEB-INF/myconfig.xml, then the path to the template file becomes SRC_MAIN_RESOURCES/com/roo/addon/mysimple/WEB-INF/myconfig.xml.

  • InputStream getTemplate(Class<?> clazz, String templateFilename): this method returns java.io.InputStream to the template file. In the case of the mysimple add-on, the createOrReplaceFile method of the MySimpleOperationsImpl class makes use of the TemplateUtils class to obtain InputStream to the info.tagx and show.tagx files.

In some add-ons, a template file may be an XML file which the add-ons need to modify before copying it to the Roo project. To modify XML templates, Roo provides an XMLUtils class which add-ons can use to modify the content of XML template files. Let's look at a scenario that shows how add-ons can modify the content of an XML template file before copying it to the Roo project.

The following config.xml file shows a Spring application context XML file which represents a template XML file of an add-on:

beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" ...>
    <context:component-scan base-package=""/>
    ...
</beans>

In the config.xml file, the <component-scan> element of Spring's context namespace specifies the packages (via the base-package attribute) that are scanned by Spring. The classes in these packages (and their sub-packages) that are annotated with the @Component, @Service, and @Repository Spring annotations are auto-registered with Spring's application context. As the add-on copies the config.xml file to a Roo project when a Roo command is executed, the add-on doesn't know in advance the value that needs to be specified for the base-package attribute. This is the reason why the value of the base-package attribute is empty in the config.xml file.

The following code shows how an add-on can read the config.xml file, modify it, and then write the modified config.xml to the Roo project:

import org.springframework.roo.metadata.MetadataService;
import org.springframework.roo.project.PathResolver;
import org.springframework.roo.project.ProjectMetadata;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.io.ByteArrayOutputStream;
import java.io.OutputStreamWriter;

@Component
@Service
public class FileWriterOperationsImpl 
 implements FileWriterOperations {
@Reference private MetadataService metadataService;
 ...
 public void copyApplicationContextXML() {
  ProjectMetadata projectMetadata = 
  (ProjectMetadata) metadataService.get(ProjectMetadata.
                              getProjectIdentifier());

  InputStream templateInputStream =              
      TemplateUtils.getTemplate(getClass(), 
                                  "config.xml");
  Document config;
  try {
    config = XmlUtils.getDocumentBuilder().
                          parse(templateInputStream);
  } catch (Exception ex) {...}

  Element rootElement = (Element) config.getDocumentElement();
  
  XmlUtils.findFirstElementByName("context:component-scan", 
    rootElement).setAttribute("base-package",      
                   projectMetadata.getTopLevelPackage().
                   getFullyQualifiedPackageName());

  ByteArrayOutputStream outputStream = 
      new ByteArrayOutputStream();

  XmlUtils.writeXml(XmlUtils.createIndentingTransformer(), 
                      outputStream, 
                      config);
  String xmlContent = outputStream.toString();

  FileCopyUtils.copy(xmlContent, new OutputStreamWriter(...));
 }
 ...
}

The FileWriterOperationsImpl class is similar to the MysimpleOperationsImpl class of the mysimple add-on. It defines the copyApplicationContextXML method which is responsible for copying the config.xml file from the add-on to the Roo project.

The MetadataService class represents a service provided by Roo for retrieving metadata information for the Roo project, Java types, fields, methods, and so on. Metadata is obtained from Roo's MetadataService using a metadata identification string, which has the format: MID:<fully-qualified-class-name>#<instance-identification-key>, where <fully-qualified-class-name> is the metadata type and <instance-identification-key> is the Java type to which the metadata applies. If the metadata is not associated with a Java type, then the metadata string format is: MID:<fully-qualified-class-name>. If the metadata identification string has the format MID:<fully-qualified-class-name>#<instance-identification-key>, then it is referred to as an instance-level metadata identification string. If the metadata identification string has the format MID:<fully-qualified-class-name>, then it is referred to as a class-level metadata identification string. In the FileWriterOperationsImpl class, ProjectMetadata represents a metadata type which holds the Roo project's details, like project name, top-level package name, dependencies, and so on. The getProjectIdentifier() method of ProjectMetadata returns a metadata identification string for the Roo project and is then passed to MetadataService to retrieve the ProjectMetadata instance.

The TemplateUtils class is used to obtain java.io.InputStream to the config.xml file. The XmlUtils class is then used to parse the config.xml file to build the org.w3c.dom.Document instance. The findFirstElementByName method of XmlUtils is used to find the first occurrence of the <context:component-scan> element in config.xml. The findFirstElementByName method returns an instance of org.w3c.dom.Element. The setAttribute method of Element is used to set the value of the base-package attribute of the <context:component-scan> element to the top-level package of the Roo project. The writeXml method of XmlUtils writes the Document object to java.io.OutputStream. The createIndentingTransformer method of XmlUtils creates a javax.xml.transform.Transformer instance, which indents entries in the Document object by 4 characters. If you want to perform a custom transformation of XML, you can create a custom Transformer implementation and pass it to the writeXml method of XmlUtils class.

Let's say you have a Roo project named flight-app whose top-level package is com.sample.flightapp. Now assume that you execute a Roo command which results in the execution of the copyApplicationContextXML method of the FileWriterOperationsImpl class of the add-on. The copyApplicationContextXML method will read the config.xml template file, set the base-package attribute of the <context:component-scan> element to com.sample.flightapp and write the modified config.xml to the flight-app Roo project.

Now let's look at some of the important configurations defined in the pom.xml file of the mysimple add-on project.

The pom.xml file

The pom.xml file of an add-on created via the addon create simple command contains the following configurations:

  • The core Spring Roo modules on which a simple add-on depends is configured in the pom.xml file. If your add-on makes use of other add-ons, then you'll need to configure it in the pom.xml file.
  • By default Google Code is configured as the SCM (Software Configuration Management) repository for the add-on.
  • The Maven assembly plugin is configured for packaging the add-on. You can execute perform assembly Roo command or assembly:single goal of the assembly plugin to package the add-on as a ZIP file. The assembly description, assembly.xml, is located in the src/main/assembly folder of add-on.
  • The Maven release plugin is configured for releasing the add-on. Once you are done with local testing of your add-on, you can release the add-on to Google Code (or the SCM you configured in the pom.xml file) by executing the mvn release:prepare release:perform Maven command.
  • The Maven GPG plugin is configured to sign add-on project artifacts using GnuPG.
  • The Maven bundle plugin is configured to package an add-on as an OSGi compliant bundle. You'll find that the <packaging> element's value is specified as bundle in the pom.xml file of add-on, which means that the add-on is packaged as an OSGi bundle.

OSGi commands for troubleshooting

Once you have deployed an add-on, you can check if it was successfully installed or not by using the following OSGi commands from the Roo shell:

  • osgi ps: lists the OSGi bundles and their status. If you have successfully installed the mysimple add-on, then executing the osgi ps command should show the mysimple add-on as active, as shown here:
    [Active] [1] Mysimple addon (0.1.0.BUILD-SNAPSHOT)
    
  • osgi log: shows the OSGi container logs. If your add-on fails to install successfully, you can refer to the container logs to troubleshoot installation issues.
  • osgi scr list: lists services and components registered with the OSGi container. If you have successfully installed the mysimple add-on, then executing the osgi scr list command should show commands and operation types, as shown here:
    [181] [active] com.roo.addon.mysimple.MysimpleOperationsImpl
    [180] [active] com.roo.addon.mysimple.MysimpleCommands
    

    In the preceding output, numbers 180 and 181 denote the component IDs assigned by the OSGi container to the MysimpleCommands and MysimpleOperations types respectively.

  • osgi scr info: shows detailed information about a component or service registered with the OSGi container. This command accepts a mandatory argument, componentId. You can use this command to find unresolvable dependencies of a component. If you have successfully installed the mysimple add-on, then executing osgi scr info --componentId 180 (substitute the component ID of MysimpleCommands as displayed by executing the osgi scr list command) should show if the dependencies of MysimpleCommands were satisfied or not, as shown here:
    ID: 180
    Name: com.roo.addon.mysimple.MysimpleCommands
    State: active
    Services: org.springframework.roo.shell.CommandMarker
    ...
    Reference: staticFieldConverter
    Satisfied: satisfied
    Service Name: org.springframework.roo.shell.converters.StaticFieldConverter
    ...
    Reference: operations
    Satisfied: satisfied
    Service Name: com.roo.addon.mysimple.MysimpleOperations
    

    The output shows that the StaticFieldConverter and MysimpleOperations dependencies of MysimpleCommands were resolved successfully.

See also

  • Refer to the Developing an advanced add-on recipe to see how to develop an add-on, which creates new Java classes, interfaces, and AspectJ ITD files
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.162.37