Developing an advanced add-on

If you want to generate Java code (classes and interfaces) and AspectJ ITDs in response to the execution of one or more Roo commands, then you should create an advanced Roo add-on.

Spring Roo treats both simple and advanced add-ons the same way. The distinction between simple and advanced add-ons exists so that you can choose an appropriate add-on template based on your custom add-on requirement. The add-on template created by the addon create simple command is useful if you want to create a custom add-on meant for adding project dependencies to the pom.xml file and for adding configuration artifacts to the project. In this recipe we'll look at the addon create advanced command, which is useful if you want to create a custom add-on to generate Java code and AspectJ ITD.

Getting ready

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

How to do it...

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

  1. Execute the addon create advanced command, as shown here, to create a com.roo.addon.myadvanced add-on project:
    ..roo> addon create advanced --topLevelPackage com.roo.addon.myadvanced
    ...
    Created SRC_MAIN_JAVA...MyadvancedCommands.java
    Created SRC_MAIN_JAVA...MyadvancedOperations.java
    Created SRC_MAIN_JAVA...MyadvancedOperationsImpl.java
    Created SRC_MAIN_JAVA...MyadvancedMetadata.java
    Created SRC_MAIN_JAVA...MyadvancedMetadataProvider.java
    Created SRC_MAIN_JAVA...RooMyadvanced.java
    Created SRC_MAIN_RESOURCES...configuration.xml
    

    The output only shows some of the important files generated by the Roo command that we'll discuss them in this recipe.

  2. Execute the perform eclipse command to create Eclipse-IDE specific configuration files:
    roo> perform eclipse
    

    Now, import the com.roo.addon.myadvanced Eclipse project into Eclipse IDE.

How it works...

The addon create advanced command creates a Roo add-on template which you can use as the starting point to create a custom Roo add-on which generates Java classes, interfaces and AspectJ ITDs. The following classes and interfaces are generated by the addon create advanced command. The <last-part-of-top-level-package> refers to the text after the last index of '.' in the value of the topLevelPackage argument. In case of our example, the topLevelPackage argument value is com.roo.addon.myadvanced, which makes value of <last-part-of-top-level-package> as 'myadvanced'.

  • <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 contain the processing logic for commands defined in the *Commands.java class.
  • <last-part-of-top-level-package>OperationsImpl.java class: implements the *Operations.java interface.
  • <last-part-of-top-level-package>Metadata.java class: represents the metadata associated with this add-on. In this class you write the code for creating Java classes, interfaces, and AspectJ ITDs.
  • <last-part-of-top-level-package>MetadataProvider.java class: creates the metadata associated with this add-on.
  • Roo<last-part-of-top-level-package>.java class: represents the Roo annotation (similar to other Roo annotations like @RooEntity, @RooJavaBean, @RooSolrSearchable, and so on) which triggers this add-on to generate Java classes, interfaces, and AspectJ ITDs.

You may have noticed that a configuration.xml file is also generated in the SRC_MAIN_RESOURCEScom ooaddonmyadvanced directory. The configuration.xml file defines dependencies that are added by the add-on to the pom.xml file of the Roo project.

The MyadvancedCommands class

The following code shows the methods of the MyAdvancedCommands class that defines Roo commands exposed by the myadvanced add-on:

@Component
@Service
public class MyadvancedCommands implements CommandMarker {
 
 @Reference private MyadvancedOperations operations;
 
 @CliAvailabilityIndicator({ "myadvanced setup", 
   "myadvanced add", "myadvanced all" })
 public boolean isCommandAvailable() {
   return operations.isCommandAvailable();
 }
 
 @CliCommand(value = "myadvanced add", 
    help = "Some helpful description")
 public void add(@CliOption(key = "type", 
   mandatory = true, 
   help = "The java type to apply this annotation to") 
  JavaType target) {
     operations.annotateType(target);
 }
 
 @CliCommand(value = "myadvanced all", 
    help = "Some helpful description")
 public void all() {
  operations.annotateAll();
 }
 
