I had to pick up JPA recently and since it's been a while since I did anything in this space, thought I would take the opportunity to find out more about Spring Roo. I first heard about it at QCon London 2010, and at the time recalled thinking how easy it was to put together an application from simple components.
One of the things that also encouraged me was the generated application was also a valid OSGi bundle; great. So when Roo 1.1 was released recently, I gave it a spin. (Sadly, and somewhat ironically, whilst Roo 1.1 is now running in an OSGi container, the generated products are no longer OSGi bundles. Roo-1052 has more information about this slide backwards.)
Before going into how to use Spring Roo, it's worth mentioning what it does and why. The goal of Roo is to make it easy to put together web-based applications that have persistent entities; in other words, your fairly typical web/servlet/jsp/hibernate kind of setup. However, the way it achieves this is somewhat different to other tools. Instead of modifying source code that you write, it creates additional files which it generates. It then uses – effectively – a #include
at compile time to bring those fragments into your class. This is achieved with the AspectJ compiler, and in a unique and fairly useful way, using AspectJ as more than just wrapping-a-method-with-log-statements.
The net result is you write a minimal set of code in your class, and Roo automatically generates the necessary AspectJ fragments to support what you've written. (Those who have seen Project Lombok may already have seen this kind of approach in an IDE.)
Using Spring Roo is fairly easy. There's a roo.sh
executable (roo.exe
on Windows) which brings up a roo>
prompt, from which you can hit a number of commands. Unlike a standard OSGi shell, there's TAB completion; also a bonus is the fact that commands which aren't enabled are hidden from the list. Finally, there's a hint system that you can call (with hint
) to bring up a list of what-you-can-do.
The steps are as follows:
- Create a project
- Setup the persistence mechanism (database, mapping provider)
- Create one (or more) entities
- (Optionally) Create a web container
Project
Creating a project is easy enough – you need to run project --topLevelPackage com.example
. It generates a number of source directories, a spring folder and a log4j properties file, along with a Maven project to build them all. Think of it as a one-liner for Maven archetypes and you'll be close to what's required.
roo> project --topLevelPackage com.example Created /tmp/example/pom.xml Created SRC_MAIN_JAVA Created SRC_MAIN_RESOURCES Created SRC_TEST_JAVA Created SRC_TEST_RESOURCES Created SRC_MAIN_WEBAPP Created SRC_MAIN_RESOURCES/META-INF/spring Created SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml Created SRC_MAIN_RESOURCES/log4j.properties
Persistence
Setting up persistence is also as easy. persistence setup --provider p --database db
will do all the legwork for you in terms of setting up a persistence.xml
file, the driver classes to use and the per-provider magic settings that you need to know. Providers such as EclipseLink, OpenJPA and Hibernate are available, as are database drivers like DB2, Derby, Oracle and so on. (The setup notes that if you use a commercial driver you have to install it manually into your Maven repository in order for it to be found.) Use the TAB
key to get a list, or complete what you're typing, in doing this.
com.example roo> persistence setup --provider ECLIPSELINK --database DERBY Managed SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml Created SRC_MAIN_RESOURCES/META-INF/persistence.xml Created SRC_MAIN_RESOURCES/META-INF/spring/database.properties Managed ROOT/pom.xml [Added dependency org.apache.derby:derby:10.6.1.0] Managed ROOT/pom.xml [Added dependency org.eclipse.persistence:eclipselink:2.1.0] Managed ROOT/pom.xml [Added dependency org.eclipse.persistence:javax.persistence:2.0.1] Managed ROOT/pom.xml [Added dependency org.hibernate:hibernate-validator:4.1.0.Final] Managed ROOT/pom.xml [Added dependency javax.validation:validation-api:1.0.0.GA] Managed ROOT/pom.xml [Added dependency cglib:cglib-nodep:2.2] Managed ROOT/pom.xml [Added dependency javax.transaction:jta:1.1] Managed ROOT/pom.xml [Added dependency org.springframework:spring-jdbc:${spring.version}] Managed ROOT/pom.xml [Added dependency org.springframework:spring-orm:${spring.version}] Managed ROOT/pom.xml [Added dependency commons-pool:commons-pool:1.5.4] Managed ROOT/pom.xml [Added dependency commons-dbcp:commons-dbcp:1.3] Managed ROOT/pom.xml
Entities
Now we're able to create entities. The entity --class c
kicks off the entity generation process; you can type in a fully qualified name or use ~
to represent the project package.
com.example roo> entity --class ~.Employee Created SRC_MAIN_JAVA/com/example Created SRC_MAIN_JAVA/com/example/Employee.java Created SRC_MAIN_JAVA/com/example/Employee_Roo_Configurable.aj Created SRC_MAIN_JAVA/com/example/Employee_Roo_Entity.aj Created SRC_MAIN_JAVA/com/example/Employee_Roo_ToString.aj
At this point, we have a project that's capable of being used as a pure JPA provider. Before going further, it's worth looking at what's been generated:
pom.xml
- the project details, source directories and so onpersistence.xml
- which defines the database, configuration mappings and so on*.properties
- for supplying mutable data, like userid, password and logging levels*.aj
- the aspect fragmentsEmployee.java
- a single Java file
The Employee is fairly simple; at the moment, we've not added anything to it. In fact, other than a few annotations (@RooJavaBean
, @RooToString
, @RooEntity
) it has no contents. We can add a field
using the console to give an employee a name and a manager.
~.Employee roo> field string --fieldName name Managed SRC_MAIN_JAVA/com/example/Employee.java Created SRC_MAIN_JAVA/com/example/Employee_Roo_JavaBean.aj Managed SRC_MAIN_JAVA/com/example/Employee_Roo_ToString.aj ~.Employee roo> field reference --fieldName manager --type ~.Employee Managed SRC_MAIN_JAVA/com/example/Employee.java Managed SRC_MAIN_JAVA/com/example/Employee_Roo_JavaBean.aj Managed SRC_MAIN_JAVA/com/example/Employee_Roo_ToString.aj
All this has done is added a couple of fields to the class, one with a single JPA annotation:
@RooJavaBean @RooToString @RooEntity public class Employee { private String name; @ManyToOne private Employee manager; }
The interesting thing is what you don't see, which are being generated in the *.aj
files. For example, take a look at the Employee_Roo_ToString.aj
file:
privileged aspect Employee_Roo_ToString { public String Employee.toString() { StringBuilder sb = new StringBuilder(); sb.append("Id: ").append(getId()).append(", "); sb.append("Version: ").append(getVersion()).append(", "); sb.append("Name: ").append(getName()).append(", "); sb.append("Manager: ").append(getManager()); return sb.toString(); } }
This aspect, when compiled with AspectJ, effectively inserts a toString()
into your class automatically. It uses the name
and manager
that we've just added; and in fact, if you were to go into the Employee.java
file and edit it with your favourite text editor, when you save it, Roo will notice and update the toString()
automatically.
The id
and version
are defined in the Employee_Roo_Entity.aj
file, so all Roo entities have these. IDs are represented with a Long by default (though you can change that when the entity is created) as well as an Integer version (to support verification of recency when two updates occur on the same row in the database).
Finally, we've also got Employee_Roo_JavaBean.aj
which generates the getName()
and setName()
methods based on the fields you add. Of course, most IDEs can do this for you but Roo takes it out of your IDE and into a standalone monitoring process which can update the aspects automatically.
If you have a need to do a specific implementation – say, to pre-validate a field – then you can simply write the setName()
in your Employee.java
file. Roo notices that it's already there and doesn't bother regenerating it.
There's a web controller as well, though I'm not going to go into it here; doing controller all
will generate a huge amount of code for you, and perform command --mavenCommand jetty:run
will bootstrap a Jetty environment to test it.
Since it's scriptable, you can easily replay this by copying and pasting the below into a Roo shell prompt:
project --topLevelPackage com.example persistence setup --provider ECLIPSELINK --database DERBY entity --class ~.Employee field string --fieldName name field reference --fieldName manager --type Employee controller all --package ~.web perform command --mavenCommand jetty:run
Once you've done that, point your web browser at http://localhost:8080/example/ and see the app in action.
What's not to like?
Simple project setup, integration with a variety of different providers and databases, single command entry – what could be better?
Well, although it's an excellent idea in principle, it does have its drawbacks. One is that although using aspects in this way is a great idea, there is still some concern with using aspects which may put some people off. The second is that sometimes it's not clear what is happening behind the covers which takes a bit of getting used to.
It's a real shame that Roo has stopped generating OSGi bundles by default. Partially the enterprise spec runtimes aren't there yet, so generating a Meta-Persistence
header may not be immediately usable; but more was the complaints against Roo that it was using the SpringSource EBR instead of Maven central. Although EBR has been great for popularising OSGi, many upstream projects are migrating over to use OSGi metadata so it's not clear immediately that it's as much of an issue as it was when Roo originally came out.
The license is also a potential sticking point as well. Although the Roo annotations are licensed under an Apache License, Roo itself (which reads and generates the *.aj
files) are GPL. Some are concerned that this might introduce viral dependencies on the GPL – though whether from some kind of indirect linkage with the annotations or whether it's a bigger concern about the code that is automatically generated by Roo differs depending on who you ask. Either way, it's something to consider when basing an application off Roo.
Conclusion
Spring Roo is a great environment for creating persistent entities and web applications to drive them. Obviously not all applications fit into this shape so it has a limited audience. Being a Spring project, it uses Spring heavily in the generation of the runtime entities; much more so than you need if you're generating an OSGi JPA bundle. It's also a great application of aspect-oriented-programming that is more than the log-it-and-see example that you come across.
However, concerns about the license and the fact that it no longer generates OSGi bundles mean that it's probably better suited to prototyping or learning about how to generate JPA entities. The approach is promising though and with a different license or different style of generation it could be a real winner.