Introducing Spring Roo, Part 2
Developing an application with Spring Roo
Creating a full-fledged enterprise application using Spring Roo
Content series:
This content is part # of # in the series: Introducing Spring Roo, Part 2
This content is part of the series:Introducing Spring Roo, Part 2
Stay tuned for additional content in this series.
In Part 1 of this series on Spring Roo, we built a small enterprise conference application using Spring Roo. Here, we will extend that simple CRUD web application into a full-fledged enterprise application using Spring Roo. Before we get started, make sure you have installed Spring Roo and have downloaded SpringSource Tool Suite (see Part 1 for more information).
Let's get started
To extend our conference application, we need to recreate the application
from Part 1. You can either follow the previous instructions or use Roo's script
command. script
executes all
commands specified in a resource file. If you followed Part 1, you noticed that Roo created a file named log.roo, which contains all the
commands fired on the Roo shell. We will execute that log.roo file and
recreate the application.
- This file is included in the sample code. You can rename it conference.roo.
- Create a new directory called conference and copy conference.roo in it.
- Open your operating system command-line shell.
- Go to the conference directory you just created.
- Fire the
script --file conference.roo
command.
script
will recreate the application in a few
seconds if you have the required JARs in your Maven repository.
Otherwise, it will take more time since it has to download all the JARs. The
script
command is useful in that you can
utilize it as a template to create Spring-managed projects.
Before we move forward, import the Maven project in STS. STS comes pre-bundled with the Maven Eclipse plug-in. You should import the project by selecting File > Import > Maven > Existing Maven Projects, then selecting the project's directory. We are importing the project in STS since we will be writing some custom code later.
The web application we have created so far works, and we can test it manually by creating, reading, updating, and deleting the Speaker and Talk entities. But wouldn't it be great if we could automate this process?
Automated web testing
Here comes the next feature of Spring Roo: Selenium testing support. Selenium is a robust set of tools that support rapid development of test automation for web-based applications. To add Selenium testing support to your application, execute the commands shown below:
selenium test --controller ~.web.SpeakerController selenium test --controller ~.web.TalkController
The selenium test
command will create Selenium
test for the Speaker and Talk controllers. This command has one required
attribute called controller to specify the name of the controller to
create the Selenium test. This command also has two optional attributes
called name
and serverUrl
to specify the name of the Selenium test and the
server where the web application is available. Spring Roo will also add
the Selenium Maven plug-in when you fire the
selenium test
command.
Above, we created Selenium test cases for our controller, but before we run them, we need to fix a small defect in the Selenium test suite created by Spring Roo. We added a constraint in our Speaker entity that age should be between 25 and 60, but the test suite does not take that constraint into consideration. It used a value of age as 1, so the test will fail. We need to modify a file named test-speaker.xhtml and update the section as shown in Listing 1.
Listing 1. Modification of test-speaker.xhtml
<tr> <td>type</td> <td>_age_id</td> <td>1</td> </tr>
to
<tr> <td>type</td> <td>_age_id</td> <td>26</td> </tr>
This defect will be fixed in a future release of Spring Roo.
To run the Selenium test cases, we need to start the Tomcat server.
You can start the it using the Maven command mvn
tomcat:run
. By default, all web applications created using Roo
have Maven plug-ins for Tomcat and Jetty web servers. To run selenium test
, execute Maven command mvn selenium:selenese
.
This will launch a Firefox browser to run the Selenium test cases. During execution of the tests, you should see an image similar to Figure 1.
Figure 1. Selenium tests

