Part 3 of this "Introducing Spring Roo" series
discusses Spring Roo add-on architecture and how to create
internationalization and simple add-ons using the
addon create command. This article focuses on
the remaining two types of add-ons supported by Spring Roo, that is, advanced
add-on and wrapper add-on. It is recommended that you first read Part 3 before continuing.
Advanced add-on allows Spring Roo to do everything a simple add-on can do, like updating a Maven POM file with dependencies or plugins, update or add configuration files plus enhance existing Java types, and introduce new Java types using AspectJ ITDs. The ability to add source code makes advanced add-ons very powerful compared to all other add-ons. Before you create a Spring Roo advanced add-on, look at an existing advanced add-on provided by Spring Roo.
One advanced add-on is JPA, which performs persistence-related work, that is, adding support for databases and creating new entities. To see this in action, open the Roo shell and execute the commands in Listing 1. In this article, I use Spring Roo version 1.2.0.M1.
Listing 1. JPA example
project --topLevelPackage com.dw.demo --projectName entity-demo jpa setup --database FIREBIRD --provider HIBERNATE entity --class ~.domain.Book |
Both the jpa setup command and
entity command correspond to an advanced add-on
called org.springframework.roo.addon.jpa. The
output of the jpa setup command and entity
command on the Roo shell allows clear distinguishment between simple and
advanced add-ons. Listing 2 shows the output of the JPA setup command.
Listing 2. Output of JPA setup
Created SRC_MAIN_RESOURCES/META-INF/spring/database.properties Updated ROOT/pom.xml [added dependencies ...] Updated SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml Created SRC_MAIN_RESOURCES/META-INF/persistence.xml |
The output of jpa setup command shows it is
performing configuration functions, such as adding dependencies in
pom.xml, updating Spring applicationContext.xml, and creating
persistence-specific persistence.xml. It is assumable that the JPA setup
command corresponds to a simple add-on because it is not creating or
updating Java source code. Use a simple add-on for scenarios similar to the
setup shown above.
Listing 3 shows the output of the entity command.
Listing 3. Output of entity command
Created SRC_MAIN_JAVA/com/dw/demo/domain Created SRC_MAIN_JAVA/com/dw/demo/domain/Book.java Created SRC_MAIN_JAVA/com/dw/demo/domain/Book_Roo_Configurable.aj Created SRC_MAIN_JAVA/com/dw/demo/domain/Book_Roo_Jpa_Entity.aj Created SRC_MAIN_JAVA/com/dw/demo/domain/Book_Roo_Entity.aj Created SRC_MAIN_JAVA/com/dw/demo/domain/Book_Roo_ToString.aj |
The output shows creation of one Java file called Book.java, and four *.aj files. The golden rule to identify an advanced add-on is the generation of Java files, or *.aj files, or both, as in the case of entity command. These *Roo_*.aj are called Inter-type Declarations (or ITDs). ITDs allows one type (an aspect) to make declarations for another type, that is, you can modify the static structure of any type by adding methods, fields, or changing their type hierarchy. Roo uses an ITD as a code-generation artifact and manages them throughout their lifetime. ITDs allow Roo to generate code in a separate compilation unit, but they are combined into the same compiled class.
After viewing the output of entity command, consider how these artifacts (.java and .aj files) are generated by Spring Roo. See Listing 4 for a sample of a Book.java file.
Listing 4. A Book.java file
package com.dw.demo.domain;
import org.springframework.roo.addon.entity.RooEntity;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.tostring.RooToString;
@RooJavaBean
@RooToString
@RooEntity
public class Book {
}
|
The Java file looks normal, except for annotations on the class.
Looking at the names of the annotation and .aj files, it is apparent some
of these annotations correspond to functionality added by .aj files. For
example, RooToString corresponds to
Book_Roo_ToString.aj file and add toString()
method. RooEntity corresponds to
Book_Roo_Entity .aj, Book_Roo_Jpa_Entity, and
persistence-related methods. You can leave the rest for now. To
understand how annotation leads to generation of ITDs, explore
how Spring Roo provides advance add-on functionalities.
- On Roo shell startup, it will scan all the classes and register all
those classes which implement
CommandMarkerinterface.CommandMarkerinterface tells Roo that these classes will define commands which this add-on can perform. - All the advanced add-ons register their services to the OSGi runtime
provided by Spring Roo. These services specify conditions on which it
will trigger code generation. For all advanced add-ons, the triggering
point is the presence of an annotation. For example, one
advanced add-on for
toString()method generation is only triggered if a Java type has RooToString annotation. This is the case for other annotations. - Upon using
entity --class ~.domain.Book, the add-on will create a Java file called Book.java with annotations. Other add-ons trigger when a Java type has these annotations, and .aj files are written for them.
More clarification will be evident when you create your own advanced add-on.
The org.springframework.roo.addon.jpa add-on is
just one example of advanced add-ons provided by Spring Roo. Other
advanced add-ons include GWT, controller, JSON, and many more.
The Spring Roo 1.2.0 release contains two more advanced
add-ons—addon-equals and addon-jsf. The addon-equals provides implementation
for equals and hashcode method for an entity and addon-jsf provides
JSF support in Spring Roo applications. To play with the latest Spring Roo
snapshot, either build the Spring Roo code or download the nightly
snapshot from Spring Roo repository.
Include compareTo() method in My Entity Class
The value objects or entities are commonly required to implement the
java.lang.Comparable interface and provide
implementation for the compareTo() method.
Comparable interface imposes total ordering on the objects of each class
implementing it. When you implement Comparable, you can:
- Call
java.util.Collections.sortandjava.util.Collections.binarySearch - Call
java.util.Arrays.sortandjava.util.Arrays.binarySearch - Use objects as keys in a
java.util.TreeMap - Use objects as elements in a
java.util.TreeSet
In this article, you will build an advanced add-on which will provide an
implementation of compareTo() for entities
created in your application. Because you want to add Java code in your
application, you have to create an advanced add-on.
The Spring Roo documentation eloquently explains how to set up a project and Maven repository on Google code, so it is unnecessary to repeat it here. Note that I will use "spring-dw-roo-compareto-addon" as the project name.
If you are not using the latest version of Spring Roo, 1.2.0.RC1, download it from the project website. Unzip and install it as explained in Part 1.
Spring Roo deprecated or removed some of the classes used in earlier versions.
After you set up the projectp, you will see a directory named spring-dw-roo-compareto-addon with just a .svn folder. From the command line, go into spring-dw-roo-compareto-addon directory and start the Roo shell. Then type the following command:
addon create advanced --topLevelPackage org.xebia.roo.addon.compareto
--projectName spring-dw-roo-compareto-addon
That's it! You just created an advanced add-on.
Next, on the Roo shell, fire perform package command
to create an add-on jar.
Listing 5 shows the files generated by the
addon create advanced command.
Listing 5. Files generated by addon create advanced command
Created ROOT/pom.xml Created SRC_MAIN_JAVA Created SRC_MAIN_RESOURCES Created SRC_TEST_JAVA Created SRC_TEST_RESOURCES Created SPRING_CONFIG_ROOT Created ROOT/readme.txt Created ROOT/legal Created ROOT/legal/LICENSE.TXT Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/ComparetoCommands.java Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/ComparetoOperations.java Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/ComparetoOperationsImpl.java Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/ComparetoMetadata.java Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/ComparetoMetadataProvider.java Created SRC_MAIN_JAVA/org/xebia/roo/addon/compareto/RooCompareto.java Created ROOT/src/main/assembly Created ROOT/src/main/assembly/assembly.xml Created SRC_MAIN_RESOURCES/org/xebia/roo/addon/compareto Created SRC_MAIN_RESOURCES/org/xebia/roo/addon/compareto/configuration.xml |
Some of the files generated like pom.xml, readme.txt, and license.txt, do not need any introduction as they were discussed in Part 3 and are self-explanatory. The more interesting artifacts are:
- ComparetoCommands.java
- ComparetoOperations.java
- ComparetoOperationsImpl.java
- ComparetoMetadata.java
- ComparetoMetadataProvider.java
- RooCompareto.java
Now look at the generated artifacts one by one.
-
ComparetoCommands.java: This class implementsCommandMarkerinterface and exposes two kinds of method—one withCliAvailablityIndicatorannotation and other withCliCommandannotation. TheCliAvailablityIndicatorannotation tells Spring Roo when the command should be visible. For example the 'entity' command will not be made available before the user defines persistence settings in the Roo shell or directly in the project. The methods annotated with@CliCommandregister the command with the Roo shell. The@CliCommandannotation has two attributes: value, which defines the command name, and help, which defines the help message shown when the help command is typed. For a detailed explanation of the*Commandsclass, refer to Part 3. -
ComparetoOperationsImpl.java: TheComparetoCommandsclass delegates all of the work toComparetoOperationsImplclass. The four methods generated in this class are:-
isCommandAvailable(): This method is called by method annotated withCliAvailabilityIndicator ComparetoCommandsannotation inComparetoCommandsclass to check if the command should be visible or not. This is to ensure commands are aware of context. This method performs various validations. For example, if the project has been created, then only the command is visible, or if persistence has been setup, then only command should be visible. It is not mandatory to have a condition for command to be visible. Simply return true to make sure command is always visible. -
setup(): This method is called bysetup()method inComparetoCommandsclass which is annotated with@CliCommand. The code makes it clear this class is responsible for performing setup related tasks such as adding Maven dependencies, adding Maven repositories, or creating or updating Spring context files (as done with the Jamon Roo add-on in Part 3). -
annotateType(): This method and theannotateAll()are new methods which do not exist in simple add-ons. The functionality of this method is to add an annotation (RooCompareto) on the specified Java type. This method uses some Spring Roo-provided services to fetch the Class details for the given Java type and appendRooJComparetoannotation to it. -
annotateAll(): This method finds all the types which are annotated withRooJavaBeanannotation and callannotateType()method on all those types. Use this when all entities should haveRooComparetoannotation.
-
-
RooCompareto.java: The presence of this annotation triggers the add-on to generate the code. -
ComparetoMetadataProvider.java: This class is a Spring Roo service and is called by Roo to retrieve the metadata for the add-on. This class registers triggers for adding and removing metadata. No changes are required in this class, but remember that this class has one method called getMetadata() which will be called by Spring Roo whenever there is any Java type withRooComparetoannotation. -
ComparetoMetadata.java: This class is responsible for generating the ITD corresponding to the add-on. In the generated code, it uses a class calledItdTypeDetailsBuilderto create an ITD with a field and method. Later in this article, you will change the default generated code to meet the requirements of adding acompareTomethod and implementing Comparable interface.
Modifying add-on to meet the requirements
You want to create an add-on which should add the
compareTo method to entity classes. You will:
- Add Maven dependency of
commons-langversion 3.1 in the target project. This is required becausecommons-langprovides a builder class calledCompareToBuilderwhich will be used to build thecompareTomethod. - Make the entity class implement the
Comparableinterface. - Create an ITD for the
compareTomethod.
To meet these requirements, changes are required in the
ComparetoOperationsImpl and
ComparetoMetadata classes. Make these
changes one by one.
- First, add the Maven
commons-langdependency in the target project. Update the configuration.xml file to have thecommons-langdependency instead of the default provided Spring batch dependency, as in Listing 6.
Listing 6. Update configuration.xml file<?xml version="1.0" encoding="UTF-8" standalone="no"?> <configuration> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.1</version> </dependency> </dependencies> </configuration>
- Next, modify the implementation of the
setup()method inComparetoOperationsImplclass to read thecommons-langMaven dependency instead of Spring batch Maven dependency, as in Listing 7. The methodannotateTypeandannotateAll()are not shown here because they don't have any changes.
Listing 7. Modify implementation of setup() method@Component @Service public class ComparetoOperationsImpl implements ComparetoOperations { @Reference private ProjectOperations projectOperations; @Reference private TypeLocationService typeLocationService; @Reference private TypeManagementService typeManagementService; /** {@inheritDoc} */ public void setup() { // Install the add-on Google code repository needed to get the annotation projectOperations.addRepository("", new Repository("Compareto Roo add-on repository", "Compareto Roo add-on repository", "https://spring-dw-roo-compareto-addon.googlecode.com/svn/repo")); List<Dependency> dependencies = new ArrayList<Dependency>(); // Install the dependency on the add-on jar ( dependencies.add(new Dependency("org.xebia.roo.addon.compareto", "org.xebia.roo.addon.compareto", "0.1.0.BUILD-SNAPSHOT", DependencyType.JAR, DependencyScope.PROVIDED)); Element configuration = XmlUtils.getConfiguration(getClass()); for (Element dependencyElement : XmlUtils.findElements( "/configuration/dependencies/dependency", configuration)) { dependencies.add(new Dependency(dependencyElement)); } projectOperations.addDependencies("", dependencies); } }
The changes made so far are similar to the changes used to create a Jamon simple add-on in Part 3.
Make the entity class implement the Comparable interface
After making changes in the code to add Maven dependency, you need to
make sure that your entity class implements java.lang.Comparable interface.
To do that, modify the ITD generated by the
ComparetoMetadata class. The metadata classes
generate ITD using the ItdTypeDetailsBuilder
class which provides various add methods for adding methods, fields,
annotations, interface, and more to an ITD. To make a Java type implements an interface, use the
addImplementsType method in the ItdTypeDetailsBuilder class,
as in Listing 8. I only show the
ComparetoMetadata constructor because
construction of the ITD happens in the constructor.
Listing 8. Implementing java.lang.Comparable interface
public ComparetoMetadata(String identifier, JavaType aspect Name,
PhysicalTypeMetadata governorPhysicalTypeMetadata) {
super(identifier, aspect Name, governorPhysicalTypeMetadata);
Assert.isTrue(isValid(identifier), "Metadata identification string '" +
identifier + "' does not appear to be a valid");
JavaType comparableInterface = new JavaType("java.lang.Comparable");
builder.addImplementsType(comparableInterface);
itdTypeDetails = builder.build();
}
|
Create an ITD for compareTo method
After making a Java type implements Comparable interface, you have to
provide implementation of compareTo method. CompareToBuilder class
provides a fluent interface for creating compareTo method. Spring Roo
equals add-on uses EqualsBuilder and HashcodeBuilder to provide
implementation for equals and hashcode method. Let's take an example
to make sure it is clear how CompareToBuilder
assists in creating the compareTo method.
Suppose you have a entity called Book and you want to provide compareTo
implementation for it using CompareToBuilder.
Listing 9 shows the Book class
and compareTo method.
Listing 9. Book class and compareTo method
import org.apache.commons.lang3.builder.CompareToBuilder;
public class Book implements Comparable {
private String title;
private String author;
private double price;
public Book(String title, String author, double price) {
this.title = title;
this.author = author;
this.price = price;
}
// getters and setters
public int compareTo(Book o) {
if(!(o instanceof Book)){
return -1;
}
Book book = (Book)o
return new CompareToBuilder().append(this.title, book.title).append(this.author,
book.author).append(this.price, book.price).toComparison();
}
@Override
public String toString() {
return "Book [title=" + title + ", author=" + author + ", price=" + price + "]";
}
}
|
The compareTo method in Listing 9 does the
following:
- If o is not
instanceOfBookthen return -1 - If o is
instanceOfBookthen type cast o to Book - Create an object of
CompareToBuilderclass and then call append method on fields
Build the compareTo method
incrementally with these steps:
- If o is not instanceOf Book then return -1
Before you add the
instanceOfcheck, create thecompareTomethod. See Listing 10.
Listing 10. Create compareTo methodpublic ComparetoMetadata(String identifier, JavaType aspect Name, PhysicalTypeMetadata governorPhysicalTypeMetadata) { super(identifier, aspect Name, governorPhysicalTypeMetadata); Assert.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid"); JavaType comparableInterface = new JavaType("java.lang.Comparable"); builder.addImplementsType(comparableInterface); builder.addMethod(getCompareToMethod()); itdTypeDetails = builder.build(); } private MethodMetadata getCompareToMethod() { final JavaType parameterType = JavaType.OBJECT; final List<JavaSymbolName> parameterNames = Arrays.asList(new JavaSymbolName("obj")); final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); bodyBuilder.appendFormalLine("return -1;"); final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, new JavaSymbolName("compareTo"), JavaType.INT_PRIMITIVE, AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, bodyBuilder); return methodBuilder.build(); }
The
getCompareToMethod()generates thecompareTomethod metadata using theMethodMetadataBuilderclass.MethodMetadataBuilderis a Spring Roo-providedBuilderclass for building method metadata. To build the method metadata, first construct aMethodMetadataBuilderobject, passing arguments such as access modifier, method name, return type, parameter list, or a method body builder to build metadata for thecompareTomethod as in Listing 11.
Listing 11. instanceOf checkprivate MethodMetadata getCompareToMethod() { final JavaType parameterType = JavaType.OBJECT; String parameterName = "obj"; final List<JavaSymbolName> parameterNames = Arrays.asList(new JavaSymbolName(parameterName)); final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final String typeName = destination.getSimpleTypeName(); bodyBuilder.appendFormalLine("if (!(" + parameterName + " instanceof " + typeName + ")) {"); bodyBuilder.indent(); bodyBuilder.appendFormalLine("return -1;"); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); bodyBuilder.appendFormalLine("return -1;"); final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, new JavaSymbolName("compareTo"), JavaType.INT_PRIMITIVE, AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, bodyBuilder); return methodBuilder.build(); }
- If o is
instanceOfBookthen type cast o to BookThe next step is a cast so you can build the
compareTomethod. To do this, append this line after the instanceOf check:bodyBuilder.appendFormalLine(typeName + " rhs = (" + typeName + ") " + OBJECT_NAME + ";");
- Create object of CompareToBuilder class and then call append method on
fields
To build
compareTomethod, you need access to all the fields of a class. TheComparetoMetadataclass does not contain any information about the type so it cannot get fields of the class. This information can be provided by theComparetoMetadataProvideras in Listing 12.
Listing 12. ComparetoMetadataProviderprotected ItdTypeDetailsProvidingMetadataItem getMetadata(String metadataId, JavaType aspect Name, PhysicalTypeMetadata governorPhysicalTypeMetadata, String itdFilename) { final String[] excludeFields = {}; final MemberDetails memberDetails = getMemberDetails(governorPhysicalTypeMetadata); if (memberDetails == null) { return null; } final JavaType javaType = governorPhysicalTypeMetadata.getMemberHoldingTypeDetails().getName(); final List<FieldMetadata> compareToFields = locateFields(javaType, excludeFields, memberDetails, metadataId); return new ComparetoMetadata(metadataId, aspect Name, governorPhysicalTypeMetadata, compareToFields); } private List<FieldMetadata> locateFields(final JavaType javaType, final String[] excludeFields, final MemberDetails memberDetails, final String metadataIdentificationString) { final SortedSet<FieldMetadata> locatedFields = new TreeSet<FieldMetadata>(new Comparator<FieldMetadata>() { public int compare(final FieldMetadata l, final FieldMetadata r) { return l.getFieldName().compareTo(r.getFieldName()); } }); final List<?> excludeFieldsList = CollectionUtils.arrayToList(excludeFields); final FieldMetadata versionField = persistenceMemberLocator.getVersionField(javaType); for (final FieldMetadata field : memberDetails.getFields()) { if (excludeFieldsList.contains(field.getFieldName().getSymbolName())) { continue; } if (Modifier.isStatic(field.getModifier()) || Modifier.isTransient(field.getModifier()) || field.getFieldType().isCommonCollectionType() || field.getFieldType().isArray()) { continue; } if (versionField != null && field.getFieldName().equals(versionField.getFieldName())) { continue; } locatedFields.add(field); metadataDependencyRegistry.registerDependency( field.getDeclaredByMetadataId(), metadataIdentificationString ); } return new ArrayList<FieldMetadata>(locatedFields); }
After you have the fields, pass them to the
ComparetoMetadataso that it can build thecompareTomethod, as in Listing 13.
Listing 13. Passing metadata to build compareTo methodprivate List<FieldMetadata> compareToFields; public ComparetoMetadata(String identifier, JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata, List<FieldMetadata> compareToFields) { super(identifier, aspectName, governorPhysicalTypeMetadata); Assert.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid"); this.compareToFields = compareToFields; if (!CollectionUtils.isEmpty(compareToFields)) { JavaType comparableInterface = new JavaType("java.lang.Comparable"); builder.addImplementsType(comparableInterface); builder.addMethod(getCompareToMethod()); } itdTypeDetails = builder.build(); } private MethodMetadata getCompareToMethod() { final JavaType parameterType = JavaType.OBJECT; String parameterName = "obj"; final List<JavaSymbolName> parameterNames = Arrays.asList(new JavaSymbolName(parameterName)); final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final ImportRegistrationResolver imports = builder.getImportRegistrationResolver(); imports.addImport( newJavaType("org.apache.commons.lang3.builder.CompareToBuilder") ); final String typeName = destination.getSimpleTypeName(); bodyBuilder.appendFormalLine("if (!(" + parameterName + " instanceof " + typeName + ")) {"); bodyBuilder.indent(); bodyBuilder.appendFormalLine("return -1;"); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); bodyBuilder.appendFormalLine(typeName + " rhs = (" + typeName + ") " + parameterName + ";"); final StringBuilder builder = new StringBuilder(); builder.append("return new CompareToBuilder()"); for (final FieldMetadata field : compareToFields) { builder.append(".append(" + field.getFieldName() + ", rhs." + field.getFieldName() + ")"); } builder.append(".toComparison();"); bodyBuilder.appendFormalLine(builder.toString()); final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, new JavaSymbolName("compareTo"), JavaType.INT_PRIMITIVE, AnnotatedJavaType.convertFromJavaTypes(parameterType), parameterNames, bodyBuilder); return methodBuilder.build(); }
This completes the implementation of the
compareTo add-on. You can download the full
source code of this add-on from the
Google code repository.
Now, you can test the compareTo that you just created.
- Exit the roo shell and run the
mvn clean installcommand. During the build process it will ask you for the GPG passphrase. - After the Roo add-on is built, open a new command line and create a directory named bookshop.
- Go into the bookshop directory and type the
roocommand to open a Roo shell. - Execute the commands from Listing 14 in the Roo shell.
Listing 14. Creating the add-onproject --topLevelPackage com.xebia.roo.bookshop --projectName bookshop jpa setup --database HYPERSONIC_IN_MEMORY --provider HIBERNATE entity jpa --class ~.domain.Book field string --fieldName title --notNull field string --fieldName author --notNull field number --fieldName price --type double --notNull
- To install and start the add-on, type the following on the Roo
shell:
osgi start --url file://<location to compareTo addon jar>
This should install and activate your compareTo add-on. You can view the status of the add-on using the OSGi ps command.
- Type
comparetoand press tab to see the three compareto addon commands in Listing 15.
Listing 15. View compareto addon commandsroo> compareto compareto add compareto all compareto setup
- The steps set out in Listing 15 will confirm that the compareto add-on
is properly installed. The next step is to run setup command which
will configure the required dependencies. See Listing 16.
Listing 16. Run setup commandroo> compareto setup Updated ROOT/pom.xml [added repository https://spring-dw-roo-compareto-addon.googlecode.com/svn/repo; added dependencies org.xebia.roo.addon.compareto:org.xebia.roo.addon.compareto: 0.1.0.BUILD, org.apache.commons:commons-lang3:3.1; removed dependency org.apache.commons:commons-lang3:3.0.1]
- Once you run the
compareto setupcommand the next logical step is to add thecompareTomethod to entity classes. This can be done either by compareto add or compareto all depending on whether you want to generate compareTo method for only one entity class or all the entity classes. Let's add thecompareTomethod to all the entity classes in the sample bookshop application (See Downloads.) See Listing 17.
Listing 17. Add compareTo method to all the entity classesroo> compareto all Updated SRC_MAIN_JAVA/com/xebia/roo/bookshop/domain/Book.java Created SRC_MAIN_JAVA/com/xebia/roo/bookshop/domain/Book_Roo_Compareto.aj
As you can see above in the output of compareto all command, an ITD named Book_Roo_Compareto.aj is generated. This file will contain the compareTo method. Listing 18 shows the Book_Roo_Compareto.aj.
Listing 18. Book_Roo_Compareto.ajimport org.apache.commons.lang.builder.CompareToBuilder; privileged aspect Book_Roo_Compareto { declare parents: Book implements java.lang.Comparable; public int Book.compareTo(java.lang.Object obj) { if (!(obj instanceof Book)) { return -1; } Book rhs = (Book) obj; return new CompareToBuilder().append(author, rhs.author).append(id, rhs.id).append(price, rhs.price).append(title, rhs.title).toComparison(); } } - Run
perform packagecommand on the Roo shell to check if everything compiled correctly after adding the add-on. To your surprise, the build will fail because Maven is not able to resolve Spring Roo bundle dependencies. These bundle dependencies come from thecompareToadd-on. You need the dependency on the add-on because your entities have to be annotated with Compareto annotation. This is the only thing you need from the add-on. The best way I found is to create another Maven module and have all add-on dependencies there. This is on similar lines to what Spring Roo does. Spring Roo does not dependency each and every add-on used. It has a common Spring Roo annotations jar which contains all the dependencies. I created a project xebia-spring-roo-addon-annotation and put the Compareto annotation in this module. Next, I updated the configuration.xml to add this jar to the client project instead of add-on jar. Listing 19 shows configuration.xml.
Listing 19. configuration.xml<?xml version="1.0" encoding="UTF-8" standalone="no"?> <configuration> <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.1</version> </dependency> <dependency> <groupId>org.xebia.roo.addon</groupId> <artifactId>xebia-spring-roo-addon-annotations</artifactId> <version>0.0.1</version> </dependency> </dependencies> <repositories> <repository> <id>spring-roo-addon-annoations</id> <name>Spring Roo Addon Annotation</name> <url>https://xebia-spring-roo-addon-annotations.googlecode.com/svn/repo</url> </repository> </repositories> </configuration>
Update the
ComparetoOperationsImplclasssetup()method to read the new dependencies and repositories specified in updated configuration.xml file. See Listing 20.
Listing 20. Update the ComparetoOperationsImpl class setup() methodpublic void setup() { bu List<Dependency> dependencies = new ArrayList<Dependency>(); Element configuration = XmlUtils.getConfiguration(getClass()); for (Element dependencyElement : XmlUtils.findElements("/configuration/dependencies/dependency", configuration)) { dependencies.add(new Dependency(dependencyElement)); } projectOperations.addDependencies("", dependencies); List<Element> repositories = XmlUtils.findElements( "/configuration/repositories/repository", configuration); for (Element repositoryElement : repositories) { Repository repository = new Repository(repositoryElement); projectOperations.addRepository(projectOperations.getFocusedModuleName(), repository); } }
Then follow these steps:
- Build the add-on again by running
mvn clean install. - Regenerate the client as you generated it in step 4.
- To remove the old add-on, type this command on Roo shell:
addon remove --bundleSymbolicName org.xebia.roo.addon.compareto
- Install the add-on again by running the
osgi installcommand. - Once the add-on is installed, run the
compareto setupandcompareto allcommands.You will see the compareTo ITDs. Run the perform package command and everything will work fine.
Once you test that your add-on is working in your development environment, you can push it to the Google code project you created. To publish the add-on to the outside world, follow the same procedure used to publish the i18n add-on in Part 3. Likewise, to register the add-on with RooBot, follow the Spring Roo documentation.
Make non-OSGi JDBC drivers OSGi compliant with wrapper add-on
A common use case of wrapper add-on is for the conversion of non-OSGi JDBC drivers into OSGi compliant bundles. One place where you need to wrap a JDBC driver is when you have to reverse engineer an Oracle database using Spring Roo. Spring Roo does not provide OSGi Oracle JDBC drivers because of copyright issues. Before reverse-engineering an Oracle database, first make the driver OSGi compliant. To create a wrapper addon for an Oracle JDBC driver:
- Install the Oracle JDBC jar in your local machine Maven repository by
typing this command.
mvn install:install-file -Dfile=ojdbc5.jar -DgroupId=com.oracle -DartifactId=ojdbc5 -Dversion=11.2.0.2 -Dpackaging=jar
- Create a new directory called oracle-wrapper-addon and from the command line go to the directory.
- Open the Roo shell and execute the wrapper add-on command:
addon create wrapper --topLevelPackage com.oracle.wrapper.jdbc --groupId com.oracle --artifactId ojdbc5 --version 11.2.0.2 --vendorName Oracle --licenseUrl oracle.comThis command will generate only pom.xml files that will be used to convert a non-OSGi Oracle JDBC driver to an OSGi driver
- From inside the Roo shell, run this command to create the OSGi bundle:
perform command --mavenCommand bundle:bundle
That's it. You have now successfully created an OSGi bundle of a non-OSGi jar.
In this article, you learned about advanced and wrapper add-on in Spring Roo. you also learned to create advanced and wrapper add-ons. This completes your exploration into a very important feature of Spring Roo: writing add-ons. Anytime you want to extend the functionality of Spring Roo, remember to think about creating add-ons.
In the next part of this "Introducing Spring Roo" series, I will talk about how you to write GWT applications using Spring Roo.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code | bookshop.zip | 14KB | HTTP |
| Sample code | spring-dw-roo-compareto-addon.zip | 18KB | HTTP |
Information about download methods
Learn
- Explore the Spring Roo Spring Roo documentation and find everything you need to use Roo effectively.
- Be sure to read the rest of this Spring Roo series:
- Part 1: Building from source
- Part 2: Developing an application with Spring Roo
- Part 3: Developing Spring Roo add-ons
- Part 4: Rapid application development in cloud with Spring Roo and Cloud Foundry
- Part 6: Develop Spring MVC and GWT apps using Spring Roo 1.2 and deploy them on Cloud Foundry
- Part 7: Develop Spring MongoDB Applications using Spring Roo
- Browse the code for Spring Roo in this web view of Spring Roo Source Code.
- Learn about the java.lang.Comparable interface.
- Read the Spring Roo documentation about how to set up a project and Maven repository on Google code.
- Follow the Spring Roo documentation to register the add-on with RooBot.
- In the Open Source area on developerWorks, find extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM products.
- On the developerWorks Java technology zone, find articles, tutorials, forums, and more about every aspect of Java programming.
- Stay current with developerWorks technical events and webcasts focused on a variety of IBM products and IT industry topics.
- Attend a free developerWorks Live! briefing to get up-to-speed quickly on IBM products and tools as well as IT industry trends.
- Listen to developerWorks podcasts for interesting interviews and discussions for software developers.
- Follow developerWorks on Twitter.
- Watch developerWorks demos that range from product installation and setup for beginners to advanced functionality for experienced developers.
Get products and technologies
- Visit the Spring Roo repository to download the nightly code snapshot.
- Download the latest version of Spring Roo, 1.2.0.RC1 from the project website.
- Download the full source code of the compareTo add-on from the Google code repository.
- Access IBM trial software (available for download or on DVD) and innovate in your next open source development project using software especially for developers.
Discuss
- Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis. Help build the Real world open source group in the developerWorks community.

Shekhar Gulati is a Java consultant working with Xebia India. He has six years of enterprise Java experience. He has extensive experience in Spring portfolio projects, such as Spring, Spring-WS, and Spring Roo. His interests are Spring, NoSQL databases, Hadoop, RAD frameworks like Spring Roo, cloud computing (mainly PaaS services like Google App Engine, CloudFoundry, OpenShift), Hadoop. He is an active writer and writes for JavaLobby, Developer.com, IBM developerWorks and his own blog at http://whyjava.wordpress.com/. You can follow him on twitter @ http://twitter.com/#!/shekhargulati.