 @CliCommand(value = "myadvanced setup", 
   help = "Setup Myadvanced addon")
 public void setup() {
  operations.setup();
 }
}

As the code shows, the myadvanced add-on registers the following commands with the Roo shell:

  • myadvanced setup: performs the initial setup that is required for using the add-on. When this command is executed, the myadvanced add-on updates project dependencies in the pom.xml file.
  • myadvanced add: annotates the Java class (specified via the type argument of the myadvanced add command) with the @RooMyadvanced annotation. In the preceding code, you'll notice that the Java class passed to the add method is of type JavaType. The JavaType represents a Roo-specific class that simplifies accessing simple and package names of a Java type. Annotating a Java type with the @RooMyAdvanced annotation kicks off code generation by the myadvanced add-on.
  • myadvanced all: finds all the Java types in the project that are annotated with the @RooJavaBean annotation (soon we'll see how the myadvanced add-on does this), and annotates them with the @RooMyAdvanced annotation. Annotating a Java type with the @RooMyAdvanced annotation kicks off code generation by the myadvanced add-on.

The MyAdvancedCommands class also defines a @CliAvailabilityIndicator annotated method, which decides the availability of the myadvanced setup, myadvanced add, and myadvanced all commands.

Let's now look at the MyadvancedOperationsImpl class, which provides implementations for the commands exposed by the myadvanced add-on.

The MyadvancedOperationsImpl class

The following code shows the MyadvancedOperationsImpl class. The code doesn't show the implementation of the annotateType and annotateAll methods, which will be discussed in detail later in this section.

import ...roo.classpath.PhysicalTypeMetadataProvider;
import ...roo.classpath.TypeLocationService;
import ...roo.metadata.MetadataService;
import ...roo.model.JavaType;
import ...roo.project.ProjectOperations;
import ...roo.project.Dependency;
import ...roo.project.DependencyScope;
import ...roo.project.DependencyType;
import ...roo.project.Repository;
import ...roo.support.util.XmlUtils;
import org.w3c.dom.Element;

@Component
@Service
public class MyadvancedOperationsImpl implements 
    MyadvancedOperations {
 
 @Reference private MetadataService metadataService;
 @Reference private PhysicalTypeMetadataProvider 
    physicalTypeMetadataProvider;
 @Reference private ProjectOperations projectOperations;
 @Reference private TypeLocationService typeLocationService;
 ...
 
 public boolean isCommandAvailable() {
  return projectOperations.isProjectAvailable();
 }

 public void annotateType(JavaType javaType) { ... }

 public void annotateAll() { ... }
 public void setup() {
  projectOperations.addRepository(
    new Repository("Myadvanced Roo add-on repository", 
    "Myadvanced Roo add-on repository", 
     "https://com-roo-addon-
        myadvanced.googlecode.com/svn/repo"));
  
  List<Dependency> dependencies = new ArrayList<Dependency>();
  dependencies.add(
   new Dependency("com.roo.addon.myadvanced", 
     "com.roo.addon.myadvanced", "0.1.0.BUILD-SNAPSHOT",  
     DependencyType.JAR, DependencyScope.PROVIDED));
  
  for (Element dependencyElement : 
    XmlUtils. findElements("/configuration/batch/" +
     "dependencies/dependency", 
    XmlUtils.getConfiguration(getClass()))) {
     dependencies.add(new Dependency(dependencyElement));
  }
 
  projectOperations.addDependencies(dependencies);
 }
}

The MyadvancedOperationsImpl class references the following services offered by Roo:

  • MetadataService: service provided by Roo for retrieving metadata information for the Roo project, Java types, fields, methods, and so on. Refer to the Developing a simple add-on recipe for more details.
  • PhysicalTypeMetadataProvider: a metadata provider that provides metadata for a class, interface, enum, or annotation type. As we'll see later in this section, this metadata provider is used by the MyadvancedOperationsImpl class to obtain the metadata information about the Java type that needs to be annotated with the @RooMyadvanced annotation.
  • ProjectOperations: this is used by the myadvanced add-on in the isCommandAvailable and setup methods to check if a Roo project exists and to modify the pom.xml file of the Roo project, as shown in the given code. The isCommandAvailable method in the given code makes use of the isProjectAvailable method of ProjectOperations to determine if a Roo project has been created. The isCommandAvailable method is invoked by the @CliAvailabilityIndicator annotated method of the MyadvancedCommands class. The setup method in this code makes use of the addRepository method of ProjectOperations to add repository information of the add-on to the Roo project's pom.xml file. By default, the repository refers to the repo directory of the Google Code project of the add-on (refer to the pom.xml file of the myadvanced add-on). The setup method adds Roo project's dependency on the myadvanced add-on and the dependencies defined in the configuration.xml (inside SRC_MAIN_RESOURCES/com/roo/addon/myadvanced) file by using the addDependencies method. The setup method makes use of the getConfiguration method of XmlUtils class to load the configuration.xml file.
  • TypeLocationService: a Roo service that helps with locating Java types in the Roo project. For instance, you can find Java types annotated with a particular annotation using TypeLocationService.
  • The annotateType method of the MyadvancedOperationsImpl class is invoked when the myadvanced add command is executed. The following code shows the implementation of the annotateType method:
    import ...roo.classpath.PhysicalTypeDetails;
    import ...roo.classpath.PhysicalTypeMetadata;
    import ...roo.classpath.PhysicalTypeMetadataProvider;
    import ...roo.classpath.TypeLocationService;
    import ...roo.classpath.details.MemberFindingUtils;
    import ...roo.classpath.details.MutableClassOrInterfaceTypeDetails;
    import ...roo.classpath.details.annotations.AnnotationMetadataBuilder;
    ...
    @Reference private MetadataService metadataService;
    @Reference private PhysicalTypeMetadataProvider 
                            physicalTypeMetadataProvider;
    ...
    public void annotateType(JavaType javaType) {
      String id = 
         physicalTypeMetadataProvider.findIdentifier(javaType);
    
      PhysicalTypeMetadata physicalTypeMetadata =
         (PhysicalTypeMetadata) metadataService.get(id);
    
      PhysicalTypeDetails physicalTypeDetails =   
         physicalTypeMetadata.getMemberHoldingTypeDetails();
    
      MutableClassOrInterfaceTypeDetails mutableTypeDetails =
        (MutableClassOrInterfaceTypeDetails) physicalTypeDetails;
    
      if (MemberFindingUtils.getAnnotationOfType(
           mutableTypeDetails.getAnnotations(), 
        new JavaType(RooMyadvanced.class.getName())) == null) {
       
       JavaType rooRooMyadvanced = 
         new JavaType(RooMyadvanced.class.getName());
       AnnotationMetadataBuilder annotationBuilder = 
         new AnnotationMetadataBuilder(rooRooMyadvanced);
       
       mutableTypeDetails.addTypeAnnotation(
         annotationBuilder.build()
       );
      }
     }

    The annotateType method accepts a JavaType argument. The JavaType argument represents the Java type that you specified as the value of the type argument of the myadvanced add command. The annotateType method performs the following actions to annotate the JavaType argument with the @RooMyadvanced annotation:

  • Obtains metadata identification string for the JavaType on which the annotation needs to be applied. This is achieved by using the findIdentifier method of the PhysicalTypeMetadataProvider class of Roo.

    Let's say that the myadvanced add command is executed as shown here:

    ... roo> myadvanced add --type sample.roo.flightapp.domain.Flight
    

    In the myadvanced add command, the value of the type argument is sample.roo.flightapp.domain.Flight. This Java type is passed to the annotateType method of the MyadvancedOperationsImpl class by Roo. The metadata identification string returned by the findIdentifier method of PhysicalTypeMetadataProvider is as follows:

    MID:org.springframework.roo.classpath.PhysicalTypeIdentifier#SRC_MAIN_JAVA?sample.roo.flightapp.domain.Flight
  • Uses the metadata identification string of the Java type to obtain the PhysicalTypeMetadata object, which represents the metadata information about the Java type.
  • Makes use of the getMemberHoldingTypeDetails method of PhysicalTypeMetadata to retrieve PhysicalTypeDetails. The PhysicalTypeDetails provides details of the Java type represented by PhysicalTypeMetadata.
  • Casts the PhysicalTypeDetails into MutableClassOrInterfaceTypeDetails. The MutableClassOrInterfaceTypeDetails is used to modify the Java type represented by it, which is the JavaType argument passed to the annotateType method of MyadvancedOperationsImpl class. Amongst other things, you can use the MutableClassOrInterfaceTypeDetails object to add or remove fields, methods, and annotations from the Java type.

    Note

    It is important to note that starting with Spring Roo 1.2.x, modifications to a Java type are performed using Roo's TypeManagementService instead of Roo's MutableClassOrInterfaceTypeDetails.

  • Uses MemberFindingUtils utility class to check if the Java type is already annotated with the @RooMyadvanced annotation.
  • Uses AnnotationMetadataBuilder to create the @RooMyAdvanced annotation, and adds it to the Java type using the MutableClassOrInterfaceTypeDetails object.
  • The following code shows the annotateAll method of the MyadvancedOperationsImpl class, which adds the @RooMyadvanced annotation to all the Java types annotated with the @RooJavaBean annotation:
    import ...roo.classpath.TypeLocationService;
    import ...roo.model.JavaType;
    ...
    public void annotateAll() {
      for (JavaType type:   
         typeLocationService.findTypesWithAnnotation(
         new JavaType("org.springframework.roo.addon. " +
           "javabean.RooJavaBean"))) {
         annotateType(type);
      }
    }

As shown in the preceding code, the annotateAll method makes use of TypeLocationService to find Java types that are annotated with the @RooJavaBean annotation, and then invokes the annotateType method to annotate Java types with the @RooMyadvanced annotation.

Let's now look at how the myadvanced add-on triggers code generation using the MyadvancedMetadataProvider class.

The MyadvancedMetadataProvider class

The MyadvancedMetadataProvider class represents an OSGi component which creates a MyadvancedMetadata instance when a Java type is annotated with the @RooMyadvanced annotation. The MyadvancedMetadata in turn creates an AspectJ ITD and adds a method and a field to it. In this section, we'll look at how Roo-generated MyadvancedMetadataProvider is implemented.

The following code shows some of the methods of the MyadvancedMetadataProvider class:

import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.component.ComponentContext;
import ...roo.classpath.PhysicalTypeIdentifier;
import ...roo.classpath.PhysicalTypeMetadata;
import ...roo.classpath.itd.AbstractItdMetadataProvider;
import ...roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.Path;

@Component
@Service
public final class MyadvancedMetadataProvider extends AbstractItdMetadataProvider {

 protected void activate(ComponentContext context) {
  metadataDependencyRegistry.
    registerDependency(PhysicalTypeIdentifier.
    getMetadataIdentiferType(), getProvidesType());

  addMetadataTrigger(
    new JavaType(RooMyadvanced.class.getName()));
 }
 
 protected void deactivate(ComponentContext context) {
  metadataDependencyRegistry.
    deregisterDependency(PhysicalTypeIdentifier.
    getMetadataIdentiferType(), getProvidesType());
  
  removeMetadataTrigger(
    new JavaType(RooMyadvanced.class.getName())); 
 }
 ...
}

The preceding code shows that MyadvancedMetadataProvider extends Roo's AbstractItdMetadataProvider abstract class. The AbstractItdMetadataProvider class defines the common functionality required by add-ons that generate AspectJ ITDs. The activate method is invoked by the Apache Felix OSGi container when the myadvanced add-on is installed.

Dependency registration and unregistration

In Chapter 4, Web Application Development with Spring Web MVC you saw that if you redefine a method of the *_Roo_Controller.aj file in the *Controller.java class, then Roo automatically removes that method from the *_Roo_Controller.aj file. We also saw that when you modify a @RooEntity or @RooWebScaffold annotation, it results in modification of corresponding AspectJ ITDs. Roo maintains dependency between Java types (which could be a class, interface, or a @Roo* annotation) of the Roo project using metadata identification strings, making it possible for Roo to manage code contained in AspectJ ITD when changes are made to a Java type.

Let's now look closely at the following code snippet in the activate method of MyadvancedMetadataProvider class:

metadataDependencyRegistry.
    registerDependency(PhysicalTypeIdentifier.
    getMetadataIdentiferType(), getProvidesType());

The metadataDependencyRegistry is a protected attribute defined in Roo's AbstractItdMetadataProvider class and is of type MetadataDependencyRegistry. The MetadataDependencyRegistry instance keeps track of dependencies between metadata identification strings. The registerDependency method is used to specify dependency between metadata identification strings. PhysicalTypeIdentifier represents a Roo class that creates a metadata identification string for a Java type in Roo project.

In the previous code, the PhysicalTypeIdentifier.getMetadataIdentiferType() code returns MID:org.springframework.roo.classpath.PhysicalTypeIdentifier, and MyadvancedMetadata.getMetadataIdentiferType() returns MID:com.roo.addon.myadvanced.MyadvancedMetadata. As both the metadata identification strings don't contain the Java type to which they apply, they are class-level metadata identification strings. You can create dependencies between class level-or instance-level metadata identification strings.

The MID:org.springframework.roo.classpath.PhysicalTypeIdentifier represents the upstream dependency and MID:com.roo.addon.myadvanced.MyadvancedMetadata represents the downstream dependency. When changes are made to an upstream dependency, Roo takes care of notifying all the downstream dependencies, which results in recreating the downstream metadata. The MetadataProviders are responsible for handling the notification. So, in the case of the myadvanced add-on, when a Java type (represented by MID:org.springframework.roo.classpath.PhysicalTypeIdentifier) in the Roo project is changed, it notifies the MyadvancedMetadataProvider instance.

The metadata dependencies specified in the activate method should be unregistered in the deactivate method of the metadata provider. The metadata dependencies are unregistered using the deregisterDependency method of the MetadataDependencyRegistry instance, as shown here for MyadvancedMetadataProvider:

protected void deactivate(ComponentContext context) {
  metadataDependencyRegistry.
    deregisterDependency(PhysicalTypeIdentifier.
    getMetadataIdentiferType(), getProvidesType());
  
   ...
}

Registering and unregistering metadata creation trigger

Metadata dependency registration ensures that downstream dependencies of a metadata are notified when changes occur in the upstream dependencies. To specify what triggers creation of metadata, metadata provider makes use of the addMetadataTrigger method of the AbstractItdMetadataProvider class, as shown here for MyadvancedMetadataProvider:

protected void activate(ComponentContext context) {
  metadataDependencyRegistry.
    registerDependency(PhysicalTypeIdentifier.
    getMetadataIdentiferType(), getProvidesType());

  addMetadataTrigger(
    new JavaType(RooMyadvanced.class.getName()));
 }

In the preceding code, the addMetadataTrigger method accepts RooMyadvanced Java type. It means that whenever a Java type is annotated with @RooMyadvanced annotation, MyadvancedMetadataProvider will create an instance of MyadvancedMetadata.

The metadata trigger is removed by the metadata provider in the deactivate method, as shown here for the MyadvancedMetadataProvider class:

protected void deactivate(ComponentContext context) {
  ...
  removeMetadataTrigger(
    new JavaType(RooMyadvanced.class.getName())); 
 }

Let's now look at how Spring Roo works behind the scenes to generate code when a Java type is annotated with the @RooMyadvanced annotation.

Code generation functionality of add-ons

The following sequence diagram shows how the myadvanced add-on processes the myadvanced add --type sample.roo.flightapp.domain.Flight command, where the Flight class represents a JPA entity in your Roo project to which you want to add the @RooMyadvanced annotation.

Code generation functionality of add-ons

The figure shows that when the myadvanced add command is executed, the add method of MyadvancedCommands instance is invoked and the type argument value is passed to the add method. You may notice that the add method accepts an argument of JavaType type, but we didn't register a custom converter with the Roo shell for it. This is because Roo is responsible for converting the type argument value to JavaType type. The JavaType contains the simple and package name information about the type argument value. In our case, JavaType argument passed to the add method contains simple and package name information about the Flight class that we specified as value of the type argument. The add method of MyadvancedCommands invokes the annotateType method of the MyadvancedOperationsImpl class and passes the JavaType instance containing information about the Flight class. The annotateType method annotates the Flight.java file with the @RooMyadvanced annotation.

Annotating Flight.java with the @RooMyadvanced annotation results in issuing a notification to the file monitor service of Roo that the Flight.java file has been modified. The following sequence diagram shows how file monitor service of Roo notifies change in Flight.java file to MetadataDependencyRegistry:

Code generation functionality of add-ons

The preceding figure shows that the file monitor service notifies PhysicalTypeMetadaProvider that the Flight.java file has been modified. As discussed earlier, PhysicalTypeMetadaProvider provides metadata for a Java type in the Roo project. PhysicalTypeMetadaProvider is notified when Flight.java is modified because PhysicalTypeMetadaProvider is a registered listener for file change events. After receiving the file change notification, PhysicalTypeMetadaProvider asks MetadataDependencyRegistry instance to inform any registered downstream dependencies. We saw earlier that MyadvancedMetadataProvider's activate method registers MID:com.roo.addon.myadvanced.MyadvancedMetadata as the downstream dependency of MID:org.springframework.roo.classpath.PhysicalTypeIdentifier. The MID:org.springframework.roo.classpath.PhysicalTypeIdentifier represents a class-level metadata identification string created by PhysicalTypeMetadataProvider and represents a Java type in the Roo project. So, the change in Flight.java results in notifying the metadata provider that creates the MID:com.roo.addon.myadvanced.MyadvancedMetadata metadata identification string, which is MyadvancedMetadataProvider.

The following sequence diagram shows how MetadataDependencyRegistry notifies MyadvancedMetadataProvider to create MyadvancedMetadata:

Code generation functionality of add-ons

In the preceding figure, MetadataProvider represents an interface that is implemented by all the metadata providers in Roo, and MetadataItem represents the metadata that is created by MetadataProvider implementations. In the case of the myadvanced add-on, MyadvancedMetadataProvider implements the MetadataProvider interface and MyadvancedMetadata implements the MetadataItem interface.

The previous sequence diagram shows that MetadataDependencyRegistry notifies MetadataService to inform the MetadataProvider (which is MyadvancedMetadataProvider in the case of the myadvanced add-on) of the downstream dependency.

Note

MetadataService is a central service in Roo which knows about all the metadata providers in the system. You don't need to register your metadata providers with MetadataService because it can automatically detect an OSGi service as a metadata provider if it implements the MetadataProvider interface.

A MetadataItem (which is MyadvancedMetadata in the case of the myadvanced add-on) is created when MetadataProvider (which is MyadvancedMetadataProvider in the case of the myadvanced add-on) of the downstream dependency is notified, as we'll see shortly. MetadataService makes use of the MetadataIdentificationUtils utility class to obtain the MetadataProvider class corresponding to the metadata identification string of the downstream dependency. Once the MetadataProvider for the downstream dependency is obtained, MetadataService notifies the MetadataProvider.

The MetadataProvider needs to know the Java type for which the MetadataItem is to be created. For instance, in the case of the myadvanced add-on example, MyadvancedMetadataProvider needs to know that MyadvancedMetadata needs to be created corresponding to the Flight.java class. MetadataProvider converts class-level MID (MID:com.roo.addon.myadvanced.MyadvancedMetadata) of downstream dependency into instance-level MID (MID:com.roo.addon.myadvanced.MyadvancedMetadata #SRC_MAIN_JAVA?sample.roo.flightapp.domain.Flight) to identify the Java type for which the MetadataItem is to be created. When the MetadataItem instance is created, it results in code generation.

Now let's look at some of the methods of MyadvancedMetadataProvider, which play an important role in creating the MyadvancedMetadata instance:

import ...roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem;
...
public final class MyadvancedMetadataProvider 
   extends AbstractItdMetadataProvider {
 ...
 protected ItdTypeDetailsProvidingMetadataItem 
  getMetadata(String metadataIdentificationString, 
    JavaType aspectName, 
    PhysicalTypeMetadata governorPhysicalTypeMetadata, 
    String itdFilename) {
  
  return new MyadvancedMetadata(metadataIdentificationString,
     aspectName, governorPhysicalTypeMetadata);
 }
 
 public String getItdUniquenessFilenameSuffix() {
  return "Myadvanced";
 }

 protected String getGovernorPhysicalTypeIdentifier(
   String metadataIdentificationString) {
  
  JavaType javaType = MyadvancedMetadata.
    getJavaType(metadataIdentificationString);
  
  Path path = MyadvancedMetadata.
    getPath(metadataIdentificationString);
  
  return PhysicalTypeIdentifier.createIdentifier(javaType,
    path);
 }
 
 protected String createLocalIdentifier(JavaType javaType, 
  Path path) {

  return MyadvancedMetadata.createIdentifier(javaType, path);
 }

 public String getProvidesType() {
  return MyadvancedMetadata.getMetadataIdentiferType();
 }
}

The MyadvancedMetadataProvider class extends the AbstractItdMetadataProvider abstract class and makes use of the template method design pattern. The methods shown in the preceding code are invoked by the concrete methods defined in the AbstractItdMetadataProvider class. The following table describes the purpose of each of these methods:

Method name

Description

getMetadata

This method is responsible for creating and returning the MetadataItem (which is MyadvancedMetadata in the case of the myadvanced add-on).

getItdUniquenessFilenameSuffix

This method returns the suffix that should be used for naming the AspectJ ITD file. As this method returns "Myadvanced", the name of the AspectJ ITD created by this add-on is *_Roo_Myadvanced.aj.

getGovernorPhysicalTypeIdentifier

Returns the instance-level MID of the Java type that receives the methods defined by the *_Roo_Myadvanced.aj AspectJ ITD file. In our example, Flight is the Java type that receives methods defined by *_Roo_Myadvanced.aj.

getProvidesType

This method returns a class-level MID that identifies the MetadataItem which this MetadataProvider implementation offers. This method delegates the responsibility of creating MID to the MetadataItem implementation class.

createLocalIdentifier

Creates a local instance-level MID for the specified Java type and path arguments. This method delegates the creation of MID to the MetadaItem implementation class.

The getMetadata method is responsible for creating the MyadvancedMetadata by passing information that MyadvancedMetadata is dependent upon. The following table describes the arguments passed to the getMetadata method:

Method argument

Description

metadataIdentificationString

This represents instance-level metadata for MyadvancedMetadata. As Flight.java file was annotated with @RooMyadvanced annotation, the value of this argument is: MID:com.roo.addon.myadvanced.MyadvancedMetadata

#SRC_MAIN_JAVA?sample.roo.flightapp.domain.F

Light. This value is created by the superclass of the metadata provider by invoking the createLocalIdentifier method and getProvidesType methods.

aspectName

This represents a JavaType corresponding to the AspectJ ITD file created by the add-on.

governorPhysicalTypeMetadata

Represents the PhysicalTypeMetadata instance that identifies the Java type corresponding to which the AspectJ ITD file is to be created. As the Flight.java file was annotated with @RooMyadvanced annotation, this argument represents the PhysicalTypeMetadata corresponding to Flight.java class.

itdFilename

This represents the name of the AspectJ ITD file that MyadvandedMetadata creates. The name of this file is derived by the superclass of the metadata provider by invoking the getItdUniquenessFilenameSuffix method. As the Flight.java file was annotated with the @RooMyadvanced annotation, the name of the file is Flight_Roo_Myadvanced.aj.

Now, let's look at the MyadvancedMetadata class that is responsible for creating the AspectJ ITD file and adding methods and fields to it.

The following code shows the MyadvancedMetadata class:

public class MyadvancedMetadata 
  extends AbstractItdTypeDetailsProvidingMetadataItem {

 private static final String PROVIDES_TYPE_STRING = 
   MyadvancedMetadata.class.getName();

 private static final String PROVIDES_TYPE = 
   MetadataIdentificationUtils.create(PROVIDES_TYPE_STRING);

 public MyadvancedMetadata(String identifier, 
  JavaType aspectName, 
  PhysicalTypeMetadata governorPhysicalTypeMetadata) {  
  
  super(identifier, aspectName, governorPhysicalTypeMetadata);
  builder.addField(getSampleField());
  builder.addMethod(getSampleMethod());
  itdTypeDetails = builder.build();
 }
 
 private FieldMetadata getSampleField() {
   ...
 }
 
 private MethodMetadata getSampleMethod() {
  ...
 }

 ...  
 public static final String getMetadataIdentiferType() {
  return PROVIDES_TYPE;
 }
 ...
}

The preceding code shows that MyadvancedMetadata defines two constants and a couple of methods. The PROVIDES_TYPE_STRING constant refers to the fully-qualified name of the MyadvancedMetadata class and PROVIDES_TYPE refers to the metadata type provided by the MyadvancedMetadata class. The value of PROVIDES_TYPE is MID:com.roo.addon.myadvanced.MyadvancedMetadata.

MyadvancedMetadata extends the AbstractItdTypeDetailsProvidingMetadataItem abstract class, which provides the common functionality for add-ons that want to create an AspectJ ITD file corresponding to a Java type. The constructor of MyadvancedMetatada makes use of information passed by the MyadvancedMetadataProvider to create the AspectJ ITD file. To simplify creation of AspectJ ITD file, Roo's ItdTypeDetailsBuilder instance (represented by the builder variable in the constructor) is used. The addField and addMethod methods of ItdTypeDetailsBuilder are used to add information about fields and methods that form part of the AspectJ ITD. The getSampleField and getSampleMethod methods in the above code return FieldMetadata and MethodMetadata, which represent the field and method to be added to the AspectJ ITD.

The following code shows the getSampleField method of MyadvancedMetadata class:

private FieldMetadata getSampleField() {
  int modifier = 0;
  
  FieldMetadataBuilder fieldBuilder = 
   new FieldMetadataBuilder(getId(), 
    modifier, 
    new ArrayList<AnnotationMetadataBuilder>(), 
    new JavaSymbolName("sampleField"), 
    JavaType.STRING_OBJECT); 
  
  return fieldBuilder.build(); 
 }

Roo's FieldMetadataBuilder is used to create a FieldMetadata instance. The following are the details of the arguments passed to the FieldMetadataBuilder constructor:

  • getId(): identifies the Java type into which the field will be introduced by the AspectJ ITD
  • modifier: represents the access modifier for the field
  • new ArrayList<AnnotationMetadataBuilder>(): contains information about the annotations that must be added to the field
  • new JavaSymbolName("sampleField"): name of the field
  • JavaType.STRING_OBJECT: Java type of the field

Similarly, the getSampleMethod method of MyadvancedMetadata makes use of Roo's MethodMetadataBuilder to create an instance of MethodMetadata.

Now that you know how the myadvanced add-on works, you can use it in your Roo project the same way we used the mysimple add-on.

There's more...

Roo provides a metadata for type command to view metadata for a Java type. You can also use the metadata trace command to see how metadata event notifications happen.

See also

  • Refer to the Developing a simple add-on recipe to see how to develop a simple add-on
..................Content has been hidden....................

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