At present, anyone can access our application and perform create, update, and delete on Speaker and Talk. In a real-time application, there is security on who can do which operation.
Secure the web application
Roo uses Spring Security to add security to your application in one line. Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Adding Spring Security
To add Spring Security, type the following command:
security setup
.
This command will add all the required Spring Security JARs and will set up the basic security for your application. This command also creates other files, but the one that is important is applicationContext-security.xml, which contains all the bean definitions related to security. The applicationContext-security.xml context looks like the content in Listing 2. I have replaced the password hashed with dots to make it more readable.
Listing 2. Context of applicationContext-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans \ http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security \ http://www.springframework.org/schema/security/spring-security-3.0.xsd"> <!-- HTTP security configurations --> <http auto-config="true" use-expressions="true"> <form-login login-processing-url="/resources/j_spring_security_check" \ login-page="/login" authentication-failure-url="/login?login_error=t"/> <logout logout-url="/resources/j_spring_security_logout"/> <!-- Configure these elements to secure URIs in your application --> <intercept-url pattern="/choices/**" access="hasRole('ROLE_ADMIN')"/> <intercept-url pattern="/member/**" access="isAuthenticated()" /> <intercept-url pattern="/resources/**" access="permitAll" /> <intercept-url pattern="/**" access="permitAll" /> </http> <!-- Configure Authentication mechanism --> <authentication-manager alias="authenticationManager"> <!-- SHA-256 values can be produced using \ 'echo -n your_desired_password | sha256sum' \ (using normal *nix environments) --> <authentication-provider> <password-encoder hash="sha-256"/> <user-service> <user name="admin" password="..." authorities="ROLE_ADMIN"/> <user name="user" password="..." authorities="ROLE_USER"/> </user-service> </authentication-provider> </authentication-manager> </beans:beans>
The security configured by Roo is generic and has no reference to our application. Keep in mind that Roo helps in setting up or configuring an application for a quick start, but it is the developer's responsibility to customize the end product. In this case, Roo has merely provided a template for Spring Security, and it is our responsibility to customize it to our needs.
Customizing Spring Security
In our application, anybody can create a Speaker, but only a Speaker can create a Talk. We need to modify applicationContext-security.xml as shown below. Listing 3 only shows the part of XML, which needs modification.
Listing 3. Modifying applicationContext-security.xml
<http auto-config="true" use-expressions="true"> <form-login login-processing-url="/resources/j_spring_security_check" \ login-page="/login" authentication-failure-url="/login?login_error=t"/> <logout logout-url="/resources/j_spring_security_logout"/> <!-- Configure these elements to secure URIs in your application --> <intercept-url pattern="/talks/**" access="hasRole('ROLE_USER')"/> <intercept-url pattern="/speakers/**" access="permitAll" /> <intercept-url pattern="/resources/**" access="permitAll" /> <intercept-url pattern="/**" access="permitAll" /> </http>
I have updated the intercept-url
so that only
users who have the role user can create a Talk, and all users are
permitted to register themselves as a Speaker.
The Roo-generated Spring Security shown above uses an in-memory authentication provider configured under
the <user-service>
tag. Since our
application manages Speaker entities, we should build a custom
authentication provider that uses Speaker data. For authentication, we
will use Speaker email as the username and add a password field to the
Speaker entity, which we will use as an authentication password.
Once again, I use the Roo shell to add the password field to the Speaker entity:
field string --class ~.domain.Speaker --fieldName password --notNull --sizeMin 6 –sizeMax 10
I have also added the constraint that the password should be not null and the password should be between 6 and 10 characters long.
Since we are using email and password as authentication parameters, we
would like to find a Speaker for a given email and password. Spring Roo
provides a facility to create finders for your application using the
finder add
command:
finder add --finderName findSpeakersByEmailAndPasswordEquals --class ~.domain.Speaker
You can find all the finders for an entity using the
finder list
command.
finder add
writes the finder code in the
Speaker_Roo_Finder.aj file and writes some view-related files. This
allows you to search for speakers from the GUI.
Writing custom AuthenticationProvider
We will write a custom Authentication provider by extending a class called
AbstractUserDetailsAuthenticationProvider
,
which works with username/password like authentication. The classes that
extend AbstractUserDetailsAuthenticationProvider
have
to provide implementation for its two abstract methods:
additionalAuthenticationChecks
and
retrieveUser
. The provider calls
the retrieveUser
method to authenticate the Speaker
using the entered email and password. The database lookup for the Speaker
is done using the finder we created above. If the Speaker is found,
then GrantedAuthority ROLE_USER
is assigned to
the Speaker. The method finally returns a populated
UserDetails
object if the login succeeded or
throws a BadCredentialsException
with the
appropriate message if not (see Listing 4).
Listing 4. Custom authentication
package com.dw.roo.conference.security; import java.util.ArrayList; import java.util.List; import javax.persistence.EntityNotFoundException; import javax.persistence.NonUniqueResultException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.\ dao.AbstractUserDetailsAuthenticationProvider; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.util.StringUtils; import com.dw.roo.conference.domain.Speaker; public class ConferenceAuthenticationProvider extends \ AbstractUserDetailsAuthenticationProvider { @Override protected void additionalAuthenticationChecks(UserDetails userDetails, \ UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { // TODO Auto-generated method stub } @Override protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken \ authentication) throws AuthenticationException { String password = (String) authentication.getCredentials(); if (!StringUtils.hasText(password)) { throw new BadCredentialsException("Please enter password"); } List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); try { Speaker speaker = Speaker.findSpeakersByEmailAndPasswordEquals(username, \ password).getSingleResult(); authorities.add(new GrantedAuthorityImpl("ROLE_USER")); } catch (EmptyResultDataAccessException e) { throw new BadCredentialsException("Invalid username or password"); } catch (EntityNotFoundException e) { throw new BadCredentialsException("Invalid user"); } catch (NonUniqueResultException e) { throw new BadCredentialsException("Non-unique user, contact administrator"); } return new User(username, password, true, // enabled true, // account not expired true, // credentials not expired true, // account not locked authorities); } }
In the applicationContext-security.xml, we have to define the
conferenceAuthenticationProvider
bean and
replace the in-memory authentication provider generated by Roo with our
conferenceAuthenticationProvider
as in Listing
5.
Listing 5. conferenceAuthenticationProvider
<beans:bean name="conferenceAuthenticationProvider" class="com.dw.roo.conference.security.ConferenceAuthenticationProvider"> </beans:bean> <!-- Configure Authentication mechanism --> <authentication-manager alias="authenticationManager"> <authentication-provider ref="conferenceAuthenticationProvider"/> </authentication-manager>
Now if you start up the server and try to create a Talk, you will be presented with a login screen where you have to enter the email and password of the Speaker you created. The intent of building the custom authentication provider was not to build an ideal authentication provider but to show you what Roo does and what you have to do yourself.
Email notification
In our application, Speakers should receive an email when they create a Talk, so let's add email support to our application. We will use Gmail as our SMTP server to focus on sending email using Roo. Adding email support to an application is done using the following command:
email sender setup --hostServer smtp.gmail.com --username \ <Your email address> --password <Your email password> --port 587 --protocol SMTP
The email sender command installs a Spring
JavaMailSender
in your project. You can change
email-related properties in the email.properties file.
After a Talk is created, we need to send an email. For that, we need to add
an email field in TalkController
. To add an
email field, type:
field email template --class ~.web.TalkController
This will add a MailSender
template and a
sendMessage
method to
TalkController
. Now we need to trigger the
sendMessage
method after a Talk is persisted in
the database. As all the code for the
TalkController
exists in
TalkController_Roo_Controller.aj file, the easiest way to accomplish this
task is to create an encodeUrlPathSegment
method from the .aj file to the TalkController
class and add a call to the sendMessage
method
after the talk.persist()
line, as shown in Listing 6.
Listing 6. Creating an
encodeUrlPathSegment
method from the .aj file
to TalkController
public class TalkController { @Autowired private transient MailSender mailTemplate; public void sendMessage(String mailFrom, String subject, String mailTo, String message) { org.springframework.mail.SimpleMailMessage \ simpleMailMessage = new org.springframework.mail.SimpleMailMessage(); simpleMailMessage.setFrom(mailFrom); simpleMailMessage.setSubject(subject); simpleMailMessage.setTo(mailTo); simpleMailMessage.setText(message); mailTemplate.send(simpleMailMessage); } @RequestMapping(method = RequestMethod.POST) public String create(@Valid Talk talk, BindingResult result, Model model, HttpServletRequest request) { if (result.hasErrors()) { model.addAttribute("talk", talk); return "talks/create"; } talk.persist(); sendMessage("spring.roo.playground@gmail.com", "Your talk is created", talk.getSpeaker().getEmail(), \ "Congrats your talk is created"); return "redirect:/talks/" + encodeUrlPathSegment(talk.getId().toString(), request); } private String encodeUrlPathSegment(String pathSegment, HttpServletRequest request) { String enc = request.getCharacterEncoding(); if (enc == null) { enc = WebUtils.DEFAULT_CHARACTER_ENCODING; } try { pathSegment = UriUtils.encodePathSegment(pathSegment, enc); } catch (UnsupportedEncodingException uee) { } return pathSegment; } }
Hereafter, Speakers will receive an email in their specified accounts after a Talk is created.
Internationalization support
As we are building an Internet-based web application, it is important to
support different languages so users from different geographies can
utilize our application. Spring Roo adds internationalization support by using the
web mvc install language
command, which installs
a new language in your application. For example, the commands for Spanish
and Italian are:
web mvc install language --code es web mvc install language --code it
Roo currently support six languages, and you can write an add-on language for others of your choice. Now when the application is run, two flags are displayed (Italy's and Spain's), along with the British flag. If you click on any of these flags, you will view the web application in the language corresponding to that flag.
Socialize your web application
This is the age of social media, and social features are commonly added to current applications. It would make sense to add video of Talks. Roo provides support for embedding videos uploaded to YouTube, Vimeo, Viddler, and Google Video, etc. To embed a video, use the following command:
web mvc embed video --provider VIMEO --videoId 16069687
If you start the server and launch your application in a browser, you will be able to watch the video embedded above. Similarly, you could have added YouTube or Viddler videos.
Roo also provides you an option to embed Twitter messages, documents, stock tickers, maps, photos, and video streams in your application. The various commands are in Listing 7.
Listing 7. Embed commands
web mvc embed document web mvc embed finances web mvc embed map web mvc embed photos web mvc embed stream video web mvc embed twitter web mvc embed video
Database reverse engineering
Database reverse engineering (DBRE) allows you to introspect an existing database and expose it as an application. To show how DBRE works, I am going to create a feedback application from an existing feedback schema. I will use MySQL as a database.
Before we launch Roo, we have to create a schema in our MySQL installation. Run the SQL script in Listing 8 to create a feedback schema in your MySQL database.
Listing 8. SQL script to create feedback schema
create database feedback_schema; use feedback_schema; CREATE TABLE feedback ( id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, TalkTitle VARCHAR(45) NOT NULL, SpeakerName VARCHAR(45) NOT NULL, Feedback VARCHAR(4000) NOT NULL, PRIMARY KEY (id) ) ENGINE = InnoDB;
This is a simple schema with one table and without any relationships, but Roo can successfully reverse-engineer a complex schema with multiple tables and relationships.
After you have run the above SQL script and generated your schema, create a feedback application:
- Create a directory called feedback.
- From your operating system command line, go to the feedback directory.
- Open the Roo shell by typing the
roo
command. - Type
project --topLevelPackage com.dw.roo.feedback
to create a new Maven project. - In this application, we will be using MySQL as our database. To set up
the persistence for your application, use:
persistence setup --provider HIBERNATE --database MYSQL --databaseName feedback_schema --userName root --password password
As I created the schema with root user, I am using root as my username. Please enter the username and password with which you created your schema. This command will also add all required JARs for persistence.
- You can introspect the database schema using
database introspect --schema feedback_schema
. Thedatabase introspect
command shows the metadata related to the database schema. This command will show you the metadata for your schema on Roo shell console. You can also export the metadata xml to a file using--file attribute
. - After you have done introspection for your database schema, you can
reverse-engineer that schema using
database reverse engineer --schema feedback_schema --package ~.domain
.The
database reverse engineer
command has two required attributes, schema and package, for specifying the name of the schema you want to reverse-engineer and the package in which Roo will generate the sources. This will create all the entities in com.dw.roo.feedback.domain package. - The next step is to generate the controllers for your application.
You can do that by firing
controller all --package ~.web
. - Before we run our application, we need to make a small change in a
property in persistence.xml. The property
hibernate.ejb.naming_strategy is using
ImprovedNamingStrategy
, which does not work for MySQL databases, and you will get an exception if you domvn clean install tomcat:run
. To make it work, we have to change the hiberate.ejb.naming_strategy toDefaultNamingStrategy
, as shown below:<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.DefaultNamingStrategy"/>
- Now you can run the feedback application using the Maven command
mvn clean install tomcat:run
.
You can download the source code for conference and feedback (see Download).
Conclusion
As this point, we have extended our simple CRUD web application to a full-fledged enterprise application. We demonstrated how simple it is to add features like Selenium testing, Spring Security, internationalization support, and others. I also showed how to create an application from an existing database using the Spring Roo database reverse-engineering feature. There are still lots of features like JMS, Solr, and JSON support, etc. that Roo can easily add to your application.
In Part 3 of this series, I talk about how we can port the conference application to Google App Engine.
Downloadable resources
- PDF of this content
- Part 2 source code (os-springroo2-sample_code.zip | 11KB)
Related topics
- Be sure to read the rest of this Spring Roo series:
- Part 1: Building from source
- Part 3: Developing Spring Roo add-ons
- Part 4: Rapid application development in cloud with Spring Roo and Cloud Foundry
- Part 5: Write advanced and wrapper Spring Roo add-ons
- 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
- Learn more about Spring Roo.
- Follow developerWorks on Twitter.
- Download IBM product evaluation versions and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.