Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreUpdate: The third installment of the "Introducing Spring Roo" blog series is now available and covers Roo's internal architecture in detail.
Today I am pleased to announce that we have just released Spring Roo 1.0.0.M1. This release not only features numerous fixes, enhancements and a 31% performance improvement, but also an exciting range of new capabilities including email services, JMS, Spring Web Flow, simplified installation and automatic Selenium support. That's on top of those many capabilities already present in the alpha releases, as mentioned in my earlier blog entry.
In addition to working on the first milestone release, over the last month we've also established the public project infrastructure typical of open source projects. We now have available a community support forum, Jira issue tracking, public Subversion repository, FishEye source monitoring and so on. Some of the comments reported on the #roo Twitter channel over the last month have included "I'm impressed", "liking it", "custom roo addons [are] very easy", "here comes some innovation", "the first milestone release will be cool!", "Roo looks interesting and works", "very impressive tool", "very cool" etc. We've also started seeing the first community-produced add-ons, underscoring the ease at which you can extend Roo with your own custom features.
Earlier this month we also concluded the community project naming competition, with "Spring Roo" easily emerging as the winner. A total of 952 eligible votes were received, distributed between Spring Roo (467), Spring Boost (180), Spring Spark (179), Spring HyperDrive (64) and Spring Dart (62). Thank you to everyone who voted!
This blog will be written assuming you're using the standalone Spring Roo shell. Unless otherwise specified, all commands work the same in both standard Spring Roo and also STS 2.3.0 or above. One of the main differences is when using the Roo shell you press TAB for completion options, whereas within STS the TAB key is replaced by CTRL + SPACE. We use CTRL + SPACE because it's the more traditional key combination for completions within Eclipse-based IDEs.
You should also verify you have Maven 2.0.9 or above installed. While Roo itself doesn't use Maven and can operate without it installed, projects created by Roo currently use Maven. In addition, be sure to delete the ROO_HOME variable if you installed one of the earlier Roo alpha releases.
Despite having online RSVP, we've still sent out paper wedding invitations. On the back of each invitation is a small "invitation code". These are not easily guessed, but they're easy to read and type in (no UUIDs!). The wedding invitation text invites the guest to visit our wedding RSVP site in order to RSVP. When they visit the wedding RSVP site, the guest will be asked to enter their invitation code.
Once the guest enters their invitation code, any existing RSVP record will be retrieved and allow them to edit it. If they haven't previously RSVPed, they'll be presented with a new RSVP form to fill in. The form will simply ask the guest how many people are attending, and any special requests (eg dietary needs). They'll also be asked to enter their email address, and if one is provided the application will send an email-based confirmation of their RSVP. We'll also keep a record of what date and time they RSVPed, which will be useful if they change their RSVP several times.
$ mkdir wedding
$ cd wedding
$ roo
If you followed the above installation instructions, you should be greeted with the Roo logo shown below. If you don't see a message like this, please go back and check your installation is correct.
There are quite a few usability features within Roo. If you type "hint" you will be presented with step-by-step instructions. Or if you type "help" you will see all of the commands currently available (these change at different phases of your project lifecycle). Plus you can press TAB in almost all cases for completion services. If you're learning Roo, "hint" is your friend. Not only will it teach you the commands, but it will also teach you about the shell and how the keyboard features work. Get into the habit of using "hint" whenever in doubt, especially during your first few Roo projects.
Let's begin our wedding project. After you type the "create project" command into the Roo shell, you should receive the following output:
roo> project --topLevelPackage com.wedding
Created /home/balex/wedding/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
As shown from the console output, Roo has created a Maven 2 project structure. Even if you quit Roo at this point and never reload it, at this moment you have a properly-configured Spring 3 web application, complete with URL rewriting, annotation-based classpath scanning and dependency injection of any class - even those created with the "new" keyword or via an ORM like Hibernate. You can even use "mvn tomcat:run" and start an embedded Tomcat container.
If you typed "hint" at this point, Roo would suggest you install a JPA provider and database. Let's do that now:
roo> persistence setup --provider HIBERNATE --database HYPERSONIC_PERSISTENT
Created SRC_MAIN_RESOURCES/META-INF/persistence.xml
Created SRC_MAIN_RESOURCES/META-INF/spring/database.properties
Managed SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml
Managed ROOT/pom.xml
Notice that we've selected Hibernate and a persistent Hypersonic database in the command. This selection happened via the TAB button, so we didn't actually need to type those commands and arguments fully. Notice also the two "Managed" statements at the bottom. These denote a file or directory Roo is managing. Roo has inbuilt file undo services, so should something go wrong, it will automatically rollback the problematic command.
Because Roo uses JPA, we enjoy the benefits of portability across different JPA implementations. If you look at the code that Roo generates, you'll see that not a single line of it is specific to a particular persistence provider. You'll also see the Roo-provided code is extremely efficient. One of the great strengths of Java is its significant performance, and Roo does everything from avoiding reflection to optimizing string operations in your toString() methods (and everything in between) to maximise runtime performance of your applications.
As we asked for a persistent database, it is stored in ~/wedding.* by default. There is a "database properties list" command which will show you the database configuration:
roo> database properties list
database.driverClassName = org.hsqldb.jdbcDriver
database.password =
database.url = jdbc:hsqldb:${user.home}/wedding
database.username = sa
While the default location would work just fine, let's change it to somewhere else:
roo> database properties set --key database.url --value jdbc:hsqldb:/home/balex/our-wedding
Managed SRC_MAIN_RESOURCES/META-INF/spring/database.properties
As shown by the console output, all Roo did was edit a stock-standard database.properties file. You could have just as validly edited your project files using a text editor or IDE. Roo doesn't mind. It was designed from the very beginning so that you could use other tools concurrently with Roo and everything would still work properly.
One reason you might like to use a "database properties set" command via Roo is because you're making a standalone script that can be replayed later. You can use the "script filename.roo" command to execute scripts, which are simply Roo commands in a standard text file format. I've included the wedding.roo script in the Roo 1.0.0 distribution for your convenience. Note that comments can also be included in the scripts using normal Java comment syntax (//, /* and */).
roo> entity --class ~.domain.Rsvp
Created SRC_MAIN_JAVA/com/wedding/domain
Created SRC_MAIN_JAVA/com/wedding/domain/Rsvp.java
Created SRC_MAIN_JAVA/com/wedding/domain/Rsvp_Roo_Entity.aj
Created SRC_MAIN_JAVA/com/wedding/domain/Rsvp_Roo_ToString.aj
Created SRC_MAIN_JAVA/com/wedding/domain/Rsvp_Roo_Configurable.aj
At this point I imagine some of you are wondering, "what are those .aj files?". In a nutshell, these are AspectJ inter-type declarations (ITDs) that very effectively deliver separation of concerns while also maintaining compatibility with future versions of the relevant Roo add-on. The .aj files are automatically created, maintained and deleted by Roo, allowing end users to safely ignore them. Indeed STS 2.1.0+ automatically hides them by default, just like Eclipse-based IDEs hide .classpath, .project and .settings resources. After all, these resources are merely internal outputs of the tool and it's pretty rare you would even open them - let alone maintain them yourself. I'm going to talk a lot more about these and other Roo internals in my next blog entry, so I'll defer further discussion until then.
You might have noticed we created the Rsvp entity within the ".domain" package. The "" character automatically expands to your project's top-level package, which you may recall we specified when originally creating the project. As such Roo fully understands the concept of Java packages, and allows you to structure your project packages in whatever way you find most intuitive.
Naturally an entity would normally contain some fields, so let's add them (Roo's output is omitted, as it simply manages the same files as listed above):
roo> field string code --notNull --sizeMin 1 --sizeMax 30
roo> field string email --sizeMax 30
roo> field number attending --type java.lang.Integer
roo> field string specialRequests --sizeMax 100
roo> field date confirmed --type java.util.Date
In the first line you'll note we use a --notNull argument, together with a --sizeMin and --sizeMax argument. These refer to the new Bean Validation standard, which is otherwise known as JSR 303. This particular standard offers automatic web and persistence tier validation, including creating the correct DDL for the tables in your database. One of the advantages of using Roo is you gain the benefit of relevant standards like JSR 303, JPA, the Servlet Specification and REST without any extra effort. Naturally you don't need to use the JSR 303 arguments if you don't wish to do so.
Another point to note from the above field commands is we didn't specify which entity we wanted to insert the fields into. Roo automatically determines you probably want to add the fields to Rsvp, as that's the last entity you worked with. You could also specify an "--class ~.SomeEntity" argument if you wish to be explicit or target the fields at another entity (in which case that entity will become the default for subsequent entity-related commands).
Let's start with JUnit integration tests. You can obtain an integration test with one command:
roo> test integration
Created SRC_TEST_JAVA/com/wedding/domain
Created SRC_TEST_JAVA/com/wedding/domain/RsvpDataOnDemand.java
Created SRC_TEST_JAVA/com/wedding/domain/RsvpIntegrationTest.java
Created SRC_TEST_JAVA/com/wedding/domain/RsvpDataOnDemand_Roo_Configurable.aj
Created SRC_TEST_JAVA/com/wedding/domain/RsvpDataOnDemand_Roo_DataOnDemand.aj
Created SRC_TEST_JAVA/com/wedding/domain/RsvpIntegrationTest_Roo_Configurable.aj
Created SRC_TEST_JAVA/com/wedding/domain/RsvpIntegrationTest_Roo_IntegrationTest.aj
This integration test will verify the common JPA operations like persist, remove, find, merge etc all work. A total of eight tests are performed per entity, and all build on Spring Framework's extensive integration test infrastructure. While we could run the integration tests at this stage, it's trivial to add a web tier as well:
roo> controller scaffold ~.web.RsvpController
Created SRC_MAIN_JAVA/com/wedding/web
Created SRC_MAIN_JAVA/com/wedding/web/RsvpController.java
Created SRC_MAIN_WEBAPP/WEB-INF/config
Created SRC_MAIN_WEBAPP/WEB-INF/config/webmvc-config.xml
Created SRC_MAIN_JAVA/com/wedding/web/RsvpController_Roo_Controller.aj
Created SRC_MAIN_WEBAPP/images
Created SRC_MAIN_WEBAPP/images/banner-graphic.png
Created SRC_MAIN_WEBAPP/images/springsource-logo.png
Created SRC_MAIN_WEBAPP/images/resultset_first.png
Created SRC_MAIN_WEBAPP/images/resultset_next.png
Created SRC_MAIN_WEBAPP/images/resultset_previous.png
Created SRC_MAIN_WEBAPP/images/resultset_last.png
Created SRC_MAIN_WEBAPP/images/us.png
Created SRC_MAIN_WEBAPP/images/de.png
Created SRC_MAIN_WEBAPP/images/list.png
Created SRC_MAIN_WEBAPP/images/add.png
Created SRC_MAIN_WEBAPP/styles
Created SRC_MAIN_WEBAPP/styles/roo-menu-left.css
Created SRC_MAIN_WEBAPP/styles/roo-menu-right.css
Created SRC_MAIN_WEBAPP/WEB-INF/classes
Created SRC_MAIN_WEBAPP/WEB-INF/classes/left.properties
Created SRC_MAIN_WEBAPP/WEB-INF/classes/right.properties
Created SRC_MAIN_WEBAPP/WEB-INF/layouts
Created SRC_MAIN_WEBAPP/WEB-INF/layouts/layouts.xml
Created SRC_MAIN_WEBAPP/WEB-INF/layouts/default.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views
Created SRC_MAIN_WEBAPP/WEB-INF/views/dataAccessFailure.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/resourceNotFound.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/uncaughtException.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/index.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/views.xml
Created SRC_MAIN_WEBAPP/WEB-INF/tags
Created SRC_MAIN_WEBAPP/WEB-INF/tags/pagination.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/tags/language.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/tags/theme.tagx
Created SRC_MAIN_WEBAPP/WEB-INF/i18n
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/messages.properties
Created SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_de.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/i18n/messages_de.properties
Created SRC_MAIN_WEBAPP/images/show.png
Created SRC_MAIN_WEBAPP/images/update.png
Created SRC_MAIN_WEBAPP/images/delete.png
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp
Managed SRC_MAIN_WEBAPP/WEB-INF/config/webmvc-config.xml
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/list.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/show.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/create.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/update.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/views.xml
Created SRC_MAIN_WEBAPP/WEB-INF/urlrewrite.xml
Created SRC_MAIN_WEBAPP/WEB-INF/web.xml
Managed SRC_MAIN_WEBAPP/WEB-INF/web.xml
Managed ROOT/pom.xml
The automatic web tier provided by Roo builds on Spring Framework 3's excellent REST support. All endpoints are completely RESTful and use clean, properly-formed URLs. Roo's automatic web tiers are useful in a number of situations, in particular:
roo> selenium test --controller ~.web.RsvpController
Created SRC_MAIN_WEBAPP/selenium
Created SRC_MAIN_WEBAPP/selenium/test-rsvp.xhtml
Created SRC_MAIN_WEBAPP/selenium/test-suite.xhtml
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Managed ROOT/pom.xml
Okay, let's see the application in action by running the following commands:
roo> perform test
(Maven console output condensed)
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.wedding.domain.RsvpIntegrationTest
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.726 sec
roo> quit
$ mvn tomcat:run
You can now use your browser to visit http://localhost:8080/wedding. When you're ready to test the web tier, leave the Tomcat server running and then execute the following command:
$ mvn selenium:selenese
During execution of the Selenium tests, you should see an image similar to:
roo> logging setup --package WEB --level DEBUG
Created SRC_MAIN_RESOURCES/META-INF/spring/log4j.properties
Managed SRC_MAIN_WEBAPP/WEB-INF/web.xml
Let's now turn our attention to security. At present anyone can access our web site and use the existing RESTful administrative backend to create, update and delete RSVPs. Recall from the application requirements that we wanted to use invitations codes (printed on the back of the cards) to ensure only invited guests can RSVP. Fortunately, Spring Security offers us a very fast way of satisfying this requirement, and Roo can install Spring Security in one line:
roo> security setup
Managed ROOT/pom.xml
Created SRC_MAIN_RESOURCES/META-INF/spring/applicationContext-security.xml
Created SRC_MAIN_WEBAPP/WEB-INF/views/login.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/views/views.xml
Managed SRC_MAIN_WEBAPP/WEB-INF/web.xml
There are also similar commands like "web flow" and "jms setup", but we're not going to explore them in this blog entry. You can expect to see many more "install" commands in future versions of Roo (and you're welcome to add installer requests to the Roo issue tracker).
roo> controller class --class ~.web.PublicRsvpController
Created SRC_MAIN_JAVA/com/wedding/web/PublicRsvpController.java
Managed SRC_MAIN_WEBAPP/WEB-INF/web.xml
Managed ROOT/pom.xml
The PublicRsvpController will respond to HTTP GET and POST requests. Two stubbed methods for these operations have automatically been provided in the PublicRsvpController.java source file.
If we consider the GET use case, our objective is to retrieve the correct RSVP for a particular invitation code. The way this will work is Spring Security will require people to login to use the application, and we'll treat each invitation code as a unique login name. As such, the GET method needs to obtain the currently logged on user's name from Spring Security and then retrieve the corresponding RSVP from the database. Normally you'd go and write a JPA QL query at this point to obtain the specific Rsvp instance that has the matching code, but because you're using Roo you can save yourself the trouble and instead use a dynamic finder.
Dynamic finders provide you with an almost unlimited range of pre-canned queries. These queries all internally use JPA QL, delivering maximum standards-based compatibility and portability. All dynamic finders (and other Roo methods) are implemented as properly-formed, type-safe Java methods - bringing all the normal advantages of familiarity, IDE code assist, debugger integration and significant runtime performance. You can list the available dynamic finders using a command such as this:
roo> finder list --class ~.domain.Rsvp --filter code,equ
findRsvpsByCodeEquals(String code)
findRsvpsByCodeNotEquals(String code)
Note the "--filter" argument is limiting the output to only those proposed method signatures that contain the "code" and "equ" strings. You can instruct Roo you'd like to see more combinations by omitting the "-filter" argument, or specifying "-depth 2" (or 3, 4 etc if you'd like more properties involved in your query).
Once you find the dynamic finder you'd like to use, simply add it:
roo> finder add --finderName findRsvpsByCodeEquals
Managed SRC_MAIN_JAVA/com/wedding/domain/Rsvp.java
Created SRC_MAIN_JAVA/com/wedding/domain/Rsvp_Roo_Finder.aj
Managed SRC_MAIN_JAVA/com/wedding/web/RsvpController_Roo_Controller.aj
Created SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/findRsvpsByCodeEquals.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/views/menu.jspx
Managed SRC_MAIN_WEBAPP/WEB-INF/views/rsvp/views.xml
If we consider the POST use case for our PublicRsvpController, our requirements specified that we should send our guests an email to confirm their RSVP. Normally we'd pull out the Spring reference guide and locate the section on configuring email support, but instead we'll just ask Roo to deal with it for us:
roo> email sender setup --hostServer 127.0.0.1
Created SRC_MAIN_RESOURCES/META-INF/spring/email.properties
Managed SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml
Managed ROOT/pom.xml
roo> field email template --class ~.web.PublicRsvpController
Managed SRC_MAIN_JAVA/com/wedding/web/PublicRsvpController.java
Managed SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml
Managed SRC_MAIN_JAVA/com/wedding/web/PublicRsvpController.java
The final command added a Spring MailSender field to PublicRsvpController, plus provided a method that shows us how to use it.
While on the topic of email integration, my colleague Stefan Schmidt has just published a separate blog entry showing how to use the Roo email and JMS add-ons together. The article shows you more advanced configuration options, such as how to use Gmail to send your email messages.
roo> perform eclipse
(Maven console output condensed)
Finally, let's import the project into Eclipse/STS. You do this by loading Eclipse/STS, then selecting File > Import > Existing Projects into Workspace, and selecting the project's directory. If you're not using STS 2.3.0 or above, ensure you have separately installed AJDT 1.6.5 or above. When prompted by AJDT whether you'd like to enable JDT weaving, select to enable weaving. This will result in a better Roo experience when using Eclipse's Java editor.
Start by editing applicationContext-security.xml. Make some minor changes so that it resembles the following file:
<http auto-config="true" use-expressions="true">
<form-login login-processing-url="/static/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t"/>
<logout logout-url="/static/j_spring_security_logout"/>
<intercept-url pattern="/rsvp/**" access="hasRole('ROLE_ADMIN')"/>
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/static/**" access="permitAll" />
<intercept-url pattern="/login**" access="permitAll" />
<intercept-url pattern="/**" access="isAuthenticated()" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<user-service>
<user name="admin1234" password="ignored" authorities="ROLE_ADMIN"/>
<user name="user12345" password="ignored" authorities="ROLE_USER"/>
<user name="user67890" password="ignored" authorities="ROLE_USER"/>
</user-service>
</authentication-provider>
</authentication-manager>
The file above shows that the invitation codes are actually usernames, and we're ignoring passwords. Spring Security doesn't realize we're ignoring passwords, so we need to edit the src/main/webapp/WEB-INF/views/login.jspx and add an <input name="j_password" type="hidden" value="ignored"/> line within the form. Of course, the existing <div> containing the "j_password" label and input element should be deleted. Some appropriate text within the login.jsp explaining to guests where on the card they can find their invitation code should also be added to this file.
Security is now setup. Let's now open the PublicRsvpController.java file. As shown, Roo has already stubbed the email features, and provided you empty Spring MVC methods to complete. This is the only actual Java programming required in the entire application, and because these use the normal features of Spring MVC and Spring's MailSender class, I won't discuss them further here:
@RequestMapping("/publicrsvp/**")
@Controller
@SessionAttributes("rsvp")
public class PublicRsvpController {
@Autowired
private transient MailSender mailTemplate;
@RequestMapping
public String get(ModelMap modelMap) {
modelMap.put("rsvp", getRsvp());
return "publicrsvp";
}
@RequestMapping(method = RequestMethod.POST)
public String post(@ModelAttribute("rsvp") Rsvp rsvp, ModelMap modelMap) {
rsvp.setConfirmed(new Date());
if (rsvp.getId() == null) {
rsvp.persist();
} else {
rsvp.merge();
}
if (rsvp.getEmail().length() > 0) {
sendMessage("Ben Alex <[email protected]>", "RSVP to our wedding", rsvp.getEmail(), "Your RSVP has been saved: " + rsvp.toString());
}
modelMap.put("rsvp", rsvp);
return "thanks";
}
private Rsvp getRsvp() {
Rsvp rsvp = new Rsvp();
try {
String code = SecurityContextHolder.getContext().getAuthentication().getName();
rsvp.setCode(code);
// Cast due to http://java.sun.com/javaee/5/docs/api/javax/persistence/Query.html#getSingleResult()
rsvp = (Rsvp) Rsvp.findRsvpsByCodeEquals(code).getSingleResult();
} catch (DataAccessException ignored) { /* no Rsvp for this code was found, so start a new Rsvp */ }
return rsvp;
}
private void sendMessage(String mailFrom, String subject, String mailTo, String message) {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setFrom(mailFrom);
simpleMailMessage.setSubject(subject);
simpleMailMessage.setTo(mailTo);
simpleMailMessage.setText(message);
mailTemplate.send(simpleMailMessage);
}
}
Note in the "get" and "post" methods we return a string, which should correlate with the JSP view name we would like rendered. As such our next step is to provide those two JSPs. Fortunately Roo built some JSP files that will serve as useful templates.
First rename src/main/webapp/WEB-INF/index.jsp to thanks.jsp. This will be the page displayed when the "post" method returns. You probably want to add something like, "Your RSVP has been confirmed: ${rsvp}".
Next copy src/main/webapp/WEB-INF/views/rsvp/create.jspx to src/main/webapp/WEB-INF/views/publicrsvp.jspx. This page should then be edited. You can safely delete the "code" and "confirmed" sections, as both are taken care of by the PublicRsvpController. You should also change the "form_url" variable assignment to <c:url value="/publicrsvp" var="form_url"/>.
Because Spring Roo uses Tiles to allow easy branding of each web view, you need to edit the src/main/webapp/WEB-INF/views/views.xml file. You need to rename the "index" definition to "thanks", and also add a new definition for "publicrsvp" to the new publicrsvp.jspx. The final file should resemble:
<tiles-definitions>
<definition extends="public" name="thanks">
<put-attribute name="body" value="/WEB-INF/views/thanks.jspx"/>
</definition>
<definition extends="public" name="dataAccessFailure">
<put-attribute name="body" value="/WEB-INF/views/dataAccessFailure.jspx"/>
</definition>
<definition extends="public" name="resourceNotFound">
<put-attribute name="body" value="/WEB-INF/views/resourceNotFound.jspx"/>
</definition>
<definition extends="public" name="uncaughtException">
<put-attribute name="body" value="/WEB-INF/views/uncaughtException.jspx"/>
</definition>
<definition extends="public" name="login">
<put-attribute name="body" value="/WEB-INF/views/login.jspx"/>
</definition>
<definition extends="public" name="publicrsvp">
<put-attribute name="body" value="/WEB-INF/views/publicrsvp.jspx"/>
</definition>
</tiles-definitions>
The very last step is to edit src/main/webapp/WEB-INF/urlrewrite.xml and change the URL rewrite rule for /. The /app/index should be modified to /app/publicrsvp/, which indicates to perform a GET of the new PublicRsvpController by default.
You should now be ready to test deployment. You have several options:
Naturally at this stage we'd normally tidy up the look and feel of publicly-visible parts of the application. Then we'd run "perform package" to provide a WAR that is ready to deploy to a production server environment such as SpringSource tc Server or SpringSource dm Server.