Spring Live
Spring Live
by Matt Raible
ISBN: 0974884340
Many designations used by organizations to distinguish their products are claimed as trademarks. These trademarked names may
appear in this book. We use the names in an editorial fashion only with no intention of infringing on the trademark; therefore you will
not see the use of a trademark symbol with every occurrence of the trademarked name.
As every precaution has been taken in writing this book, the author and publisher will in no way be held liable for any loss or damages
resulting from the use of information contained in this book.
Table of Contents
Table of Contents 3
Dedication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
About the Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Download Struts and Spring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Create Project Directories and an Ant Build File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Tomcat and Ant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Create Unit Test for Persistence Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Configure Hibernate and Spring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
How Spring Is Configured in Equinox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Implement UserDAO with Hibernate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Run Unit Test and Verify CRUD with DAO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Create Manager and Declare Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Create Unit Test for Struts Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Create Action and Model (DynaActionForm) for Web Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Run Unit Test and Verify CRUD with Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Complete JSPs to Allow CRUD through a Web Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Verify JSP’s Functionality through Your Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Adding Validation Using Commons Validator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Add the Validator Plug-in to struts-config.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Edit the validation.xml File to Specify That lastName Is a Required Field . . . . . . . . . . . . . . . . . . . 54
Change the DynaActionForm to DynaValidatorForm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Configure Validation for save() Method, But Not for Others . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Spring Live
Table of Contents 4
Spring Live
Table of Contents 5
Spring Live
Table of Contents 6
Chapter 7: Persistence Strategies: Hibernate, iBATIS, JDBC, JDO and OJB . 199
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Preparing for the Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
Hibernate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
Test It! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
MySQL Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
Lazy-Loading Dependent Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
Community and Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
iBATIS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Test It! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
Retrieving Generated Primary Keys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Community and Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Spring JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Spring Live
Table of Contents 7
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
JUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
Mock Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
Integration Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
Testing the Database Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
DbUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Database Switching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
Overriding Beans for Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
Use JNDI DataSource in Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
Load Context Once . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
Testing the Service Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
Using Mocks for DAO Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
EasyMock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
jMock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
Testing the Web Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
Testing Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
Spring Mocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
Cactus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
Spring Live
Table of Contents 8
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
Logging Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
Definitions and Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
Pointcuts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
Weaving Strategies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
JDK Dynamic Proxies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
Dynamic Byte-Code Generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
Custom Class Loading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
Language Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
Convenient Proxy Beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
AutoProxy Beans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
Practical AOP Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Caching in the Middle Tier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
Event Notification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
Spring Live
Dedication
Spring Live
Acknowledgments
I'd like to thank my wife, Julie, for being such a great mother and terrific wife. Your ability to entertain
Abbie and comfort Jack at the same time is amazing. Thank you for building our home and giving birth
to Jack while I wrote this book. Abbie and Jack, thanks for reminding me that work is not as important
as playing with you. Your smiles and giggles always make my day.
I'd like to thank my parents, Joe and Barb, for instilling in me the passion to learn. I learned a lot about
computers from my Dad's passion for them. Dad, thanks for being such a good role model and great
friend. To my sister Kalin, I appreciate your listening to all my stories when we walked home from the
bus stop. I can't help but think that my storytelling as a child helped me become an author.
Thanks to Rod Johnson for providing J2EE developers with such a great framework for developing
J2EE applications more easily. Juergen, Colin, Keith and the rest of the Spring Framework team – keep
up the coding! Your time, commitment and user support are greatly appreciated. Open-source is a great
way to develop software, and your team is one of the best.
To the SourceBeat founders, Matt Filios and James Goodwill, thanks for the opportunity to write and to
do it such an innovative way. SourceBeat is a great model from which readers will truly benefit. Dion, I
appreciate all the time you spent plowing through the code in Spring Live and ensuring all the examples
worked. You've been a great technical editor. Amy, thanks for changing all my "we's" to "you's" and
causing my words to make sense. Your ability to make a technical book flow is remarkable.
Spring Live
About the Author
Matt Raible is a J2EE Consultant and Developer living in Denver, Colorado. He grew up in a log cabin
in the backwoods of Montana, without any electricity or running water. He loves Montana and its deli-
cious huckleberries and tries to visit a few times a year.
Matt has been developing websites since before Netscape 1.0. He wrote a lot of HTML, JavaScript and
CSS while in college in the 1990s. When he graduated with degrees in Russian, International Business
and Finance, he found that he actually liked computers more and became a consultant.
Matt has been developing Java web applications since 1999 and is President of Raible Designs, Inc. He
is also a member of the J2EE 5.0 Expert Group and hopes to help them make J2EE easier for developers.
Rather than just talking about technologies that make things easier, Matt also uses them in his open-
source application starter kit called AppFuse. He is actively involved in many open-source projects and
blogs about his experiences regularly on www.raibledesigns.com.
Spring Live
Preface
Thank you for buying a subscription to Spring Live. Unlike traditional books, this book will change,
grow and become better over the next year. Your subscription entitles you to have a say in what goes into
this book over the next 12 months. While the 1.0 version has been fun to write, the updates to this book
are what excite me.
Together, we can act as a team to produce an in-depth, pragmatic and interesting book about the Spring
Framework. As you provide feedback, I can improve existing content in the book and add new content
that interests you. This book is yours as much as it is mine; let's make it great together.
Spring Live is an introduction to the Spring Framework. Version 1.0 targets readers who have no experi-
ence with Spring. The update chapters will target the advanced Spring user. As the Spring Framework
grows and improves over the next year, you will need a book like Spring Live to keep up and get your
job done. If you know of projects that are coming up and using a certain technology in Spring, let me
know; maybe I can write a chapter on it before you start.
This book will be managed similar to an open-source project. There will be monthly releases of the book
and they will be announced on my Spring Live Weblog. All of the source code developed in the chapters
is available at http://sourcebeat.com/downloads. This page has links to download before and after snap-
shots of each chapter's code.
If you find issues or would like to request features, please enter them in JIRA. For general discussion
questions, you can use the Spring Live Forums or Equinox User Mailing List. I've also setup a Conflu-
ence Wiki for tracking FAQs and update chapters. You can also send e-mail to me directly at
[email protected].
Thanks to Atlassian for the JIRA and Confluence software and to Javalobby for hosting the weblog and
forums.
Sincerely,
Matt Raible
Spring Live
Introduction
This book is written for Java developers familiar with web frameworks. Its main purpose is to allow Java devel-
opers to get up to speed quickly with Spring. It is quite code-intensive because code examples are more useful
than theory when learning a new technology.
This book includes a useable sample application that uses Spring and Hibernate to manage the persistence layer
and the middle tier. This application uses a simple webapp starter kit I developed called Equinox. Equinox is
really just an Ant build file, a directory structure, and the JARs you need to develop a Spring-based J2EE appli-
cation.
In Chapter 2, Equinox is used to quickly develop a simple webapp, called MyUsers, that does CRUD on a
database table. JUnit, Struts, Spring and Hibernate are used to develop MyUsers, and HSQL and Tomcat are used
as a deployment platform. In Chapter 4, MyUsers is refactored to use Spring's MVC framework for the web tier.
All the code developed in the chapters below is done using a test-first methodology.
Chapter 1: Introducing Spring covers the basics of Spring, how it came to be and why it’s getting so much
press and rave reviews. It compares the traditional way of resolving dependencies (binding interfaces to imple-
mentations using a Factory Pattern) and how Spring does it all in XML. It also briefly covers how it simplifies
the Hibernate API.
Chapter 2: Spring Quick Start Tutorial is a tutorial on how to write a simple Spring web application using the
Struts MVC framework for the front end, Spring for the middle-tier glue, and Hibernate for the back end. In
Chapter 4, this application will be refactored to use the Spring MVC framework.
Chapter 3: The BeanFactory and How It Works. The BeanFactory represents the heart of Spring, so it’s
important to know how it works. This chapter explains how bean definitions are written, their properties, depen-
dencies, and autowiring. It also explains the logic behind making singleton beans versus prototypes. Then it
delves into Inversion of Control, how it works, and the simplicity it brings. This chapter dissects the Lifecyle of
a bean in the BeanFactory to show how it works. This chapter also inspects the applicationContext.xml file for
the MyUsers application created in Chapter 2.
Chapter 4: Spring’s MVC Framework describes the many features of Spring’s MVC framework. It shows you
how to replace the Struts layer in MyUsers with Spring. It covers the DispatcherServlet, various Controllers,
Handler Mappings, View Resolvers, Validation and Internationalization. It also briefly covers Spring’s JSP Tags.
Chapter 5: Advanced MVC Framework - Templates, Validation, Exceptions and Uploading Files covers
advanced topics in web frameworks, particularly validation and page decoration. It shows the user how to use
Tiles or SiteMesh to decorate a web application. It also explains how the Spring framework handles validation,
and shows examples of using it in the web business layers. Finally, it explains a strategy for handling exceptions
in the controllers, how to upload files and how to send e-mail.
Chapter 6: View Options covers the view options in Spring’s MVC architecture. At the time of this writing, the
options are JSP, Velocity, FreeMarker, XSLT, PDF and Excel. This chapter aims to become a reference for con-
figuring all Spring-supported views. It also contains a brief overview how each view works and compares con-
structing a page in MyUsers with each option. Additionally, it focuses on internationalization for each view
option.
Spring Live
Introduction 14
Chapter 7: Persistence Strategies: Hibernate, iBATIS, JDBC, JDO and OJB. Hibernate is quickly becoming
a popular choice for persistence in Java applications, but sometimes it doesn’t fit. If you have an existing data-
base schema, or even pre-written SQL, sometimes it’s better to use JDBC or iBATIS (which supports external-
ized SQL in XML files). This chapter refactors the MyUsers application to support both JDBC and iBATIS as
persistence framework options. It also implements the UserDAO using JDO and OJB to showcase Spring's
excellent support for these frameworks.
Chapter 8: Testing Spring Applications explains how to use test-driven development to create high-quality,
well-tested, Spring-based applications. You will learn how to test your components using tools like EasyMock,
jMock and DBUnit. For the Controllers, you will learn how to use Cactus for in-container testing, and Spring
Mocks for out-of-container testing. Lastly, you will learn how to use jWebUnit and Canoo's WebTest for testing
the web interface.
Chapter 9: AOP. Aspect Oriented Programming has received a lot of hype in the Java community in the last
year. What is AOP and how can it help you in your applications? This chapter will cover the basics of AOP and
give some useful examples of how AOP might help you.
Spring Live
Chapter
1
Introducing Spring
The Basics of Spring and Its History
This chapter covers the basics of Spring, how it came to be and why it’s getting so much press and rave reviews.
It compares the traditional way of resolving dependencies (binding interfaces to implementations using a
Factory Pattern) and how Spring does it all in XML. It also briefly covers how it simplifies the Hibernate API.
Spring Live
The History of Spring 2
Rod Johnson is the ingenious inventor of Spring. It started from infrastructure code in his book, Expert One-on-
One J2EE Design and Development, in late 2002. I highly recommend this book. In it, Rod explains his
experiences with J2EE and how Enterprise JavaBeans (EJBs) are often overkill for projects. He believes a
lightweight, JavaBeans-based framework can suite most developers’ needs. The framework described eventually
became known as The Spring Framework when it was open-sourced on SourceForge in February 2003. At this
point, Rod was joined by Juergen Hoeller as Lead Developer and right-hand man of Spring. Rod and Juergen
have added many other developers over the last several months. At the time of this writing, sixteen developers
are on Spring’s committee list. Rod and Juergen have recently written a book titled Expert One-on-One J2EE
Development without EJB that describes how Spring solves many of the problems with J2EE.
The architectural foundations of Spring have been developed by Rod since early 2000 (before Struts or any other
frameworks I know of). These foundations were built from Rod’s experiences building infrastructure on a
number of successful commercial projects. Spring’s foundation is constantly being enhanced and re-enforced by
hundreds (possibly thousands) of developers. All are bringing their experience to the table, and you can literally
watch Spring become stronger day-by-day. Its community is thriving, its developers are enthusiastic and
dedicated and it’s quite possibly the best thing that has ever happened to J2EE.
Spring Live
About Spring 3
About Spring
According to Spring’s web site, “Spring is a layered J2EE application framework based on code published in
Expert One-on-One J2EE Design and Development by Rod Johnson.” At its core, it provides a means to manage
your business objects and their dependencies. For example, using Inversion of Control (IoC), it allows you to
specify that a Data Access Object (DAO) depends on a DataSource. It also allows a developer to code to
interfaces and simply define the implementation using an XML file. Spring contains many classes that support
other frameworks (such as Hibernate and Struts) to make integration easier.
Following J2EE Design Patterns can be cumbersome and unnecessary at times (and in fact often become anti-
patterns). Spring is like following design patterns, but everything is simplified. For example, rather than writing
a ServiceLocator to look up Hibernate Sessions, you can configure a SessionFactory in Spring. This
allows you to follow the best practices of J2EE field experts rather than trying to figure out the latest pattern.
If you follow online forums like TheServerSide.com or JavaLobby.org, you’ve likely seen Spring mentioned. It
has even more traction in the Java blogging community (such as JavaBlogs.com and JRoller.com). Many
developers are describing their experiences with Spring and praising its ease of use.
Not only does Spring solve developers’ problems, it also enforces good programming practices like coding to
interfaces, reducing coupling and allowing for easy testability. In the modern era of programming, particularly in
Java, good developers are practicing Test Driven Development (TDD). TDD is a way of letting your tests, or
clients of your classes, drive the design of those classes. Rather than building a class, then trying to retrofit the
client, you’re building the client first. This way, you know exactly what you want from the class you’re
developing. Spring has a rich test suite of its own that allows for easy testing of your classes.
Compare this to “best practices” from J2EE, where the blueprints recommend that you use EJBs to handle
business logic. EJBs require an EJB container to run, so you have to startup your container to test them. When’s
the last time you started up an EJB server like WebLogic, WebSphere or JBoss? It can test your patience if you
have to do it over and over to test your classes.
NOTE
Recently, an article titled “Testing EJBs with OpenEJB” showed a faster way to test EJBs, but it’s only a
workaround for the testability problems inherent in EJBs.
Spring Live
About Spring 4
With success, there’s always some criticism. The most compelling argument I’ve seen against Spring is that it’s
not a “standard,” meaning it’s not part of the J2EE specification and it hasn’t been developed through the Java
Community Process. The same folks who argue against Spring advocate EJBs, which are standard. However, the
main reason for standards is to ensure portability across appservers. The code you develop for one server should
run on another, but porting EJBs from one EJB container to another is not as simple as it should be. Different
vendors require different deployment descriptors and there’s no common way of configuring data sources or
other container dependencies. However coding business logic with Spring is highly portable across containers –
with no changes to your code or deployment descriptors!
While Spring “makes things easier,” some developers complain that it’s “too heavyweight.” However, Spring is
really an a la carte framework where you can pick and choose what you want to use. The development team has
segmented the distribution so you can use only the Java ARchives (JARs) you need.
The J2EE Design and Development book illustrates Spring and how it works. Spring is a way to configure
applications using JavaBeans. When I say JavaBeans, I mean Java classes with getters and setters (also called
accessors and mutators) for its class variables. Specifically, if a class exposes setters, Spring configures that
class. Using Spring, you can expose any of your class dependencies (that is, a database connection) with a setter,
and then configure Spring to set that dependency. Even better, you don’t have to write a class to establish the
database connection; you can configure that in Spring too! This dependency resolution has a name: Inversion of
Control or Dependency Injection. Basically, it’s a technical term for wiring dependent objects together through
some sort of container.
Spring Live
About Spring 5
Spring has seven individual modules, each having its own JAR file Below is a diagram1 of the seven modules:
In the MyUsers application that you will develop in Chapter 2, you will use several of the modules above, but
not all of them. Furthermore, you’ll only be using a fraction of the functionality in each module.
Spring Live
About Spring 6
The diagram below shows the Spring modules that MyUsers will be using.
Spring Live
How Spring Makes J2EE Easier 7
In Figure 1.2, you can see that Spring provides a lot of pieces in the application you’ll be building. At first
glance, it looks intrusive and that you’ll to have to know a lot about Spring. Not true, my friend. In most cases,
you won’t even see Spring’s API being used. For instance, in the middle tier, you will set up declarative
transactions and set DAOs on the business delegates. Your code will not see a single import from Spring, nor any
Factory Patterns to figure out which DAO implementation to use. You simply configure it all in an XML file -
and voila! - clean design is yours.
The following sections talk about some of the processes that Spring simplifies.
Coding to Interfaces
Coding to interfaces allows developers to advertise the methods by which their objects will be used. It’s helpful
to design your application using interfaces because you gain a lot of flexibility in your implementation.
Furthermore, communicating between the different tiers using interfaces promotes loose coupling of your code.
Easy Testability
Using Test-Driven Development (TDD) is the best way to rapidly produce high-quality code. It allows you to
drive your design by coding your clients (the tests) before you code interfaces or implementations. In fact,
modern IDEs like Eclipse and IDEA will allow you to create your classes and methods on-the-fly as you’re
writing your tests. Spring-enabled projects are easy to test for two reasons:
• You can easily load and use all your Spring-managed beans in a JUnit test. This allows you to
interact with them as you normally would from any client.
• Your classes won’t bind their own dependencies. This allows you to ignore Spring in your tests
and set mock objects as dependencies.
Spring Live
How Spring Makes J2EE Easier 8
In order to have an easily maintainable and extendable application, it’s not desirable to tightly couple your code
to a specific resource (for example, you may have code that uses SQL functions that are specific to a database
type). Of course, it’s often easier to code to a specific database if the proprietary functionality helps you get your
job done quicker. When you do end up coding proprietary functionality into a class, the J2EE Patterns
recommend that you use a Factory Pattern to de-couple your application from the implementing class.
In general, it’s a good idea to create interfaces for the different layers of your application. This allows the
individual layers to be ignorant of the implementations that exist. A typical J2EE application has three layers:
• Data Layer: classes that talk to a database or other data storage system.
• Business Logic: classes that hold business logic provide a bridge between the GUI layer and data
layer.
• User Interface: classes and view files used to compose a web or desktop interface to a user.
Spring Live
How Spring Makes J2EE Easier 9
A Factory Pattern (also known as the Abstract Factory and Factory Method from the Gang of Four Patterns)
allows you to easily switch from one implementation to another by using a couple of factory classes. Typically,
you’ll create an abstract DAOFactory class and a factory class for the specific implementation (such as
DAOFactoryMySQL). For more information, see Core J2EE Patterns – Data Access Object from the J2EE
Patterns Catalog.
Spring Live
How Spring Makes J2EE Easier 10
The Factory Pattern is a complicated J2EE pattern. Not only does it require two classes to setup, but it also
introduces issues with managing dependencies of those “factoried” objects. For instance, if you’re getting a
DAO from the factory, how do you pass in a connection (rather than opening one for each method)? You can pass
it in as part of the constructor, but what if you’re using a DAO implementation that requires a Hibernate Session?
You could make the constructor parameter a java.lang.Object and than cast it to the required type, but it just
seems ugly.
The better way is to use Spring to bind interfaces to implementations. Everything is configured in an XML file
and you can easily switch out your implementation by modifying that file. Even better, you can write your unit
tests so no one knows which implementation you’re using – and you can run them for numerous
implementations. It works great with iBATIS and Hibernate DAO implementations. Since the test is in fact a
client, it’s a great way to ensure the business logic layer will work with an alternative DAO implementation.
Here’s an example of getting a UserDAO implementation using Spring:
ApplicationContext ctx =
new ClassPathXmlApplicationContext("/WEB-INF/applicationContext.xml");
UserDAO dao = (UserDAO) ctx.getBean("userDAO");
You’ll have to configure the “userDAO” bean in the /WEB-INF/applicationContext.xml file. Below is a code
snippet from Chapter 2:
If you want to change the implementation of UserDAO, all you need to change is the “class” attribute in the
above XML block. It’s a cleaner pattern that you can use throughout your application. All you need to do is add a
few lines to your beans definition file. Furthermore, Spring manages the Hibernate Session for this DAO for you
via the "sessionFactory" property. You don’t even need to worry about closing it anymore!
NOTE
Spring is often referred to as a “lightweight container” because you talk to its “ApplicationContext” in order to get
instantiated objects. The objects are defined in a context file (also called beans definition file). This file is simply an
XML file with a number of <bean> elements. In a sense, it’s a “bean container” or an “object library,” where
everything has been set up and is ready to be used. It’s not really a container in the traditional sense (such as
Tomcat or WebLogic), but more of a “configured beans provider.”
Spring Live
How Spring Makes J2EE Easier 11
Another example of Spring’s usability is its first-class support for Object/Relational Mapping (ORM) tools. The
first advantage of using the ORM support classes is you don’t need to try/catch many of the checked exceptions
that these APIs throw. Spring wraps the checked exceptions with runtime exceptions, allowing the developer to
decide if he wants to catch exceptions. Below is an example of a getUsers() method from a
UserDAOHibernate class without Spring:
try {
list = ses.find("from User u order by upper(u.username)");
} catch (HibernateException he) {
he.printStackTrace();
throw new DAOException(he);
}
return list;
}
Here is an example using Spring’s Hibernate support (by extending HibernateDaoSupport), which is much
shorter and simpler:
From these examples, you can see how Spring makes it easier to de-couple your application layers, your
dependencies and it handles configuring and binding a class’s dependencies. It also greatly simplifies the API to
use ORM tools, such as Hibernate.
Spring Live
Summary 12
Summary
This chapter discussed the history of Spring, why everyone loves it and how it makes J2EE development easier.
Examples were provided comparing the traditional Factory Pattern to Spring’s ApplicationContext, as well
as a before and after view of developing with Hibernate.
Spring’s ApplicationContext can be thought of as a “bean provider” that handles instantiating objects,
binding their dependencies and providing them to you pre-configured.
Chapter 2 is a tutorial for developing a web application that uses Spring, Hibernate and Struts to manage users in
a database. This application will demonstrate how Spring makes J2EE and Test-Driven Development easier.
Chapter 3 examines the Spring Core module, as well as the lifecycle of the beans it manages. This is the heart
and brain of Spring – controlling how objects work together and providing them with the support they need to
survive.
Spring Live
Chapter
2
This chapter is a tutorial on how to write a simple Spring web application using the Struts MVC framework for
the front end, Spring for the middle-tier glue, and Hibernate for the back end. In Chapter 4, this application will
be refactored to use the Spring MVC framework.
Spring Live
Overview 14
Overview
You will create a simple application for user management that does basic CRUD (Create, Retrieve, Update and
Delete). This application is called MyUsers, which will be the sample application throughout the book. It’s a 3-
tiered webapp, with an Action that calls a business service, which in turn calls a Data Access Object (DAO). The
diagram below shows a brief overview of how the MyUsers application will work when you finish this tutorial.
The numbers below indicate the order of flow – from the web (UserAction) to the middle tier,
(UserManager), to the data layer (UserDAO) – and back again.
This application uses Struts as the MVC framework because most readers are familiar with Struts. The real
power of Spring lies in its declarative transactions, dependency binding and persistence support (for example
Hibernate and iBATIS). Chapter 4 refactors this application to use Spring’s MVC framework.
Spring Live
Overview 15
10. Run the unit test and verify CRUD with Action.
Spring Live
Download Struts and Spring 16
• JAVA_HOME
• ANT_HOME
• CATALINA_HOME
• JAVA_HOME/bin
• ANT_HOME/bin
• CATALINA_HOME/bin
To develop a Java-based web application, developers download JARs, create a directory structure, and create an
Ant build file. For a Struts-only application, simplify this by using the struts-blank.war, which is part of the
standard Struts distribution. For a webapp using Spring’s MVC framework, use the webapp-minimal application
that ships with Spring. Both of these are nice starting points, but neither simplifies the Struts-Spring integration
nor takes into account unit testing. Therefore, I have made available to my readers Equinox.
Equinox is a bare-bones starter application for creating a Struts-Spring web application. It has a pre-defined
directory structure, an Ant build file (for compiling, deploying and testing), and all the JARs you will need for a
Struts, Spring and Hibernate-based webapp. Much of the directory structure and build file in Equinox is taken
from my open-source AppFuse application. Therefore, Equinox is really just an “AppFuse Light” that allows
rapid webapp development with minimal setup. Because it is derived from AppFuse, you will see many
references to it in package names, database names and other areas. This is done purposefully so you can migrate
from an Equinox-based application to a more robust AppFuse-based application.
1. You can learn more about how I set up my development environment on Windows at http://raibledesigns.com/wiki/
Wiki.jsp?page=DevelopmentEnvironment.
Spring Live
Create Project Directories and an Ant Build File 17
To set up your initial directory structure and Ant build file, extract the Equinox download onto your hard drive. I
recommend putting projects in C:\Source on Windows and ~/dev on Unix or Linux. For Windows users, now
is a good time set your HOME environment variable to C:\Source. The easiest way to get started with Equinox
is to extract it to your preferred “source” location, cd into the equinox directory and run ant new -
Dapp.name=myusers from the command line.
NOTE
I use Cygwin (www.cygwin.org) on Windows, which allows me to type forward-slashes, just like Unix/Linux.
Because of this, all the paths I present in this book will have forward slashes. Please adjust for your environment
accordingly (that is, use backslashes (\) for Windows’ command prompt).
At this point, you should have the following directory structure for the MyUsers webapp:
Spring Live
Create Project Directories and an Ant Build File 18
Equinox contains a simple but powerful build.xml file to deploy, compile and test using Ant. For all the ant
targets available, type “ant” in the MyUsers directory. The return should look like the following:
Equinox supports Tomcat’s Ant tasks. These tasks are already integrated into Equinox, but showing you how
they were integrated will help you understand how they work.
Tomcat ships with a number of Ant tasks that allow you to install, remove and reload webapps using its Manager
application. The easiest way to declare and use these tasks is to create a properties file that contains all the
definitions. In Equinox, a tomcatTasks.properties file is in the base directory with the following contents:
deploy=org.apache.catalina.ant.DeployTask
undeploy=org.apache.catalina.ant.UndeployTask
remove=org.apache.catalina.ant.RemoveTask
reload=org.apache.catalina.ant.ReloadTask
start=org.apache.catalina.ant.StartTask
stop=org.apache.catalina.ant.StopTask
list=org.apache.catalina.ant.ListTask
A number of targets are in build.xml for installing, removing and reloading the application:
Spring Live
Create Project Directories and an Ant Build File 19
Spring Live
Create Project Directories and an Ant Build File 20
In the targets listed above, several ${tomcat.*} variables need to be defined. These are in the build.properties
file in the base directory. By default, they are defined as follows:
To make sure the “admin” user is able to access the Manager application, open the $CATALINA_HOME/conf/
tomcat-users.xml file and verify that the following line exists. If it does not exist, you must create it. Note that the
“roles” attribute may contain a comma-delimited list of roles.
To test these changes, save all your files and start Tomcat. Then navigate to the “myusers” directory from the
command line and try running “ant list.” You should see a list of currently running applications on your Tomcat
server.
Spring Live
Create Project Directories and an Ant Build File 21
Now you can install MyUsers by running ant deploy. Open your browser and go to http://localhost:8080/
myusers. The “Welcome to Equinox” screen displays, as shown in Figure 2.4:
WARNING
In order for the in-memory HSQLDB to work correctly with MyUsers, start Tomcat from the same directory from
which you run Ant. Type “$CATALINA_HOME/bin/startup.sh” on Unix/Linux and
“%CATALINA_HOME%\bin\startup.bat” on Windows. You can also change the database settings to use an
absolute path.
In the next few sections, you will develop a User object and a Hibernate DAO to persist that object. You will use
Spring to manage the DAO and its dependencies. Lastly, you will write a business service to use AOP and
declarative transactions.
Spring Live
Create Unit Test for Persistence Layer 22
In the MyUsers app, you will use Hibernate for your persistence layer. Hibernate is an Object/Relational (O/R)
framework that relates Java Objects to database tables. It allows you to very easily perform CRUD (Create,
Retrieve, Update, Delete) on these objects. Spring makes working with Hibernate even easier. Switching from
Hibernate to Spring+Hibernate reduces code by about 75%. This code reduction is sponsored by the removal of
the ServiceLocator class, a couple of DAOFactory classes, and using Spring’s runtime exceptions instead of
Hibernate’s checked exceptions.
Writing a unit test will help you formulate your UserDAO interface. To create a JUnit test for your UserDAO,
complete the steps below:
1. Create a UserDAOTest.java class in the test/org/appfuse/dao directory. This class should extend
BaseDAOTestCase, which already exists in this package. This parent class initializes Spring's
ApplicationContext from the web/WEB-INF/applicationContext.xml file. Below is the code you
will need for a minimal JUnit test:
package org.appfuse.dao;
This class won’t compile yet because you haven’t created your UserDAO interface. Before you do
that, write a couple of tests to verify CRUD works on the User object.
Spring Live
Create Unit Test for Persistence Layer 23
2. Add the testSave and testAddAndRemove methods to the UserDAOTest class, as shown below:
dao.saveUser(user);
assertNotNull("primary key assigned", user.getId());
log.info(user);
assertNotNull(user.getFirstName());
}
dao.saveUser(user);
assertNotNull(user.getID());
assertEquals(user.getFirstName(), "Bill");
if (log.isDebugEnabled()) {
log.debug("removing user...");
}
dao.removeUser(user.getId());
assertNull(dao.getUser(user.getId()));
}
From these test methods, you can see that you need to create a UserDAO with the following methods:
• saveUser(User)
• removeUser(Long)
• getUser(Long)
Spring Live
Create Unit Test for Persistence Layer 24
3. Create a UserDAO.java file in the src/org/appfuse/dao directory and populate it with the code
below:
NOTE
If you are using an IDE like Eclipse or IDEA, a “lightbulb” icon will appear to the left of a non-existent class and
allow you to create it on-the-fly.
package org.appfuse.dao;
Finally, in order for the UserDAOTest and UserDAO to compile, create a User object to persists.
4. Create a User.java class in the src/org/appfuse/model directory and add “id,” “firstName” and
“lastName” as member variables, as shown below:
package org.appfuse.model;
public class User extends BaseObject {
private Long id;
private String firstName;
private String lastName;
/*
Generate your getters and setters using your favorite IDE:
In Eclipse:
Right-click -> Source -> Generate Getters and Setters
*/
}
Notice that you’re extending a BaseObject class. It has the following useful methods:
toString(), equals() and hashCode(). The latter two are required by Hibernate.
NOTE
In a real-world application, these methods should not be in a parent class, but rather in each subclass. Making
them abstract in BaseObject can help to enforce this rule. Commonclipse is an Eclipse plugin that can generate
these methods for you.
After creating the User object, open the UserDAO anda UserDAOTest classes and organize imports
with your IDE.
Spring Live
Configure Hibernate and Spring 25
Now that you have the Plain Old Java Object (POJO), create a mapping file so Hibernate can persist it.
1. In the src/org/appfuse/model directory, create a file named User.hbm.xml with the following
contents:
<hibernate-mapping>
<class name="org.appfuse.model.User" table="app_user">
</class>
</hibernate-mapping>
2. Add this mapping to Spring’s applicationContext.xml file in the web/WEB-INF directory. Open this
file and look for <property name="mappingResources"> and change it to the following:
<property name="mappingResources">
<list>
<value>org/appfuse/model/User.hbm.xml</value>
</list>
</property>
In the applicationContext.xml file, you can see how the database is set up and Hibernate is configured
to work with Spring. Equinox is designed to work with an HSQL database named “db/appfuse.” It
will be created in your Ant “db” directory. Details of this configuration will be covered in the “How
Spring Is Configured in Equinox” section.
Spring Live
Configure Hibernate and Spring 26
3. Run ant deploy reload (with Tomcat running) and see the database tables being creating as part
of Tomcat’s console log:
TIP
If you’d like to see more (or less) logging, change the log4j settings in the web/WEB-INF/classes/log4j.xml file
4. To verify that the “app_user” table was actually created in the database, run ant browse to bring up
a HSQL console. You should see the HSQL Database Manager as shown below:
Spring Live
Configure Hibernate and Spring 27
It is very easy to configure any J2EE-based web application to use Spring. At the very least, you can simply add
Spring’s ContextLoaderListener to your web.xml file:
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
This is a ServletContextListener that initializes when your webapp starts up. By default, it looks for
Spring’s configuration file at WEB-INF/applicationContext.xml. You can change this default value by specifying
a <context-param> element named “contextConfigLocation.” An example is provided below:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/sampleContext.xml</param-value>
</context-param>
The <param-value> element can contain a space or comma-delimited set of paths. In Equinox, Spring is
configured using this Listener and its default “contextConfigLocation.”
So, how does Spring know about Hibernate? This is the beauty of Spring: it makes it very simple to bind
dependencies together. Look at the full contents of your applicationContext.xml file:
Spring Live
Configure Hibernate and Spring 28
<beans>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:db/appfuse</value>
</property>
<property name="username"><value>sa</value></property>
<!-- Make sure <value> tags are on same line - if they’re not,
authentication will fail -->
<property name="password"><value></value></property>
</bean>
Spring Live
Configure Hibernate and Spring 29
The first bean (dataSource) represents an HSQL database, and the second bean (sessionFactory) has a
dependency on that bean. Spring just calls setDataSource(DataSource) on the
LocalSessionFactoryBean to make this work. If you wanted to use a JNDI DataSource instead, you could
easily change this bean’s definition to something similar to the following:
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/appfuse</value>
</property>
</bean>
Also note the “hibernate.hbm2ddl.auto” property in the “sessionFactory” definition. This property creates the
database tables automatically when the application starts. Other possible values are update and create-drop.
The last bean configured is the “transactionManager” (and nothing is stopping you from using a JTA transaction
manager), which is necessary to perform distributed transactions across two databases. If you want to use a JTA
transaction manager, simply change this bean’s “class” attribute to
org.springframework.transaction.jta.JtaTransactionManager.
Spring Live
Implement UserDAO with Hibernate 30
package org.appfuse.dao.hibernate;
if (log.isDebugEnabled()) {
log.debug(“userId set to: “ + user.getID());
}
}
Spring’s HibernateDaoSupport class is a convenient super class for Hibernate DAOs. It has
handy methods you can call to get a Hibernate Session, or a SessionFactory. The most
convenient method is getHibernateTemplate(), which returns a HibernateTemplate. This
template wraps Hibernate checked exceptions with runtime exceptions, allowing your DAO
interfaces to be Hibernate exception-free.
Nothing is in your application to bind UserDAO to UserDAOHibernate, so you must create that relationship.
Spring Live
Implement UserDAO with Hibernate 31
<bean id="userDAO"
class="org.appfuse.dao.hibernate.UserDAOHibernate">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
Spring Live
Run Unit Test and Verify CRUD with DAO 32
Before you run this first test, tune down your default logging from informational messages to warnings.
1. Change <level value="INFO"/> to <level value="WARN"/> in the log4j.xml file (in web/
WEB-INF/classes).
2. Run UserDAOTest using ant test. If this wasn’t your only test, you could use ant test -
Dtestcase=UserDAO to isolate which tests are run. After running this, your console should have a
couple of log messages from your tests, as shown below:
Spring Live
Create Manager and Declare Transactions 33
A recommended practice in J2EE development is to keep your layers separated. That is to say, the data layer
(DAOs) shouldn’t be bound to the web layer (servlets). Using Spring, it’s easy to separate them, but it’s useful to
further separate these tiers with a business service class.
• Most presentation tier components execute a unit of business logic. It’s best to put this logic in a
non-web class so a web-service or rich platform client can use the same API as a servlet.
• Most business logic can take place in one method, possibly using more than one DAO. Using a
business service class allows you to use Spring’s declarative transactions feature at a higher
“business logic” level.
The UserManager interface in the MyUsers application has the same methods as the UserDAO. The main
difference is the Manager is more web-friendly; it accepts Strings where the UserDAO accepts Longs, and it
returns a User object in the saveUser() method. This is convenient after inserting a new user (for example, to
get its primary key). The Manager (or business service) is also a good place to put any business logic that your
application requires.
Spring Live
Create Manager and Declare Transactions 34
package org.appfuse.service;
In the setUp() method above, you are loading your applicationContext.xml file into the
ApplicationContext variable using ClassPathXmlApplicationContext. Several methods
are available for loading the ApplicationContext: from the classpath, the file system or within a
web application. These methods will be covered in the Chapter 3: The BeanFactory and How It
Works.
Spring Live
Create Manager and Declare Transactions 35
2. Code the first test method to verify that adding and removing a User object with the UserManager
completes successfully:
user = mgr.saveUser(user);
assertNotNull(user.getId());
if (log.isDebugEnabled()) {
log.debug("removing user...");
}
user = mgr.getUser(userId);
assertNull("User object found in database", user);
}
This test is really an integration test rather than a unit test because it uses all the real components it
depends on. To be more like a unit test, you would use EasyMock or a similar tool to “fake” the DAO.
Using this, you could even get away from loading Spring’s ApplicationContext and depending
on any of Spring’s APIs. I recommend the test we created because it tests all the internals that our
project depends on (Spring, Hibernate, our classes), including the database. Chapter 8 discusses
refactoring the UserManagerTest to use mocks for its DAO dependency.
package org.appfuse.service;
Spring Live
Create Manager and Declare Transactions 36
package org.appfuse.service.impl;
if (user == null) {
log.warn("UserId '" + userId + "' not found in database.");
}
return user;
}
return user;
}
This class has no indication that you’re using Hibernate. This is important if you ever want to switch
your persistence layer to use a different technology.
This class has a private dao member variable, as well as a setUserDAO() method. This allows
Spring to perform its “dependency binding” magic and wire the objects together. Later, when you
refactor this class to use a mock for its DAO, you’ll need to add the setUserDAO() method to the
UserManager interface.
Spring Live
Create Manager and Declare Transactions 37
<bean id="userManager"
class="org.appfuse.service.UserManagerImpl">
<property name="userDAO"><ref local="userDAO"/></property>
</bean>
The only problem with this is you’re not leveraging Spring’s AOP and, specifically, declarative
transactions.
<bean id="userManager"
class="org.springframework.transaction.interceptor.TransactionProxy
FactoryBean">
<property name="transactionManager">
<ref local="transactionManager"/>
</property>
<property name="target">
<ref local="userManagerTarget"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
You can see from this XML fragment that the TransactionProxyFactoryBean must have a
“transactionManager” property set, and “transactionAttributes” defined.
Spring Live
Create Manager and Declare Transactions 38
7. Tell this Transaction Proxy the object you’re mimicking: userManagerTarget. As part of this new
bean, change the old “userManager” bean to have an id of “userManagerTarget.”
<bean id="userManagerTarget"
class="org.appfuse.service.impl.UserManagerImpl">
<property name="userDAO"><ref local="userDAO"/></property>
</bean>
8. If you’d like to see the transactions execute and commit, add the XML below to the log4j.xml file:
<logger name="org.springframework.transaction">
<level value="DEBUG"/> <!-- INFO does nothing -->
</logger>
Running the test again will give you a plethora of Spring log messages as it binds objects, creates
transactions, and then commits them. You’ll probably want to remove the above logger after running
the test.
Congratulations! You’ve just implemented a Spring/Hibernate solution for the backend of a web application.
You’ve also configured a business service class to use AOP and declarative transactions. This is no small feat;
give yourself a pat on the back!
Spring Live
Create Unit Test for Struts Action 39
The business service and DAO are now functional, so let’s slap an MVC framework on top of this sucker! Whoa,
there – not just yet. You can do the C (Controller), but not the V (View). Continue your Test-Driven
Development path by creating a Struts Action for managing users.
The Equinox application is configured for Struts. Configuring Struts requires putting some settings in web.xml
and defining a struts-config.xml file in the web/WEB-INF directory. Since there is a large audience of Struts
developers, this chapter deals with Struts way first. Chapter 4 deals with the Spring way. If you’d prefer to skip
this section and learn the Spring MVC way, please refer to Chapter 4: Spring’s MVC Framework.
To develop your first Struts Action unit test, create a UserActionTest.java class in test/org/appfuse/web.
This class extends MockStrutsTestCase and contains the following code:
package org.appfuse.web;
Spring Live
Create Action and Model (DynaActionForm) for Web Layer 40
package org.appfuse.web;
return mapping.findForward("success");
}
}
2. To configure Struts so that the “/user” request path means something, add an action-mapping to web/
WEB-INF/struts-config.xml. Open this file and add the following as an action-mapping:
3. Execute ant test -Dtestcase=UserAction and you should get the lovely “BUILD
SUCCESSFUL” message.
Spring Live
Create Action and Model (DynaActionForm) for Web Layer 41
4. Add a form-bean definition to the struts-config.xml file (in the <form-beans> section). For the
Struts ActionForm, use a DynaActionForm, which is a JavaBean that gets created dynamically
from an XML definition.
<form-bean name="userForm"
type="org.apache.struts.action.DynaActionForm">
<form-property name="user" type="org.appfuse.model.User"/>
</form-bean>
You’re using this instead of a concrete ActionForm because you only need a thin wrapper around
the User object. Ideally, you could use the User object, but you’d lose the ability to validate
properties and reset checkboxes in a Struts environment. Later, I’ll show you how Spring makes this
easier and allows you to use the User object in your web tier.
5. Modify your <action> definition to use this form and put it in the request:
6. Modify your UserActionTest to test the different CRUD methods in your Action, as shown below:
Spring Live
Create Action and Model (DynaActionForm) for Web Layer 42
verifyForward("edit");
verifyNoActionErrors();
}
7. Modify the UserAction so your tests will pass and it can handle CRUD requests. The easiest way to
do this is to write edit, save and delete methods. Be sure to remove the existing “execute” method
first. Below is the modified UserAction.java:
Spring Live
Create Action and Model (DynaActionForm) for Web Layer 43
mgr.removeUser(request.getParameter("user.id"));
saveMessages(request, messages);
if (user == null) {
ActionMessages errors = new ActionMessages();
errors.add(ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("user.missing"));
saveErrors(request, errors);
return mapping.findForward("list");
}
userForm.set("user", user);
}
return mapping.findForward("edit");
}
Spring Live
Create Action and Model (DynaActionForm) for Web Layer 44
request.setAttribute("users", mgr.getUsers());
return mapping.findForward("list");
}
Spring Live
Create Action and Model (DynaActionForm) for Web Layer 45
Now that you’ve modified this class for CRUD, perform the following steps:
8. Modify struts-config.xml to use the ContextLoaderPlugin and configure Spring to set the
UserManager. To configure the ContextLoaderPlugin, simply add the following to your struts-
config.xml file:
<plug-in
className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/applicationContext.xml,
/WEB-INF/action-servlet.xml"/>
</plug-in>
This plug-in will load the action-servlet.xml file by default. Since you want your Test Actions to
know about your Managers, you must configure the plug-in to load applicationContext.xml as well.
9. For each action that uses Spring, define the action mapping to
type="org.springframework.web.struts.DelegatingActionProxy" and declare a
matching Spring bean for the actual Struts action. Therefore, modify your action mapping to use this
new class.
In order for the DispatchAction to work, add parameter="method" to the mapping. This
indicates (in a URL or hidden field) which method should be called. At the same time, add forwards
for the “edit” and “list” forwards that are referenced in your CRUD-enabled UserAction class:
<action path="/user"
type="org.springframework.web.struts.DelegatingActionProxy"
name="userForm" scope="request" parameter="method">
<forward name="list" path="/userList.jsp"/>
<forward name="edit" path="/userForm.jsp"/>
</action>
Be sure to create the userList.jsp and userForm.jsp files in the “web” directory of MyUsers. You
don’t need to put anything in them at this time.
11. As part of this plug-in, configure Spring to recognize the “/user” bean and to set the UserManager
on it. Add the following bean definition to web/WEB-INF/action-servlet.xml:
Spring Live
Create Action and Model (DynaActionForm) for Web Layer 46
In this definition you’re using singleton="false". This creates new Actions for every request,
alleviating the need for thread-safe Actions. Since neither your Manager nor your DAO contain
member variables, this should work without this attribute (defaults to singleton=”true”).
In the UserAction class are a few references to success and error messages that will appear after
operations are performed. These references are keys to messages that should exist in the
ResourceBundle (or messages.properties file) for this application. Specifically, they are:
• user.saved
• user.missing
• user.deleted
Add these keys to the messages.properties file in web/WEB-INF/classes, as in the example below:
This file is loaded and made available to Struts via the <message-resources> element in struts-
config.xml:
<message-resources parameter="messages"/>
Spring Live
Run Unit Test and Verify CRUD with Action 47
Run the ant test -Dtestcase=UserAction. It should result in the following output:
Spring Live
Complete JSPs to Allow CRUD through a Web Browser 48
1. Add code to your JSPs (userForm.jsp and userList.jsp) so that they can render the results of your
actions. If you haven’t already done so, create a userList.jsp file in the web directory. Now add some
code so you can see the all the users in the database. In the code below, the first line includes a
taglibs.jsp file. This file contains all the JSP Tag Library declarations for this application, mostly for
Struts Tags, JSTL and SiteMesh (which is used to “pretty up” the JSPs).
<table class="list">
<thead>
<tr>
<th>User Id</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
<tbody>
<c:forEach var="user" items="${users}" varStatus="status">
<c:choose>
<c:when test="${status.count % 2 == 0}"><tr class="even"></c:when>
<c:otherwise><tr class="odd"></c:otherwise>
</c:choose>
<td><a href="user.do?method=edit&id=${user.id}">${user.id}</a></
td>
<td>${user.firstName}</td>
<td>${user.lastName}</td>
</tr>
</c:forEach>
</tbody>
</table>
You can see a row of headings (in the <thead>). JSTL’s <c:forEach> tag iterates through the
results and displays the users.
Spring Live
Complete JSPs to Allow CRUD through a Web Browser 49
2. Populate the database so you can see some actual users. You have a choice: you can do it by hand,
using ant browse, or you can add the following target to your build.xml file:
<target name="populate">
<echo message="Loading sample data..."/>
<sql driver="org.hsqldb.jdbcDriver"
url="jdbc:hsqldb:db/appfuse"
userid="sa" password="">
<classpath refid="classpath"/>
</sql>
</target>
Spring Live
Verify JSP’s Functionality through Your Browser 50
1. With this JSP and sample data in place, view this JSP in your browser. Run ant deploy reload,
then go to http://localhost:8080/myusers/user.do?method=list. The following screen displays:
2. This example doesn’t have an internationalized page title or column headings. Do this by adding
some keys to the messages.properties file in web/WEB-INF/classes.
user.id=User Id
user.firstName=First Name
user.lastName=Last Name
<thead>
<tr>
<th><bean:message key=”user.id”/></th>
<th><bean:message key=”user.firstName”/></th>
<th><bean:message key=”user.lastName”/></th>
</tr>
</thead>
Spring Live
Verify JSP’s Functionality through Your Browser 51
Note that JSTL’s <fmt:message key=”...”> tag could also be used. If you wanted to add sorting
and paging to this table, use the Display Tag (http://displaytag.sf.net). Below is an example of using
this JSP tag:
Please refer to the display tag’s documentation for internationalization of column headings.
Spring Live
Verify JSP’s Functionality through Your Browser 52
3. Now that you’ve created your list, create the form where you can add/edit data. If you haven’t already
done so, create a userForm.jsp file in the web directory of MyUsers. Below is the code to add to this
JSP to allow data entry:
onclick="this.form.method.value='delete'">
Delete</html:submit>
</c:if>
</td>
</tr>
</table>
</html:form>
NOTE
If you’re developing an application with internationalization (i18n), replace the informational message (at the top)
and the button labels with <bean:message> or <fmt:message> tags. This is a good exercise for you. For
informational messages, I recommend key names like pageName.message (such as, “userForm.message”), and
button names like button.name (such as “button.save”).
4. Run ant deploy and perform CRUD on a user from your browser.
The last thing that most webapps need is validation. In the next section, you’ll configure Struts’ Validator to
make the user’s last name a required field.
Spring Live
Adding Validation Using Commons Validator 53
4. Configure validation for the save() method, but not for others.
Configure the Validator plug-in by adding the following XML fragment to your struts-config.xml file (right after
the Spring plug-in):
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property
property="pathnames" value="/WEB-INF/validator-rules.xml,
/WEB-INF/validation.xml"/>
</plug-in>
From this you can see that the Validator is going to look for two files in the WEB-INF directory: validator-
rules.xml and validation.xml. The first file, validator-rules.xml, is a standard file that’s distributed as part of
Struts. It defines all the available validators, as well as their client-side JavaScript functions. The second file,
validation.xml, contains the validation rules for each form.
Spring Live
Adding Validation Using Commons Validator 54
The validation.xml file has a number of standard elements to match its Document Type Definition (DTD), but
you only need the <form> and <field> elements you see below. Please refer to the Validator’s documentation
for more information. Add the following <formset> between the <form-validation> tags in web/WEB-
INF/validation.xml:
<formset>
<form name="userForm">
<field property="user.lastName" depends="required">
<arg0 key="user.lastName"/>
</field>
</form>
</formset>
<form-bean name="userForm"
type="org.apache.struts.validator.DynaValidatorForm">
...
Spring Live
Adding Validation Using Commons Validator 55
One unfortunate side effect of using Struts’ DispatchAction is that validation is turned on at the mapping
level. In order to turn validation off for the list and edit screen, you could create a separate mapping with
validate=”false”. For example, AppFuse’s UserAction has two mappings: “/editUser” and “/saveUser”.
However, there’s an easier way that requires less XML, and only slightly more Java.
2. In UserAction.java, modify the save() method to call form.validate() and return to the edit
screen if any errors are found.
if (log.isDebugEnabled()) {
log.debug("entering 'save' method...");
}
When working with DispatchAction, this is cleaner than having two mappings with one measly
attribute changed. However, the two mappings approach has some advantages:
• It allows you to specify an “input” attribute that indicates where to go when validation fails.
• You can declare a “roles” attribute on your mapping to specify who can access that mapping.
For instance, anyone can see the “edit” screen, but only administrators can save it.
Spring Live
Adding Validation Using Commons Validator 56
3. Run ant deploy reload and try to add a new user without a last name. You will see a validation
error indicating that last name is a required field, as in the example below:
4. To enable this quickly, add an “onsubmit” attribute to the <form> tag (in web/userForm.jsp), and a
<html:javascript> tag at the bottom of the form.
<html:javascript formName="userForm"/>
Now if you run ant deploy and try to save a user with a blank last name, you will get a JavaScript
alert stating that “Last Name is required.” The one issue with the short-form of the
<html:javascript> tag is that it puts all of the Validator’s JavaScript functions into your page.
There is a better way: include the JavaScript from an outside page (which is itself generated). How to
do this will be covered in Chapter 5.
Congratulations! You’ve just developed a webapp that talks to a database, implements validation and even
displays success and error messages. In Chapter 4, you will convert this application to use Spring’s MVC
framework. In Chapter 5, you will add exception handling, file uploading and e-mailing features. Chapter 6 will
explore alternatives to JSP, and in Chapter 7 you’ll add alternative DAO implementations using iBATIS, JDO
and Spring’s JDBC.
Spring Live
Summary 57
Summary
Spring is a great framework for reducing the amount of code you have to write. If you look at the number of steps
in this tutorial, most of them involved setting up or writing code for Struts. Spring made the DAO and Manager
implementations easy. It also reduced most Hibernate calls to one line and allowed you to remove any Exception
handling that can sometimes be tedious. In fact, most of the time I spent writing this chapter (and the MyUsers
app) involved configuring Struts.
I have two reasons for writing this chapter with Struts as the MVC Framework. The first is because I think that’s
the framework most folks are familiar with, and it’s easier to explain a Struts-to-Spring migration than a JSP/
Servlet-to-Spring migration. Secondly, I wanted to show you how writing your MVC layer with Struts can be a
bit cumbersome. In Chapter 4, you’ll refactor the web layer to use Spring’s MVC Framework. I think you’ll find
it a bit refreshing to see how much easier and more intuitive it is.
Spring Live
Chapter
3
The BeanFactory represents the heart of Spring, so it’s important to know how it works. This chapter explains
how bean definitions are written, their properties, dependencies, and autowiring. It also explains the logic
behind making singleton beans versus prototypes. Then it delves into Inversion of Control, how it works, and the
simplicity it brings. This chapter dissects the Lifecyle of a bean in the BeanFactory to show how it works. This
chapter also inspects the applicationContext.xml file for the MyUsers application created in Chapter 2.
Spring is an excellent tool for integrating Inversion of Control (IoC) with your application. It uses a
BeanFactory to manage and configure beans. In most cases, however, you won’t interact with the
BeanFactory; you will use the ApplicationContext, which adds more enterprise-level, J2EE functionality, such
as internationalization (i18n), custom converters (for converting Strings to Object types), and event publication/
notification. This chapter explains how the BeanFactory works, IoC as it relates to the BeanFactory, how to
configure beans for the BeanFactory, and how to use the ApplicationContext.
Spring Live
About the BeanFactory 59
The BeanFactory is an internal interface that configures and manages virtually any Java class. The
XMLBeanFactory reads bean definitions from an XML file, while the ListableBeansFactory reads
definitions from properties files. When the BeanFactory is created, Spring validates each bean’s configuration.
However, the properties of each bean are not set until the bean is created. Singleton beans are instantiated by the
BeanFactory at startup time, while other beans are created on demand. According to the BeanFactory’s Javadocs,
“There are no constraints on how the definitions could be stored: LDAP, RDBMS, XML, properties file, etc.” At
the time of this writing, only XML and properties file implementations exist. Since the XMLBeanFactory is the
most commonly used method to configure J2EE applications, this chapter uses XML for all examples.
NOTE
For reading bean definitions from properties files, you can use PropertiesBeanDefinitionReader. For
more information on using this class, see R. J. Lorimer’s article on Alternative Spring Bean Mappings.
The BeanFactory is a workhorse that initializes beans and calls their lifecycle methods. It should be noted that
most lifecycle methods only apply to singleton beans. Spring cannot manage prototype (non-singleton)
lifecycles. This is because, after they’re created, prototypes are handed off to the client and the container loses
track of it. For prototypes, Spring is really just a replacement for the “new” operator.
Spring Live
A Bean’s Lifecycle in the BeanFactory 60
Figure 3.1 illustrates a bean’s lifecycle. An outside force controls the bean, which is the Inversion of Control
container. The IoC container defines the rules by which the bean operates. The rules are bean definitions. The
bean is pre-initialized through its dependencies. The bean then enters the ready state where the beans are ready to
go to work for the application. Finally, the IoC container destroys the bean.
Spring Live
A Bean’s Lifecycle in the BeanFactory 61
Inversion of Control
Inversion of Control is a powerful concept in application development. One form of IoC is Dependency
Injection, as described by Martin Fowler. Dependency Injection uses the Hollywood Principle: “Don’t call me,
I’ll call you.” In other words, your classes don’t look up or instantiate the classes they depend on. The control is
inverted and some form of container sets the dependencies. Using IoC often leads to much cleaner code and
provides an excellent way to de-couple dependent classes. Dependency Injection exists in three forms:
• Setter-based: Classes are typically JavaBeans, with a no-arg constructor, and with setters for the
IoC container to use when wiring dependencies. This is the variant recommended by Spring.
While Spring supports constructor-based injection, a large number of constructor arguments can
be difficult to manage.
• Constructor-based: Classes contain constructors with a number of arguments. The IoC container
discovers and invokes the constructor based on the number of arguments and their object types.
This approach guarantees that a bean is not created in an invalid state.
• Getter-based (or method injection): This is similar to setter-based, except you add a getter to
your class. The IoC container overrides this method when it runs inside, but you can easily use the
getter you specify when testing. This approach has only recently been discussed; more
information is available on TheServerSide.
Below is an example of a class before it’s been made IoC-ready. It is a Struts Action called ListUsers that
depends on a UserDAO, which, in turn, requires a connection as part of its constructor.
DatabaseUtils.closeConnection(conn);
return mapping.findForward("success");
}
}
Spring Live
A Bean’s Lifecycle in the BeanFactory 62
return mapping.findForward("success");
}
}
The above class doesn’t contain any lookup code. This makes it clean and workable for testing. For instance, in
your test you can use a Mock Object for the DAO to eliminate any dependencies on your data tier. A good IoC
container allows you to wire a connection (or DataSource) to the UserDAO. With Spring you can choose to
wire connections in the data layer, or use a filter to open a connection per request.
Spring Live
A Bean’s Lifecycle in the BeanFactory 63
A bean definition, or <bean>, is really quite simple, as illustrated by the following example:
At the very least, a bean has an id (or name) attribute and a class attribute. You don’t have to set any properties
on the bean if you don’t want to, but that’s similar to invoking “new” on an object. Most bean definitions contain
some kind of property setting, unless they’re simply used to bind interfaces to implementations (emulating the
factory pattern).
The first required bean attribute is id. Like any good XML document, the bean definition’s allowed attributes and
child elements are dictated by a Document Type Definition (DTD). The DTD for Spring is appropriately named
spring-beans.dtd and is publicly available on the web. The id attribute of a bean is a real XML ID, which means
it must be unique throughout the XML document. You can only define one id per bean. To add aliases to a bean,
or to use illegal XML characters in a bean’s id, specify the name attribute. This attribute allows one or more bean
ids, each separated by a comma or semicolon. Using an id attribute is the prefered and recommended way to
configure beans.
The second required bean attribute is class. While Spring can manage practically any Java class, the most
common pattern is a default (empty) constructor with setters for dependent properties. The value specified in this
attribute must be a fully-qualified class name (package name + class name). Otherwise, you can use the parent
attribute. This can be useful if you want to duplicate a class and override its properties.
Spring Live
A Bean’s Lifecycle in the BeanFactory 64
The table below lists all the attributes that you can define in a <bean> element.
Frequency
Attribute Description of Use
id This XML ID element enables reference checking. To use a name High
that’s illegal as an XML ID (such as 1 or 2), use the optional name
attribute. If you specify neither an id nor a name, Spring assigns
the class name as the id.
name Use this attribute to create one or more aliases illegal as an id. Medium
Multiple aliases can be comma- or space-delimited. Use this
attribute if you use the ContextLoaderPlugin with Struts
and Spring manages your Actions.
class The class and parent attributes are interchangeable. A bean High
definition must specify the fully-qualified name of the class
(package name + class name), or the name of the parent bean.
parent Note: A child bean definition that references a parent can override Low
property values of the singleton attribute. It inherits all of the
parent’s other attributes, such as lazy-init and autowire.
singleton This attribute determines if the bean is a singleton (one shared Low
instance returned by all calls to getBean(id)), or a prototype
(independent instance resulting from each call to
getBean(id)). The default value is true.
lazy-init If true, the bean will be lazily initialized. If false, it will get Low
instantiated on startup by bean factories that perform eager
initialization of singletons.
autowire This attribute controls the autowiring of bean properties. If used, Low
Spring automatically figures out the dependencies using one of
the following modes:
no: (Default) You must define bean references in the XML file
using the <ref> element. Recommended to make
documentation more explicit.
Note: While this attribute reduces the size of your XML file, it
reduces the readability and self-documentation supplied by
declaring properties. For larger applications, autowiring is
discouraged because it removes the transparency and structure
from collaborating classes.
Spring Live
A Bean’s Lifecycle in the BeanFactory 65
Spring Live
A Bean’s Lifecycle in the BeanFactory 66
The BeanFactory could be compared to EJB’s lifecycle, except Spring is much simpler; you can wire up pretty
much any class, and you don’t need to extend or implement interfaces. With bare-bones EJBs, you’re required to
implement many lifecycle classes that you may never even use. With Spring-managed beans, you can manage
the lifecycle using init-method and destroy-method attributes, and you only need to implement interfaces if you
want to physically talk to the BeanFactory during the initialization process.
A bean property is a member variable of a class. For example, your bean might have a “maxSize” property and a
method to set it:
You can set the value of “maxSize” by using the following XML fragment on your bean’s definition:
<property name="maxSize"><value>1000</value></property>
A bean dependency is a property the bean depends on in order to operate. Dependencies refer to other classes,
rather than simple values. For instance, the “dataSource” property in the example below refers to another bean.
The “dataSource” is a dependency of the “sessionFactory” bean, whereas the “mappingResources” is just a
property with values.
In this example, the reference to the “dataSource” bean is set using a <ref> tag. Let’s take a closer look at this
tag.
Spring Live
A Bean’s Lifecycle in the BeanFactory 67
The <ref> tag that points to the dataSource uses a “local” attribute to point to the “dataSource” bean. In addition
to local, other options exist for pointing to dependent beans. The following list shows the available attributes for
the <ref> element:
• Bean: Finds the dependent bean in either the same XML file or another XML file that has been
loaded into the ApplicationContext.
• Local: Finds the dependent bean in the current XML file. This attribute is an XML IDREF so it
must exist or validation will fail.
• External: Finds the bean in another XML file and does not search the current XML file.
From this list, <ref bean="..."/> and <ref local="..."/> are most commonly used. Bean is the most
flexible option, allowing you to move beans between files, but local has the convenience of built-in XML
validation.
In addition to specifying values from String, you can also read values from a .properties file using the
PropertyPlaceHolderConfigurer class. Below is an example using a database.properties file in the
classpath.
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfig
urer">
<property name="location">
<value>database.properties</value>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${db.driverName}</value>
</property>
<property name="url"><value>${db.url}</value></property>
<property name="username"><value>${db.username}</value></property>
<property name="password"><value>${db.password}</value></property>
</bean>
You can use a special <null/> element to set a property to Java’s null value. An empty value, <value></
value>, will result in setting an empty String ("").
Spring Live
A Bean’s Lifecycle in the BeanFactory 68
To pre-initialize your beans is to prepare them to work for your application. You do this by configuring their
properties and dependencies. The following sections discuss the most critical steps of this process.
Autowiring
Even though autowiring is not recommended for larger applications as noted below, you may choose to use it for
your smaller ones. If you choose to autowire a bean, I recommend that you use autowire="byName", because
it’s good practice to keep the names of your setters and bean ids in synch.
For example, you could define the UserDAO from Chapter 2 with autowiring, and then you wouldn’t have to
specify the “sessionFactory” property.
If you use autowire="byType", Spring will look for a bean that is a Hibernate SessionFactory. The
problem with this approach is that you may have multiple session factories talking to two databases. With
byName, you can give those beans different names and label your setters appropriately. The constructor and
autodetect options will suffer from the same affliction, since they employ byType under the covers.
NOTE
Autowiring causes you lose the self-documenting features of the XML file. Without autowiring, you’ll know what a
bean’s dependencies are because they’re specified in XML. With autowiring, you might have to look at a class’s
Javadocs or source to figure it out. Also, though rare, the BeanFactory could autowire some dependencies that you
didn’t want set.
Dependency Checks
Defining a dependency-check attribute in your bean’s definition is useful when you want to ensure that all
properties are properly set on a bean. A properly structured bean has default values. Some properties may not be
needed in certain scenarios, limiting this feature’s usefulness. The default is “none,” meaning dependency
checking is not activated, but you can enable this on a per bean basis. A “simple” verifies primitive types and set
collections. An “object” value checks a bean’s dependencies (also called collaborators). The “all” value includes
both “simple” and “object.”
Spring Live
A Bean’s Lifecycle in the BeanFactory 69
setXXX()
The setXXX() methods are simply the setters that inject dependencies into a class. These properties are
configured in the context file and can be primitives (that is, int or boolean), object types (Long, Integer), null
values or references to other objects.
To demonstrate how each of these types is set, the Smorgasbord class below has all the previously mentioned
property types:
package org.appfuse.model;
Spring Live
A Bean’s Lifecycle in the BeanFactory 70
Rather than writing a Unit Test for this class example, use the dependency-check attribute in the bean’s
definition, as well as the init-method attribute to call its toString() method.
NOTE
For your professional applications, I strongly recommend always writing a Unit Test.
You can turn on informational logging for the org.springframework.beans package by adding the
following to web/WEB-INF/classes/log4j.xml:
<logger name="org.springframework.beans">
<level value="INFO"/>
</logger>
Spring Live
A Bean’s Lifecycle in the BeanFactory 71
Now if you run any tests or deploy and run MyUsers, you should see the following output in your console. This
example uses ant test –Dtestcase=UserDAO.
setBeanFactory()
After the initialization methods are called, the BeanFactory checks for classes implementing the
BeanFactoryAware and BeanNameAware interfaces. These interfaces provide a means for beans to find out
more information about where they came from and who they are. The BeanFactoryAware interface defines
one method:
If you implement this interface, it references to the BeanFactory, which you can use to look up other beans. It
basically documents a bean’s origins.
In order for a bean to discover its “id”, use the BeanNameAware interface. This interface has a single method:
Spring Live
A Bean’s Lifecycle in the BeanFactory 72
In most cases, you won’t need access to the BeanFactory because you can talk to other beans by wiring them as
dependencies (using <ref bean=”...”/>). Accessing the bean’s name may be helpful if you want to
configure the same class as two different beans with different dependency implementations. Using this, you can
perform conditional logic based on which “name” is configured.
After calling methods from the BeanFactoryAware and BeanNameAware interfaces, beans enter into a ready
state. This is when your application has completed starting up (in Tomcat, for example).
Spring Live
A Bean’s Lifecycle in the BeanFactory 73
afterPropertiesSet()
You can configure your beans for post-initialization processing using one of two approaches: 1) use the “init-
method” attribute as illustrated in the example above, or 2) implement InitializingBean and its
afterPropertiesSet() method. (The diagram shows both methods, but only one is required.) Clearly, using
“init-method” is a much cleaner and simpler way to do this. However, implementing InitializingBean can
be helpful for testing when you’re not using Spring to manage your beans. For instance, you can call this method
in your tests and verify that your mock objects have been set correctly. It’s also useful for guaranteeing that your
bean will be configured correctly; you’re not depending on someone to write the bean’s definition correctly.
The previous example injected property values into the Smorgasbord class. It set primitive values, object
values and even a reference (dataSource) to another bean in the factory. Not only does Spring’s DTD support
simple <value> elements in properties, it also supports setting Properties, Lists and Maps. Below is an example
(from Spring’s documentation) of using these more complex properties:
init-method
The init-method attribute of a bean definition calls a method after all the properties of a bean have been set. This
has the same functionality as implementing the InitializingBean interface, except that it doesn’t tie your
bean to Spring.
Spring Live
A Bean’s Lifecycle in the BeanFactory 74
Ready State
After your beans have been pre-initialized and any setup methods have been called, they enter into a ready state.
The ready state means that your application can get these beans and use them as needed. The entire lifecycle to
enter the ready state is very quick. It increases based on the number of beans in your app. However, it only occurs
at startup.
Destroying Beans
When you shut down (or reload) your application, beans that are singletons once again get lifecycle methods
called on them. First, beans that implement the DisposableBean interface will have their destroy() methods
called. Next, beans with a “destroy-method” specified in their bean definitions will have that method invoked.
Spring Live
The ApplicationContext: Talking to Your Beans 75
Understanding the BeanFactory is important when developing with Spring, but you probably won’t need to
interface with it in your application. In most cases, you’ll use the ApplicationContext, which adds more
enterprise-level, J2EE functionality, such as internationalization (i18n), custom converters (for converting
Strings to Object types) and event publication/notification. An ApplicationContext is instantiated with bean
definition files and beans can be easily retrieved using context.getBean("beanId"). Instantiating the
ApplicationContext and loading the bean definition XML files is the hardest part, so let’s look at the different
ways to do this.
Spring gives you many options for loading its bean definitions. In the current release (1.0.2), bean definitions
must be loaded from files. You could also implement your own ApplicationContext and add support for loading
from other resources (such as a database). While many Contexts are available for loading beans, you’ll only need
a few, which are listed below. The others are internal classes that are used by the framework itself.
• ClassPathXmlApplicationContext: Loads context files from the classpath (that is, WEB-INF/
classes or WEB-INF/lib for JARs) in a web application. Initializes using a new
ClassPathXmlApplicationContext(path) where path is the path to the file. The path
argument can also be a String array of paths. This is a good context for using in unit tests.
• FileSystemXmlApplicationContext: Loads context files from the file system, which is nice for
testing. Initializes using a new FileSystemXmlApplicationContext (path) where path
is a relative or absolute path to the file. The path argument can also be a String array of paths.
XmlWebApplicationContext context =
new XmlWebApplicationContext();
context.setServletContext(ctx);
context.refresh();
Once you’ve obtained a reference to a context, you can get references to beans using
ctx.getBean("beanId"). You will need to cast it to a specific type, but that’s the easy part. Of the above
contexts, ClassPathXmlApplicationContext is the most flexible. It doesn’t care where the files are, as
long as they’re in the classpath. This allows you to move files around and simply change the classpath.
Spring Live
The ApplicationContext: Talking to Your Beans 76
Writing unit tests with Spring is generally pretty easy. Spring-ready beans can be instantiated and tested sans-
container with mocks put into the setters. Testing is also easy because of Spring’s interface-based design, which
allows you to choose how to test implementing classes. Testing with the least amount of setup can be achieved by
using a ClassPathXmlApplicationContext, getting a reference to your bean, and calling methods on it.
This method allows you to write your tests to interfaces, not to implementation classes. If you decide to swap out
implementations (by changing the “class” attribute of your bean), you don’t have to change anything in your test.
However, one issue with using this is the ApplicationContext can take a few seconds to initialize. As your
application grows, the time-to-initialize will increase. Furthermore, if you load the context in a setUp()
method, the context will be instantiated each time before a testXXX() method is called. Luckily, a couple of
simple solutions exist to load the context only once per TestCase. The first is to use JUnit’s TestSetup class in a
suite, or you can put the context-loading code in a static block of your test.
static {
ctx = new ClassPathXmlApplicationContext("/appContext.xml");
}
The second solution is to directly test the implementation classes, without ever using Spring’s BeanFactory or
ApplicationContext. This generally involves creating an instance with the “new” operator, setting its
dependencies manually, and invoking methods to test. Using this technique allows you to replace dependent
classes with mock objects, which can speed up your tests and isolate them from their environment dependency.
More information on unit testing will be given in Chapter 8: Testing Spring Applications.
Spring Live
The ApplicationContext: Talking to Your Beans 77
The ApplicationContext interface extends the MessageSource interface, which gives it messaging (i18n)
functionality. In conjunction with the HeirarchicalMessageSource, capable of hierarchical message
resolving, these are the basic interfaces Spring provides to resolve messages. When loading an
ApplicationContext, it searches for a bean with the name “messageSource” defined in its context. If no such bean
is found in the current or parent contexts, a StaticMessageSource will be created so that getMessage()
calls don’t fail.
Two MessageSource implementations exist in the current Spring code base. They are
ResourceBundleMessageSource (which reads from a .properties file) and StaticMessageSource (hardly
used, but allows for adding messages programmatically). Below is an example “messageSource” bean definition
that will load messages.properties from the classpath:
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessage
Source">
<property name="basename"><value>messages</value></property>
</bean>
If you want to specify multiple ResourceBundles, you can set the “basenames” property instead of the single-
value “basename” property.
<property name="basenames">
<list>
<value>messages</value>
<value>errors</value>
</list>
</property>
The next chapter defines a “messageSource” bean and interacts with it to get error and success messages.
Spring Live
The ApplicationContext: Talking to Your Beans 78
The ApplicationContext supports Event Handling via the ApplicationEvent class and
ApplicationListener interface. If you’d like to use this functionality, you can implement
ApplicationListener in a bean and when an ApplicationEvent is published to the context, your bean will be
notified. See the Spring Events table for the three standard events.
Event Description
ContextRefreshedEvent Event published when the ApplicationContext is initialized or refreshed.
Initialized here means that all beans are loaded, singletons are pre-
instantiated and the ApplicationContext is ready for use.
ContextClosedEvent Event published when the ApplicationContext is closed, using the close()
method on the ApplicationContext. Closed here means that singletons are
destroyed.
RequestHandledEvent A web-specific event telling all beans that a HTTP request has been
serviced (this will be published after the request has been finished). Note
that this event is only applicable for web applications using Spring's
DispatcherServlet.
You can also implement custom events by calling the publishEvent() method on the ApplicationContext. See
Spring’s Reference Documentation for an example.
In the MyUsers application from Chapter 2, you loaded your bean definitions from web/WEB-INF/
applicationContext.xml. In this file, several beans are defined. Of the seven beans defined in this file, only three
of them (userDAO, userManagerTarget and /user) refer to classes that you created. This really shows the power
of Spring: four of the classes you used (dataSource, sessionFactory, transactionManager and userManager) are
internal Spring classes upon which you set properties.
Now that you understand how a bean definition XML file is composed, I encourage you to take a closer look at
the applicationContext.xml file from MyUsers. I think you will notice that it’s rather simple and easy to
comprehend.
Spring Live
Summary 79
Summary
In this chapter, you learned about Inversion of Control, which was recently aliased as Dependency Injection by
Martin Fowler. Injecting dependencies using a container like Spring is a clean and powerful way to configure
applications and reduce coupling.
The BeanFactory and Bean Definitions are the driving force behind Spring’s IoC container, allowing you to
specify dependencies and control your class’s lifecycles in XML. Knowing how the BeanFactory works and how
bean definitions are specified will help you to become an extremely efficient developer with Spring. Knowing
how properties are set – whether they’re Strings, Objects, or references to other beans – is a tremendous asset.
You can also define more complex properties like Properties, Lists and Maps. This is where you will be wiring
up your entire application, rather than in code itself. Now, your code is loosely coupled, and can take care of its
concerns, and you can think of the ApplicationContext/BeanFactory as the container that couples everything
together.
In the next chapter, you will convert the MyUsers application from Chapter 2 to use Spring’s MVC framework. I
think you’ll be amazed at how simple web development with Spring is, once you’ve gotten the internals set up
and configured.
Spring Live
Chapter
4
This chapter describes the many features of Spring’s MVC framework. It shows you how to replace the Struts
layer in MyUsers with Spring. It covers the DispatcherServlet, various Controllers, Handler Mappings, View
Resolvers, Validation and Internationalization. It also briefly covers Spring’s JSP Tags.
Spring Live
81
Chapter 3 explored Spring’s BeanFactory and its lifecycle, which you can use to control how your beans are
invoked and used. Spring carries this concept into the web tier and has some pretty neat concepts in its MVC
Framework. In popular frameworks like Struts and WebWork, controllers usually contain a single method:
execute(). The framework, regardless of whether a GET or POST request is sent, will call this method. It’s up
to the developer to code any logic needed in this method; for example, you may populate drop downs, handle
validation errors and set up the view to add a new record. Of course, you can code multiple methods in a Struts or
WebWork Action and then dispatch to them based on request parameters or button names. But it doesn’t change
the fact that the method call doesn’t care which request method (GET vs. POST) is used.
Spring’s MVC is a bit friendlier. The simplest way to look at it is that it offers two controllers: a Controller
interface and a SimpleFormController class. The Controller is best suited for displaying read-only data
(such as list screens), while the SimpleFormController is designed to handle forms (such as edit, save,
delete). The Controller interface shown below is quite simple, containing a single
handleRequest(request, response) method.
package org.springframework.web.servlet.mvc;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
/**
* Process the request and return a ModelAndView object which the
* DispatcherServlet will render. A null return is not an error:
* It indicates that this object completed request processing
* itself, thus there is no ModelAndView to render.
*/
ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws Exception;
}
The handleRequest() method returns a ModelAndView class. This class holds both the model and the view,
which are both very distinct. The model is the information that you intend to display, and the view is the logical
name where you want to display it. The model can be a single object with a name, or it can be a
java.util.Map containing several objects. The view can be a View object (which is an interface for the
different view types) or it can be a String name that is determined by a ViewResolver. The rich set of Views
available in Spring is discussed meticulously in Chapter 6.
Spring Live
82
The SimpleFormController, on the other hand, is a concrete class with several methods that are invoked
while processing a data-entry form. is the reason one is an interface and the other super-class is primarily for
flexibility. The Controller interface is used by all Controllers in Spring, whereas the
SimpleFormController is an implementation with default settings for many of its methods. If you don’t need
all the rich functionality of a FormController, you can extend the AbstractCommandController to populate
your command beans from an HTTPServletRequest. Spring’s MVC has a deep hierarchy for its
FormControllers that is out of the scope of this chapter. In most cases, you simply won’t need them and
SimpleFormController will fulfill most requirements.
With SimpleFormController, certain methods are called on GET requests, while others are called on POST
requests. This corresponds with how most web applications work: a GET signifies an “edit,” while a POST
signifies a “save” or “delete.” This allows for easy isolation of the two operations. In Struts, you can achieve
similar functionality using a DispatchAction (or one of its subclasses). We used a DispatchAction in
Chapter 2 to separate the different CRUD operations into different methods. Spring’s approach is better; you can
re-use methods for all CRUD actions. This chapter discusses the different SimpleFormController methods
(and when they’re called) in Section in the Method Lifecyle Review towards the end.
This chapter covers only what you need to know to develop a simple webapp with validation, including the
following topics:
• Create Unit Test for UserFormController (edits, saves and deletes users)
Spring Live
83
As previously mentioned, Spring’s MVC framework is a bit different than traditional frameworks like Struts and
WebWork. With Spring, I tend to use two controllers for my master/detail screens. With Struts, I tend to use one
Action for deleting, editing, saving and listing rows in a database table. By “listing,” I mean the process of
getting all the rows from a particular table. This satisfies most of what I tend to do in web applications. With
Spring MVC, rather than having one Controller do all the work, it’s simpler to create a controller for the listing
(master) and another for the delete/edit/save (detail).
NOTE
If you don’t feel like creating a new Controller for every list screen, you could create a MultiActionController that
has separate methods for each list screen.
Chapter 11 will conduct a detailed analysis of Spring MVC versus the more popular MVC Frameworks
available: Struts, WebWork, Tapestry and JSF. It will explain the strengths and weaknesses of each, as well as
demonstrate how Spring’s middle tier can integrate with each of them.
Spring Live
Unit Testing Spring Controllers 84
When I first started working with Spring’s MVC Framework, I found it somewhat difficult to test. I was
surprised, because one of Spring’s advertised benefits is “Applications built using Spring are very easy to unit
test.”1 While it was easy to test Controllers (those classes that drive list screens), it was a bit more difficult to test
SimpleFormControllers. The main problem I had was that none of the recommended solutions (such as Mock
Objects) had APIs to handle the stuff you normally do in a webapp: setting request parameters/attributes,
grabbing stuff from application scope, etc. With Struts, you can use StrutsTestCase, which does a very nice job of
providing mock implementations of most Struts and Servlet API classes.
Because Spring is open-source, I was able to dig in and see what the developers were using internally to test the
Controllers. It turned out they had a number of home-grown Mocks, which covered most of the Servlet APIs that
I needed. Shortly after discovering that, the Spring team cleaned up these mocks for public consumption and
added them to the Spring distribution. You’ll be using these classes when you write your unit tests. If you’d like
to use similar mocks in your project, be sure to include spring-mock.jar in your classpath.
In this chapter, like previous ones, you can follow along and do the examples as you go. The easiest way to do
this is to download the MyUsers Chapter 4 bundle from http://sourcebeat.com/downloads. This download is
similar to the Equinox package you downloaded for Chapter 2. However, it has all Struts-related components
removed and is designed to be a pure Spring (with Hibernate) application. It contains all the JARs you will need
in this chapter in its web/WEB-INF/lib directory.
You can also use the application you developed in Chapter 2. If you go this route, download Chapter 4 JARs
from http://sourcebeat.com/downloads. The next section discusses how Spring is configured in the downloaded
bundle. It also shows what you need to modify if you're converting the Struts-based application from the last
chapter.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 85
Spring’s MVC framework is similar to Struts in that it uses a single instance of a controller by default. You can
change your Controllers to create new instances for every request as well, by adding singleton=”false” to your
controller’s bean definition. This way, if you prefer WebWork’s “new action per request,” you can still get that
functionality.
Spring MVC has a single servlet than handles all requests, similar to most Java web frameworks2. It’s called the
DispatcherServlet and is responsible for “dispatching” request to handlers, which have mappings to tell it
where to go next. In the Chapter 4 download, the DispatcherServlet is already configured in the web/WEB-INF/
web.xml file. Its mapping is set to "*.html" - which means that any URLs ending in ".html" will be handled by
this servlet.
If you're modifying the application created in Chapter 2, you’ll need configure the MyUsers application to use
the DispatcherServlet for its front controller, rather than Struts’ ActionServlet. Instructions for doing
this are in the section below.
At this point, you should have the “myusers” project setup on your hard drive. To begin, open web/WEB-INF/
web.xml and modify the “action” servlet’s <servlet-class> from:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
to:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Spring Live
Configure DispatcherServlet and ContextLoaderListener 86
In addition, change the action’s <servlet-mapping> from *.do to *.html. You’re serving up HTML, so it
makes sense to use this instead of .do. Also, there’s no point in advertising the web framework you’re using.
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
By default, the DispatcherServlet looks for an XML file named servlet-name-servlet.xml in the WEB-INF
directory. In this case, it'll find and load the action-servlet.xml once you create it. This file contains all of the web
controllers and settings used in MyUsers.
In Chapter 2, you used the Spring Plugin for Struts (ContextLoaderPlugin) to load the bean configuration
files. However, the ContextLoaderListener was also configured in your web.xml. This caused the
applicationContext.xml file to be loaded twice. This was so you could unit test your Action classes without
loading any context files manually.
NOTE
This Listener will only work with Servlet 2.3 containers, so if you’re on an older container, use the
ContextLoaderServlet.
Since the ContextLoaderListener is already configured in web.xml, no further configuration is needed on your
part. If you have more than one file with bean definitions, you must add a contextConfigLocation context
parameter to indicate the different files. For example, to do this in MyUsers, you would add the following XML
fragment to web.xml, directly after the “sitemesh” filter and before its <filter-mapping>.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext1.xml
/WEB-INF/applicationContext2.xml
</param-value>
</context-param>
NOTE
Notice that the paths to the two files are space-delimited. These paths can also be comma-delimited.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 87
Those are the basic steps to configure a Java webapp to use Spring’s MVC Framework. Here’s a review of the
steps:
1. In web.xml, add a <servlet> definition for DispatcherServlet and configure its <servlet-
mapping>.
2. If you have more than one context file, define a contextConfigLocation <context-param>
with the paths to your bean configuration files.
Remove UserAction and UserActionTest Struts classes, as well as a few Struts JARs in web/WEB-INF/lib.
Here are a couple commands to accomplish this quickly:
rm src/org/appfuse/web/UserAction.java
rm test/org/appfuse/web/UserActionTest.java
rm web/WEB-INF/lib/struts*
rm web/WEB-INF/struts-config.xml
Now remove the definition for the UserAction class from action-servlet.xml. Delete the following lines from
the file:
Download Chapter 4 JARs to your hard drive. Put the JAR files in the web/WEB-INF/lib directory. The
spring.jar file contains Spring 1.1.1 (Equinox 1.0 ships with Spring 1.0.2), the spring-mock.jar file contains
mocks for the Servlet API, and the spring-sandbox.jar file is used for its Commons Validator support. Another
file, validation-rule.xml is also contained in the download - put this file in the web/WEB-INF directory.
Configuring validation will be covered later in this chapter.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 88
To practice Test-Driven Development (TDD), start by writing a unit test for the UserController. This class
returns a list of all the users from a business service class (UserManager). If you’re not familiar with TDD,
here’s a good definition from Dave Thomas’s weblog:
“For me, test-driven development is an important way of thinking about coding. It’s about using tests
to gain perspective on your design and implementation. You listen to what the tests are telling you,
and alter to code accordingly. Finding it hard to test something in isolation? Refactor your code to
reduce coupling. Is it impossible to mock out a particular subsystem? Look at adding facades or
interfaces to make the separation cleaner. Tests drive the design, and tests verify the implementation.”
To create a JUnit Test for the Controller, create a UserControllerTest.java file in test/org/appfuse/web (you might
need to create this directory/package). This class should extend junit.framework.TestCase and have a
setUp() method defined to load the context files using an XmlWebApplicationContext. The main reason
for using this ContextLoader over a ClassPathXmlApplicationContext is so web-only beans can be
instantiated. By web-only beans, I mean those that require a WebApplicationContext to be present.
The above code will instantiate any beans defined in their respective XML files. Now write a test method in
order to test your Controller. This is where TDD comes into play. The test will drive the design. Write a test
method to retrieve a list of users, verify the success, and confirm the view returned is the one you expect. Below
is the entire UserControllerTest, so you can easily integrate it into your project. The testGetUsers() is
the method you’re most interested in.
package org.appfuse.web;
Spring Live
Configure DispatcherServlet and ContextLoaderListener 89
ctx.setConfigLocations(paths);
ctx.setServletContext(new MockServletContext(""));
ctx.refresh();
}
TIP
When writing tests for your own project, I recommend creating a BaseControllerTestCase that extends
TestCase and all your *ControllerTest classes extend. In this class’s setUp() method, you can load the
context for all child tests. If you do this, make sure to call super.setUp() in child classes' setUp() methods.
Stepping through the testGetUsers() method, you’re grabbing the UserController and invoking its
handleRequest() method – which returns a ModelAndView class. This method is common to all Spring
Controllers, so you’ll actually use this same method to test your FormControllers. The ModelAndView class is a
unique concept in web frameworks. It basically contains information about the next page (the view) and what
data to expose to it (the model). With Struts, the model and view are separated. The model is usually put into the
request (or session) scope, and Actions typically return ActionForwards, which are just fancy wrappers around
URLs.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 90
Now that you’ve written your unit test, it’s time to create the UserController class so you can get it to
compile. First, create a UserController.java file in src/org/appfuse/web (you may need to create this directory/
package). This class should implement the Controller interface and implement its
handleRequest(request, response) method. You’re also going to need to use the UserManager to talk
to get the list of users, so you’ll need to add a private UserManager variable and a setUserManager()
method for Spring’s IoC container to use. When you configure this Controller (a.k.a. bean) in the next section,
you’ll add the UserManager as a dependency. So far, you have the following class structure:
package org.appfuse.web;
Now implement the handleRequest() method to get a list of users and route the user to userList.jsp.
This method is quite simple; in fact, it would be only one line without the logging statement at the beginning!
Spring Live
Configure DispatcherServlet and ContextLoaderListener 91
Compiling the UserControllerTest class should work now, but if you try to run the test (using ant test -
Dtestcase=UserController) it will fail.
This error is saying that the web/WEB-INF/action-servlet.xml does not validate. This is because it has no beans
defined in it. To make the test pass, edit the action-servlet.xml file in the web/WEB-INF/ directory. The action-
servlet.xml file starts similar to any other Spring configuration file with the DTD at the top and the beginning
<beans> element.
Adding a "userController" bean definition should cause this file to look as follows:
<beans>
<bean id="userController" class="org.appfuse.web.UserController">
<property name="userManager">
<ref bean="userManager"/>
</property>
</bean>
</beans>
Spring Live
Configure DispatcherServlet and ContextLoaderListener 92
Now your UserControllerTest should run just fine. You can execute it by running ant test -
Dtestcase=UserController or run it as a JUnit Test in Eclipse or IDEA.
TIP
Instructions for setting up MyUsers in Eclipse and IDEA are available in this book's FAQ.
From the command line, the output should look similar to the following screenshot.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 93
Now that the UserController is working, you must configure Spring so it knows the “userList” view actually points to
the userList.jsp. The simplest way to do this is to use the InternalResourceViewResolver, which resolves
names to files. It allows you to add a prefix and a suffix, so you can easily control where your JSPs reside. Add the
following XML block to action-servlet.xml, after the “userController” bean definition.
In the above “viewResolver” definition, notice that you’re specifying a JstlView class for the “viewClass”
property. This is so you can use JSTL’s <fmt:message> tag, which requires a bit of preparation to use its i18n
features with Spring MVC. The prefix is “/” and the suffix is “.jsp”. If you need to move your JSPs to “/WEB-
INF/pages,” all you’ll need to change is the prefix.
NOTE
If you’re developing a production application, I highly recommend placing your JSPs under WEB-INF. If you’re
using a Servlet 2.3+ container, any files under WEB-INF will not be accessible from the browser. This can be very
useful during development because it enforces MVC and requires you to use a controller to access your views.
By adding “viewResolver” definition, the “userList” view name in UserController will be resolved to “/
userList.jsp”. You can use several other ViewResolvers depending on your view technology of choice. These will
be discussed in Chapter 6: View Options.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 94
Now configure URLs in the application so that the “/users.html” URL invokes the UserController class. To
do this, Spring MVC requires you to define a HandlerMapping bean and define which URLs go to which
controllers. In most cases, the SimpleUrlHandlerMapping is all you’ll need. It allows you to specify url-
patterns to bean names. In order to map “/users.html” to the “userController” bean, add the following to web/
WEB-INF/action-servlet.xml:
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.Simple
UrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/users.html">userController</prop>
</props>
</property>
</bean>
NOTE
You could also use BeanNameUrlHandlerMapping instead of the SimpleUrlHandlerMapping. This handler simply
looks for bean names (not ids) that match the URL. So if you gave UserController a name of “/users.html”, then this
hander would resolve it correctly. The BeanNameUrlHandlerMapping is the default handler if you don’t define one.
If you're modifying the application you created in Chapter 2, modify a couple of JSPs. The web/taglibs.jsp file
should contain the following:
Spring Live
Configure DispatcherServlet and ContextLoaderListener 95
1. Create a userList.jsp file in the web directory. This file may already exist.
2. Add code so you can see the all the users in the database. In the code below, the first line includes a
taglibs.jsp file. This file contains all the JSP Tag Library declarations for this application, mostly for
JSTL and SiteMesh (which is used to “pretty up” the JSPs).
<table class="list">
<thead>
<tr>
<th><fmt:message key="user.id"/></th>
<th><fmt:message key="user.firstName"/></th>
<th><fmt:message key="user.lastName"/></th>
</tr>
</thead>
<tbody>
<c:forEach var="user" items="${users}" varStatus="status">
<c:choose>
<c:when test="${status.count % 2 == 0}"><tr class="even"></c:when>
<c:otherwise><tr class="odd"></c:otherwise>
</c:choose>
<td><a href="editUser.html?id=${user.id}">${user.id}</a></td>
<td>${user.firstName}</td>
<td>${user.lastName}</td>
</tr>
</c:forEach>
</tbody>
</table>
3. To enable i18n message lookups (for the <fmt:message> tag), add a “messageSource” bean to action-
servlet.xml.
The “basename” property refers to “messages,” which means look for messages.properties at the root of the
classpath. If you’d like to use more than one .properties file, you can use the “basenames” property with a
<list> of <values>.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 96
NOTE
The ResourceBundleMessageSource depends on Java’s ResourceBundle, which caches loaded bundles
indefinitely. With this class, reloading a bundle during VM executing is not possible. If you need such functionality,
refer to ReloadableResourceBundleMessageSource.
To test that you’ve configured Spring’s handlers and resolvers correctly, as well as modified userList.jsp
successfully, start Tomcat, deploy MyUsers (ant deploy) and view http://localhost:8080/myusers/users.html in
your browser. To add a couple of users to the database, run ant populate.
NOTE
If running ant populate doesn't cause users to show up in the list, see this FAQ.
If your screen looks like the one above, congratulations! If not, send e-mail to the Equinox users’ mailing list
([email protected]) to get help.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 97
The list screen was the easy part since it simply retrieves and displays data. Now you must create a Controller
and JSP to handle editing database records. Since you are practicing TDD, start by creating a unit test for the
UserFormController (that you haven’t created yet). To do this, create a UserFormControllerTest.java file in
the test/org/appfuse/web directory. This file extends JUnit’s TestCase class and has the following setUp() and
tearDown() methods.
package org.appfuse.web;
Spring Live
Configure DispatcherServlet and ContextLoaderListener 98
The setUp() method in this class is very similar to the setUp() method in UserManagerTest from Chapter
2, except that it also loads action-servlet.xml. Separating your business components and data layer classes
between the two files allows you to easily switch out the MVC framework without even touching
applicationContext.xml. This is very powerful for decoupling your different tiers and allows for easy refactoring.
Now add a few methods to test your CRUD actions (edit, save and delete). The test methods below use Spring’s
Servlet API mocks to allow for easy testing of Controllers.
In the previous methods, you should probably review a couple of classes. The first is Spring’s
MockHttpServletRequest. This class makes it easy to call GET and POST methods on a given URI (Uniform
Resource Indicator). It has a number of constructors, each listed below.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 99
This is a very flexible class and is useful for testing Controllers. Originally, it was only used by Spring
developers internally for testing Controllers. As of the 1.0.2 release, it’s included in the spring-mock.jar.
The second class is the Errors interface. This is an interface that is implemented by an object to store and
expose information about data binding errors. In the UserFormController that you will create, you must
register a data binder to handle the java.lang.String java.lang.Long conversion for user.setId().
The UserFormControllerTest class will not compile until you create the UserFormController class. If
you’re using an IDE like IDEA or Eclipse, it will actually prompt you with an icon on the left to auto-create the
new class.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 100
This is one of the unique things about Spring’s MVC framework versus others like Struts or WebWork. The latter
frameworks typically only provide one method for you to override, and you don’t have as much control over
what happens when. Of course, with Spring’s MVC you don’t have to override its lifecycle methods; it’s simply
an option if you need it. Towards the end of this chapter is a detailed overview of the different lifecycle methods
and when they’re called.
The UserFormController is designed to be simple so you can easily grasp how Spring MVC works. In fact,
you only need to override two methods: onSubmit() and formBackingObject(). The onSubmit() method
handles form posts and the formBackingObject() method gives the request an Object that matches the fields
in your HTML form. This method is a convenient location to fetch existing records, as well as good place to
instantiate empty objects (for example, to display an empty form). This method’s default implementation simply
creates a new empty object. In Spring’s terminology, this object is called a “Command class.” Now that you’re
familiar with the guts of a SimpleFormController, take a look at the code. Below are the contents of the
UserFormController class, minus the imports.
package org.appfuse.web;
/**
* Set up a custom property editor for converting Longs
*/
protected void initBinder(HttpServletRequest request,
Spring Live
Configure DispatcherServlet and ContextLoaderListener 101
ServletRequestDataBinder binder) {
NumberFormat nf = NumberFormat.getNumberInstance();
binder.registerCustomEditor(Long.class, null,
new CustomNumberEditor(Long.class, nf, true));
}
if (request.getParameter("delete") != null) {
mgr.removeUser(user.getId().toString());
request.getSession().setAttribute("message",
getMessageSourceAccessor()
.getMessage("user.deleted",
new Object[] {user.getFirstName() +
' ' + user.getLastName()}));
} else {
mgr.saveUser(user);
request.getSession().setAttribute("message",
getMessageSourceAccessor().getMessage("user.saved",
new Object[] {user.getFirstName() +
' ' + user.getLastName()}));
}
Spring Live
Configure DispatcherServlet and ContextLoaderListener 102
From this code, you can see how the formBackingObject() method simply creates an empty object for adds,
and a populated object for edits. You can also see how simple the onSubmit() method is; it calls the
UserManager to save/delete the User object. The onSubmit() method returns a ModelAndView, but wraps
the successView with a RedirectView. This is to fix the "double submit" problem that often exists in web
applications.
You might notice a lack of exception handling. This is primarily to keep things simple. An exception handling
strategy will be covered in Chapter 5.
Now configure this Controller in the action-servlet.xml file. In this bean’s definition, you will set a fair amount of
declarative values: the “successView”, the “formView” and the command class and its name. This is also where
you will inject its dependency on the UserManager. Below is the definition you need to add to web/WEB-INF/
action-servlet.xml.
<bean id="userFormController"
class="org.appfuse.web.UserFormController">
<property name="commandName"><value>user</value></property>
<property name="commandClass">
<value>org.appfuse.model.User</value>
</property>
<property name="formView"><value>userForm</value></property>
<property name="successView"><value>users.html</value></property>
<property name="userManager"><ref bean="userManager"/></property>
</bean>
In this definition, the commandName property is optional. If you choose not to specify this property, it will
default to “command.” In the next section, when you create the JSP for this controller, you’ll see where the
commandName property is used. It is not by any code in this controller, nor in its Test class.
Since you’re editing action-servlet.xml, add a URL mapping so that “editUser.html” resolves to use the
“userFormController” bean. Do this by adding an additional line to the “mappings” property of the “urlMapping”
bean.
<property name="mappings">
<props>
<prop key="/users.html">userController</prop>
<prop key="/editUser.html">userFormController</prop>
</props>
</property>
Spring Live
Configure DispatcherServlet and ContextLoaderListener 103
Now you should be able to run the UserFormControllerTest in your IDE or from the command line. Figure
4.3 shows the output on the command line from running ant test –Dtestcase=UserForm.
If you see something similar to the output above, nice work! Next create the JSP to interact with the
UserFormController.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 104
In the Struts version of this application, the userForm.jsp was pretty simple: relying on Struts’ <html:form>
and <html:text> JSP tags to make things easy. Unfortunately, Spring doesn’t have similar simplistic form-
specific tags. However, they have good reasons. The main premise behind the lack of rich form tags is to give the
user maximum control over the HTML. Since Spring’s form-handling tags do not generate any HTML, the user
has complete control over it. Since several Struts folks are migrating to Spring, there have been some discussions
on the mailing list of producing something similar. At the time of this writing, an enhancement request is in
Spring's JIRA to add JSP 2.0 tag files for easier form syntax.
Besides unobtrusive form-handling tags, Spring also allows you to configure your form’s action URL to
whatever you like. This may be annoying because it requires more typing, as though you’re hard-coding the URL
to the controller.
Struts will prepend the contextPath, and append the suffix you defined in web.xml (such as *.do).
<html:form action="/user">
You could also use relative paths, which makes the Spring version less typing than the Struts version:
<form action="user.html">
The <c:url> version prepends your application’s contextPath (for example, /myusers), allowing you to easily
move the JSP to a sub-folder. Furthermore, it gives you full control over the extension you want to use. This
works very nicely if you want to have secure and unsecure sections of your application. You can define servlet-
mappings in web.xml (such as *.html and *.secure) and then protect any *.secure URLs. You cannot do this with
Struts, since the form action’s URL is always filled in for you.
The next difference between a Struts JSP and a Spring JSP is how Spring binds object values to form input fields.
Struts’ input tags look up information from the <html:form> tag and match properties to getters in the
ActionForm. Spring’s <spring:bind> tag allows you to bind getters (properties) in your command object
with input fields. Let’s compare the differing syntax for the firstName input field. If you were using Struts, you’d
use an <html:text> tag:
<html:text property="user.firstName"/>
Spring Live
Configure DispatcherServlet and ContextLoaderListener 105
<spring:bind path="user.firstName">
<input type="text" name="firstName" value="${status.value}"/>
<span class="fieldError">${status.errorMessage}</span>
</spring:bind>
Struts' ActionForm and Spring's Command objects are very similar in their functionality. However, Spring
allows easier binding to your domain objects, eliminating the need (in many cases) to develop a form just to
handle web input. One case where you may still need a “web-only form object” is when you want to combine
two domain objects into one form, or if you want to put two forms on one page.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 106
<html:javascript formName="user"/>
At the time of this writing, Spring 1.1.1 has been released, and it does not contain a built-in declarative validation
framework. The core validation framework requires that you create classes to validate other classes. While this is
rather simple, I prefer the way I learned with Struts: using Commons Validator and declaring rules in an XML
file. Daniel Miller recently added support for using Commons Validator with Spring.
For Struts users, using the validation framework they’re familiar with should make Spring MVC even easier. To
migrate the validation.xml file from Struts to Spring MVC, you only need to change a few attribute values – the
formName (userForm user) and the field names (removing user. since you’re not wrapping it with a
DynaActionForm). The modified-for-Spring version is as follows:
<form-validation>
<formset>
<form name="user">
<field property="lastName" depends="required">
<arg0 key="user.lastName"/>
</field>
</form>
</formset>
</form-validation>
The download for this chapter has a validation-rules.xml file (in web/WEB-INF) that is different from the one
that ships with Struts. This file defines all of the Spring-specific classes and methods to use for validation, as
well as defining JavaScript methods for client-side validation.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 107
At the bottom of userForm.jsp is a JSP tag that renders the JavaScript for client-side validation.
<html:javascript formName="user"/>
NOTE
The above one-line JavaScript tag is not the recommended way to configure client-side validation with Commons
Validator. The above method results in all the JavaScript functions being included in the final HTML. Chapter 5
discusses a cleaner way, where the functions are referenced from an external JavaScript file.
To notify Spring that you want to use Commons Validator as your validation engine, add a couple of <bean>
definitions to web/WEB-INF/action-servlet.xml:
<bean id="validatorFactory"
class="org.springframework.validation.commons.DefaultValidatorFactory"
init-method="init">
<property name="resources">
<list>
<value>/WEB-INF/validator-rules.xml</value>
<value>/WEB-INF/validation.xml</value>
</list>
</property>
</bean>
<bean id="beanValidator"
class="org.springframework.validation.commons.BeanValidator">
<property name="validatorFactory">
<ref local="validatorFactory"/>
</property>
</bean>
The first bean (validatorFactory) loads the Validator’s methods and class mappings (validation-rules.xml), as
well as the application-specific validation rules (validation.xml). The second bean (beanValidator) applies
validation to any Plain ol' Java Object (POJO). To configure your UserFormController to use the
beanValidator for validation, you simply need to add a “validator” property to the “userFormController”
definition.
NOTE
The developers of Spring plan to add their own declarative validation framework in the coming months. However,
Commons Validator is currently the only Spring-compatible declarative validation framework. Commons Validator
support is currently in Spring’s sandbox, and there’s talk of integrating it into the core at a future date.
Spring Live
Configure DispatcherServlet and ContextLoaderListener 108
Now if you run ant deploy reload, open your browser to http://localhost:8080/myusers/editUser.html and
try to add a new user without specifying his last name; you should see the following JavaScript alert:
Figure 9.1: Results of the ant deploy reload test with JavaScript disabled
If you want to make sure things are really working as expected, turn off JavaScript and ensure that server-side
validation is working. This is easy in Mozilla Firefox; just go to Tools > Options > Web Features and uncheck
“Enable JavaScript.” Now if you clear the lastName field and save the form, you should see the following:
Figure 9.1: Results of the ant deploy reload test with JavaScript enabled
Spring Live
Configure DispatcherServlet and ContextLoaderListener 109
To capture all the errors at the top of your JSP (like Struts), add the following code block to the top of the JSP,
just after the <title>.
<spring:bind path="user.*">
<c:if test="${not empty status.errorMessages}">
<div class="error">
<c:forEach var="error" items="${status.errorMessages}">
<c:out value="${error}" escapeXml="false"/><br/>
</c:forEach>
</div>
</c:if>
</spring:bind>
If you add this code, run ant deploy and click “Save” without adding a lastName (with JavaScript turned off).
Your screen should resemble the following screenshot:
Figure 9.1: Results of the ant deploy test with JavaScript disabled
You’ve just created a web application that uses Spring for its MVC layer. Congratulations!
Spring Live
SimpleFormController: Method Lifecyle Review 110
SimpleFormController can be a bit overwhelming at first. I recommend that you read its JavaDocs, which
describe the lifecycle (that is, workflow) of its methods. Figure 4.7 illustrates the lifecycle for a GET request, and
Figure 4.8 shows a POST request’s lifecycle.
Spring Live
SimpleFormController: Method Lifecyle Review 111
Spring Live
SimpleFormController: Method Lifecyle Review 112
These diagrams should give you a better understanding of the Spring’s FormController’s lifecycle. Knowing
these methods and when they’re called can be helpful when developing apps with Spring MVC.
Spring Live
Spring’s JSP Tags 113
We briefly touched on the <spring:bind> tag when modifying userForm.jsp; now let’s look at the other JSP
tags available. Below is a list of current tags that ship with Spring by default.
• spring:hasBindErrors provides support for binding the errors for an object. This tag seems
to be most useful for checking a command object if it has bind errors.
• spring:bind binds command object properties to form fields. This tag exposes a “status”
variable in the pageContext. This variable can be grabbed with JSP’s EL; ${status.value}
will give you a properties value, and ${status.errorMessage} will display any errors
associated with a property. This is the most useful tag in Spring’s taglib.
• spring:htmlEscape sets the default HTML escape value for the current page. The default is
“false.” You can also set a defaultHtmlEscape web.xml context-param. Using the tag in a JSP
overrides any settings in web.xml. Its not currently used in any of Spring’s sample apps.
• spring:theme looks up the theme message in the scope of the current page. This tag will be
covered in greater detail in Chapter 5 when we talk about Templating.
From the above list, you can see that many of the core tags are not used. Therefore, you’ve already seen the most
important one: spring:bind.
Spring Live
Summary 114
Summary
This chapter covered a lot of material. It discussed unit testing and using spring-mock.jar to test Controller
classes. Then it demonstrated how to convert the MyUsers app to use Spring MVC instead of Struts. Through
this process, you learned how to create a Controller unit test and how to configure action-servlet.xml for
Controllers, Handlers and Resolvers. You saw how to modify a Struts JSP to use Spring tags, and how to add
declarative validation with Commons Validator. Lastly, it discussed the SimpleFormController’s lifecycle and
Spring’s JSP tags.
This chapter was designed to show you the basics of developing webapps with Spring MVC. My favorite part of
Spring MVC is its method lifecycles. Chapter 5 will cover more advanced validation and website page
decoration (also called templating). You’ll also learn how to handle exceptions in Controllers and how to do a
simple file upload.
Spring Live
Chapter
5
This chapter covers advanced topics in web frameworks, particularly validation and page decoration. It shows
the user how to use Tiles or SiteMesh to decorate a web application. It also explains how the Spring framework
handles validation, and shows examples of using it in the web business layers. Finally, it explains a strategy for
handling exceptions in the controllers, how to upload files and how to send e-mail.
Spring Live
116
Several years ago, web developers didn’t have frameworks to help them develop Java/JSP-based applications.
They were more concerned with getting it done than doing it right. Today, numerous frameworks are available to
accelerate a developer’s efficiency. They have built-in page decoration, validation engines, exception handling
and file upload. Some even support interceptors, which can interrupt a web request and perform logic on-the-fly.
Spring supports all of these features as well. This chapter explores how to configure page decoration frameworks
like SiteMesh and Tiles in your Spring-based webapp. It then shows how you can build from your Struts
Validator knowledge and use the Commons Validator with Spring (which nicely supports client-side validation
with JavaScript). After validation, this chapter covers exception handling, applying interceptors and uploading
files. Lastly, it briefly covers sending e-mail.
This may sounds like a lot to accomplish in one chapter, but it will be simple and easy to understand.
Furthermore, all of these technologies and concepts will be viewed in the context of the MyUsers application. At
the end of this chapter, you’ll have a reference application that employs all of these features.
Spring Live
Templating with SiteMesh 117
SiteMesh is an open-source layout and page decoration framework from the OpenSymphony project. It was
originally created over 5 years ago, when Joe Walnes downloaded the first Sun servlet engine and wrote it using
servlet chains. Over the years, the basic design has stayed the same; content is intercepted and parsed, and a
decorator mapper finds a decorator and merges everything together. The diagram below shows a simplistic
example of how this works.
Skinning is an essential element to every web application. The ability to edit one or two files to change the entire
layout of an app is a must for maintainability. SiteMesh is a simple decoration framework and is very easy to
install and configure.
Spring Live
Templating with SiteMesh 118
If you’re using the application you developed in Chapter 4, SiteMesh is already configured. In order to start from
scratch (without SiteMesh) you must download the MyUsers Chapter 5 bundle from http://sourcebeat.com/
downloads. All of the topics for this chapter have not been configured in the downloaded application. After
downloading it, extract it to “myusers-ch5” and then copy “myusers-ch5” to “myusers-sitemesh.”
NOTE
You will copy “myusers-ch5” to “myusers-tiles” when you install and configure Tiles.
Run ant remove clean install while Tomcat is running; your screen should resemble the one below when
you open http://localhost:8080/myusers. This is much different and quite bland when compared with previous
screens you’ve seen. Installing and configureing SiteMesh will allow you to pretty it up a bit.
Spring Live
Templating with SiteMesh 119
The sitemesh-2.1.jar should already be in the web/WEB-INF/lib directory of “myusers-sitemesh,” so you won’t
have to install that. However, if you were installing SiteMesh from scratch, you would download it from http://
www.opensymphony.com/sitemesh.
2. At the top of this file, right after the <display-name> and before the <context-param>, add the
following <filter> definition:
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>
com.opensymphony.module.sitemesh.filter.PageFilter
</filter-class>
</filter>
3. After the <context-param> and before the <listener> element, add the following <filter-
mapping>:
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
The <dispatcher> elements in the <filter-mapping> above are new to Servlet 2.4. These dispatchers
(as well as ERROR) allow map filters to more than just requested URLs.
Spring Live
Templating with SiteMesh 120
<sitemesh>
<page-parsers>
<parser default="true"
class="com.opensymphony.module.sitemesh.parser.FastPageParser"/>
<parser content-type="text/html"
class="com.opensymphony.module.sitemesh.parser.FastPageParser"/>
<parser content-type="text/html;charset=ISO-8859-1"
class="com.opensymphony.module.sitemesh.parser.FastPageParser"/>
</page-parsers>
<decorator-mappers>
<mapper
class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper">
<param name="config" value="/WEB-INF/decorators.xml"/>
</mapper>
</decorator-mappers>
</sitemesh>
This file configures page-parsers and decorator-mappers. The page-parsers are configured to parse
certain content-types, and it’s unlikely you’ll ever need to change these values. The
ConfigDecoratorMapper is one of several possible decorator-mappers. Specifically, it tells
SiteMesh to read decorators and mappings from the “config” property, which is /WEB-INF/
decorators.xml by default. Because you’re using the default, you could remove the <param> element
in sitemesh.xml and everything would work the same. The decorators.xml file specifies which URL
patterns will use which decorators.
2. Create a decorators.xml file in web/WEB-INF and put the following XML into it.
<decorators defaultdir="/decorators">
<decorator name="default" page="default.jsp">
<pattern>/*</pattern>
</decorator>
</decorators>
Spring Live
Templating with SiteMesh 121
Lastly, build a decorator that will act as the template to wrap the pages in your app. If you’re familiar with Tiles,
this is the same thing as creating a base layout.
<div id="quickSummary">
<p>
<strong>Equinox</strong> is a lightweight version of
<a href="http://raibledesigns.com/appfuse">AppFuse</a>
designed
to accelerate starting a webapp with the
<a href="http://www.springframework.org">Spring Framework</
a>.
</p>
<p class="credit">
Spring Live
Templating with SiteMesh 122
<a href="http://www.csszengarden.com/?cssfile=/083/
083.css">
Design and CSS</a> donated by <a href="http://
www.calcium.ro">
Boér Attila</a>.
</p>
</div>
<div id="content">
<%@ include file="/messages.jsp"%>
<decorator:body/>
</div>
</div>
<div id="supportingText">
<div id="underground">
<decorator:getProperty property="page.underground"/>
</div>
<div id="footer"></div>
</div>
<div id="linkList">
<div id="linkList2">
</div>
</div>
</div>
</body>
</html>
The important elements to look for are the <decorator:*> tags, which are highlighted in yellow. At
the top of the document, a <decorator:title> grabs the <title> tag from JSP pages that are
wrapped, and the <decorator:head/> tag pulls in anything defined in <head>. In the middle, a
<decorator:body/> tag grabs the “body” of the page. Lastly, the <decorator:getProperty>
tag pulls in a <content> tag that’s defined in your decorated JSPs. Here’s an example from the
index.jsp page:
<content tag="underground">
<h3>Additional Information</h3>
<!-- more content here -->
</content>
Spring Live
Templating with SiteMesh 123
3. Add the <decorator> taglib directive to web/taglibs.jsp. Add the following line at the bottom of
this file:
4. Run ant deploy reload, and open your browser to http://localhost:8080/myusers; you should see
a nice and pretty page like the one below.
This is a clean and simple, yet attractive design. For the most part, your decorated pages can use standard HTML
elements. Yet they don’t need all the elements (such as <html>, <body>) that static pages require. Even better,
SiteMesh doesn’t care what server-side technology you use for your application; it works with CGI, PHP,
Servlets, Velocity and FreeMarker. This makes it very Spring-friendly since Spring supports so many different
view technologies (covered thoroughly in the next chapter).
Spring Live
Templating with Tiles 124
Tiles is a templating and document layout engine much like SiteMesh. Tiles is the default layout engine for
Struts, while most SiteMesh users tend to use WebWork (mainly due to the fact that both WebWork and SiteMesh
come from OpenSymphony). Tiles was originally written by Cedric Dumoulin and released shortly after Struts
1.0. Initially, it was a separate add-on to Struts, much like the Validator. Much of the work in Struts 1.1 was
devoted to extracting useful components from Struts into the Jakarta Commons projects. Because no one had the
time to extract it, Tiles did not become a separate project, but became a part of the core Struts (in struts.jar).
The next few sections show you how to configure Tiles to work with Spring’s MVC Framework.
The instructions below are for Struts 1.1, with which Tiles is integrated; hence it does not have its own version
number.
Copy “myusers-ch5” to “myusers-tiles.” If you haven’t downloaded “myusers-ch5” yet, you can download it
from http://sourcebeat.com/downloads. At this point, if you run ant remove clean install, your screen
will look like it did before the SiteMesh section (Figure 5.2).
Spring Live
Templating with Tiles 125
1. In order for Spring to recognize Tiles and use it for rendering views, create a “tilesConfigurer” bean
definition in the action-servlet.xml file in web/WEB-INF, as shown below:
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles.
TilesConfigurer">
<property name="factoryClass">
<value>org.apache.struts.tiles.xmlDefinition.I18nFactorySet</
value>
</property>
<property name="definitions">
<list>
<value>/WEB-INF/tiles-config.xml</value>
</list>
</property>
</bean>
2. Change the “viewClass” property of the “viewResolver” bean from JstlView to TilesJstlView.
You can also delete the “prefix” and “suffix” values in this bean definition. Below is the replacement
“viewResolver” bean definition:
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResource
ViewResolver">
<property name="requestContextAttribute">
<value>rc</value>
</property>
<property name="viewClass">
<value>org.springframework.web.servlet.view.tiles.TilesJstlView</value>
</property>
</bean>
The TilesJstlView class will now resolve any view names to definition names. For instance, in
UserController, it returns the “userList” view from the handleRequest() method. This will
now render the “userList” definition.
Another example is the “formView” property of the UserFormController class. In action-servlet.xml, it’s set
to “userForm,” which will render the “userForm” definition.
Spring Live
Templating with Tiles 126
Now you must create a base layout JSP. This is basically a template (equivalent to SiteMesh’s decorator) that
controls the layout of the page and where certain components are inserted.
1. Create a web/layouts/baseLayout.jsp file. Fill this file with the code below:
<div id="quickSummary">
<p>
<strong>Equinox</strong> is a lightweight version of
<a href="http://raibledesigns.com/appfuse">AppFuse</a>
designed
to accelerate starting a webapp with the
<a href="http://www.springframework.org">Spring Framework</
a>.
</p>
<p class="credit">
<a href="http://www.csszengarden.com/?cssfile=/083/
083.css">
Spring Live
Templating with Tiles 127
<div id="content">
<%@ include file="/messages.jsp"%>
<tiles:insert attribute="content"/>
</div>
</div>
<div id="supportingText">
<div id="underground">
<c:out value="${underground}" escapeXml="false"/>
</div>
<div id="footer"></div>
</div>
<div id="linkList">
<div id="linkList2">
</div>
</div>
</div>
</body>
</html>
This file is very similar to the web/decorators/default.jsp that you created for SiteMesh, except that it
uses the “tiles” JSP tag instead of the “decorator” tag.
2. Because of this, you must add the “tiles” tag to the web/taglibs.jsp file. Here’s what this file should
look like after making this change:
Spring Live
Templating with Tiles 128
Tiles supports two methods of configuring page definitions: configure a page’s definition in a JSP, and configure
each page definition in an XML file. The second method gives a cleaner separation of concerns and allows your
JSPs to be agnostic of the fact that Tiles is using them.
1. To create page definitions for MyUsers, create a tiles-config.xml file in web/WEB-INF and populate
it with the XML below:
<tiles-definitions>
<!-- base layout definition -->
<definition name="baseLayout" path="/layouts/baseLayout.jsp">
<put name="title" value="MyUsers"/>
</definition>
The above file defines the page titles here instead of in the JSPs.
2. To produce clean HTML in your application, delete the <title> elements from userList.jsp and
userForm.jsp in the web directory. There is no easy way to control the title in the JSP as with
SiteMesh.
Spring Live
Templating with Tiles 129
3. Configure Spring so it can resolve URLs to Tiles definitions by adding a bean definition. The
UrlFilenameViewController is declared in the action-servlet.xml as follows:
<bean id="filenameController"
class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/>
4. In order to render the index page (/index.html) of MyUsers, configure the “urlMapping” bean with an
additional mapping:
<prop key="/index.html">filenameController</prop>
5. Run ant remove clean install to see a similar view to the SiteMesh result (Figure 5.3) when
you go to http://localhost:8080/myusers/users.html.
The problem that you’ll experience now is that if you go to http://localhost:8080/myusers, it shows the index.jsp
page with no decoration.
6. Solve this by renaming index.jsp to welcome.jsp and create a new index.jsp with the following
contents:
<tiles:insert definition="index"/>
While the previous solution works, it can be a real pain to create two JSPs to solve problems like this
one. Therefore, I recommend using a servlet filter to redirect certain URLs to others. Using this
solution, you must configure your application so that the root URL invokes the “/index.html” URL.
You cannot do this using the <welcome-file-list> in web.xml, so use Paul Tuckey’s URL
Rewrite Filter to make it happen.
NOTE
Paul Tuckey’s Rewrite Filter is modeled after mod_rewrite for the Apache HTTP Server. It redirects or forwards
requested URLs in order to create tidy URLs, do browser detection, or gracefully handle moved content.
Spring Live
Templating with Tiles 130
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>
org.tuckey.web.filters.urlrewrite.UrlRewriteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
10. Configure this filter’s rules by creating an urlrewrite.xml file in the web/WEB-INF directory:
<urlrewrite>
<rule>
<from>/$</from>
<to type="forward">index.html</to>
</rule>
<rule>
<from>/index.jsp</from>
<to type="forward">index.html</to>
</rule>
</urlrewrite>
Finally, you must fix index.jsp so it sets the “underground” content as a request variable for the definition to pick
up. With SiteMesh, you were able to set content from the JSP using the <content> tag and the
<decorator:getProperty> tag in your decorator. Tiles does not have similar functionality, but you can
mimic this pattern by setting a request attribute with JSTL.
Spring Live
Templating with Tiles 131
11. Open web/index.jsp and change the <content tag="...">...</content> to the following:
This text will then be picked up and rendered by the following line in layouts/baseLayout.jsp:
NOTE
This solution also works with SiteMesh if you’d prefer not to use its proprietary <content> tags.
The last couple of sections have given you the knowledge you need to configure the two most popular page
layout and decoration engines. They both work with Spring’s MVC framework and they’re both relatively easy
to configure (especially now that you have this guide). I recommend SiteMesh, because it is easier to work with,
especially with new applications. However, it’s up to you to choose the one you prefer.
Spring Live
Validating the Spring Way 132
Currently, Spring does not ship with the Commons Validator setup that the MyUsers app uses. It does, however,
have a fairly simple validation system you can use, if you don’t want to use Commons Validator. All you need to
do is create a class that implements org.springframework.validation.Validator, which has the
following methods:
/**
* Return whether or not this object can validate objects
* of the given class.
*/
boolean supports(Class clazz);
/**
* Validate an object, which must be of a class for which
* the supports() method returned true.
* @param obj Populated object to validate
* @param errors Errors object we're building. May contain
* errors for this field relating to types.
*/
void validate(Object obj, Errors errors);
1. Disable Commons Validator in your userForm.jsp file. Simply remove the onsubmit attribute of the
<form> tag:
Spring Live
Validating the Spring Way 133
package org.appfuse.web;
ValidationUtils.rejectIfEmptyOrWhitespace(errors,
"lastName", "errors.required", "Value required."); }
}
}
In the preceding code, this Validator only supports the User class and its validate() method will
return an error if the lastName variable is empty.
3. To configure this Validator in action-servlet.xml, simply add the following bean definition:
4. Change the “validator” property of the “userFormController” bean to use “userValidator” instead of
“beanValidator”:
5. Run ant deploy reload and try to add a new user without a last name. To prove the
UserValidator is being invoked, check your logs for the debug message when entering the
validate method. Using this validation mechanism can be very powerful when you want to do more
sophisticated validation (such as comparing properties against database values).
The next section reviews setting up Commons Validator for a Spring application and using it to verify the
lastName field is not empty. Then it will show you how you can use both Commons Validator’s declarative
validation and the Validator interface to create a validation chain.
Spring Live
Using Commons Validator 134
Chapter 4 explained in detail how to use Commons Validator’s declarative validation framework, so I won’t go
through all the particulars again. However, since this is the chapter on validation, here is a step-by-step overview
of what you need to do:
1. Create a validation.xml file in web/WEB-INF and define your validation rules in it.
2. Download the Spring-specific validation-rules.xml file and install it in web/WEB-INF. This file is
included in the downloadable bundle for this chapter.
4. Configure your FormController bean to use “beanValidator” for its “validator” property.
These steps enable server-side validation, but what about client-side validation? The method from Chapter 4
works, but it includes all of the JavaScript validation functions in the page. A better way is to refer to a
standalone JavaScript file that the user’s browser can cache. The following instructions assume you have no
client-side validation configured on your form.
1. Add an onsubmit attribute to the <form> on which you want to enable validation.
2. At the bottom of the form, add the following lines of code to write JavaScript function calls for the
form’s rules and to include the standalone JavaScript file. If you try this in MyUsers, make sure to
replace the existing <html:javascript> tag.
<html:javascript formName="user"
staticJavascript="false" xhtml="true" cdata="false"/>
<script type="text/javascript"
src="<c:url value="/scripts/validator.jsp"/>"></script>
3. Create a validator.jsp file in web/scripts (you must create the scripts directory) with the following
code:
Spring Live
Using Commons Validator 135
The hardest part about using Commons Validator is the setup and configuration process. Once you have that in
place, creating the rules is fairly simple. You can even use XDoclet to generate the rules from your POJOs.
XDoclet
XDoclet is an open source code generation engine. It enables Attribute-Oriented Programming for Java. This
means that you can add more significance to your code by adding metadata (attributes) to your java sources. This
is done in special JavaDoc tags. For more information, please refer to the XDoclet website. The metadata
attributes that XDoclet uses are also called annotations. This concept has received such praise and use that
annotations have been added as a new feature in J2SE 5.
At the time of this writing, this functionality doesn’t exist in an XDoclet release, but it is checked into XDoclet’s
CVS.
1. To generate your validation rules from your POJOs, define a <webdoclet> task that uses the
<springvalidationxml> task. An example is given below:
<target name="webdoclet"
description="Generate web deployment descriptors">
<taskdef name="webdoclet"
classname="xdoclet.modules.web.WebDocletTask">
<classpath>
<path refid="xdoclet.classpath"/>
</classpath>
</taskdef>
<webdoclet destdir="${webapp.target}/WEB-INF"
force="${xdoclet.force}"
mergedir="metadata/web"
excludedtags="@version,@author"
verbose="true">
<fileset dir="src"/>
<springvalidationxml/>
</webdoclet>
</target>
/**
* @spring.validator type="required"
*/
public void setLastName(String lastName) {
this.lastName = lastName;
}
Spring Live
Using Commons Validator 136
The above code will generate the same validation.xml file that you are using in MyUsers, which makes the
lastName a required field. This example demonstrates how simple XDoclet can make declarative validation. If
you’d like more information on XDoclet, please refer to AppFuse or build XDoclet from CVS.
Chaining Validators
The previous two examples set a “validator” property on the “userFormController”. The bean referenced in this
property referred to the custom “userValidator” or to Commons Validator’s “beanValidator”, which reads its
rules from an XML file. With Spring MVC you can actually add multiple validators by setting a “validators”
property as follows:
<property name="validators">
<list>
<ref bean="beanValidator"/>
<ref bean="userValidator"/>
</list>
</property>
This creates a sort of validation chain that can do simple validation using Commons Validator and more complex
validation with a custom Validator implementation.
While validation in the web tier seems to be the most common practice, there is a demand for validation in the
business layer as well. Below is a simple example of how to use Spring’s validation in your middle tier.
Spring Live
Using Commons Validator 137
try {
user = mgr.saveUser(user);
fail("Validation exception not thrown!");
} catch (Exception e) {
log.debug(e.getCause().getMessage());
assertNotNull(e.getCause());
}
}
3. Run the test using ant test -Dtestcase=UserManager; you should see output similar to Figure
5.4.
This example has a hard-coded validator, but it is not necessary to hard-code which validator to use. You could
add a setValidator() method to the UserManagerImpl (and its interface), then use dependency injection to
set the “validator” property declaratively in your applicationContext.xml file. To make this work, you would
have to declare your “validator” bean in applicationContext.xml, or load action-servlet.xml in your test. All in all,
it’s much easier to configure and use validation in the web tier.
Spring Live
Using Commons Validator 138
Commons Validator is the only declarative validation framework supported by Spring MVC. However, Keith
Donald (a Spring Developer) is working feverishly to develop a more native and robust declarative validation
framework. I asked him to provide me with a few details about it, and here are the key features/differentiators he
sent me:
• A simple, consistent interface for defining new validation rules (rule providers simply implement
a single "boolean test(argument)" method).
• Support for bean property expressions (for example, minProperty must be less than maxProperty).
The property access strategy will be pluggable and not limited to java beans (for example,
allowing map-backed storage, or buffered "form objects" on the rich client side of the house).
• Support for complex nested expressions (and/or/not), and all relational operators (>, >=, <, <=, !=,
==).
• A reporting subsystem capable of iterating over rule structures, performing validation, and
capturing/generating error message results. This allows you to assemble complex rules on-the-fly
without having to hard code a lot of static messages; the reporter is capable of generating rule
messages from the underlying structures automatically.
• Report field typing hints (the rules associated with a field to let the user know what they're
expected to type).
• Integration with Spring Rich Client Platform (RCP) and Spring MVC environments.
From this list, you can see that validation in Spring has a very bright future.
Spring Live
Exception Handling in Controllers 139
Exception handling is something that every webapp should have. The Servlet API provides a simple mechanism
for mapping particular exceptions and error-codes to specific views. In Equinox, for example, the following
clause in its web.xml file says that “if a page is not found” go to the 404.jsp page:
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>
The XML below says that any “500: Internal Server Errors” should go to error.jsp. If you’re seeing a lot of 500
errors when developing your app, you need better exception handling.
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
In addition to web.xml error-pages, it’s a good idea to tell your JSP pages to go to an error page when they
encounter an exception. The MyUsers application does this in web/taglibs.jsp, where you specify
errorPage="/error.jsp". In web.xml, you can additionally declare certain Exceptions go to certain pages
using the <exception-type> mapping:
<error-page>
<exception-type>
org.appfuse.service.UserNotFound
</exception-type>
<location>/userNotFound.html</location>
</error-page>
WARNING
SiteMesh has a bug in Tomcat; it will not decorate <error-page> mappings in your web.xml file. If you want to
map exceptions in web.xml, write these pages so they can stand alone, without being decorated.
While the Servlet API provides a nice means of handling exceptions, it’s difficult to extract information from the
exception and perform logic on that information. It’s difficult to put try/catch statements in Controllers because
they take up a fair amount of lines. It’s so much cleaner to simply call a business delegate’s method. An easy way
to avoid try/catching exceptions in your Controllers is by adding throws Exception to the onSubmit()
method in a FormController. In framewords like Struts, you can do this on your Actions and then declaratively
map Exceptions to views. In other words, you can specify (in XML) that when an Exception gets thrown, the
user should be forwarded to an Action, a Tiles’ definition, or a JSP.
Spring Live
Exception Handling in Controllers 140
The ability to forward to a particular view for a specific exception is also possible with Spring. The easy way to
do this is to define a SimpleMappingExceptionResolver in your action-servlet.xml and specify which
Exceptions go to which view name.
<bean id="exceptionResolver"
class="org.springframework.web.servlet.handler.SimpleMappingException
Resolver">
<property name="exceptionMappings">
<props>
<prop
key="org.springframework.dao.DataAccessException">
dataAccessFailure
</prop>
</props>
</property>
</bean>
In the above code, “dataAccessFailure” refers to the name of a view that the “viewResolver” bean can
determine. To Tiles users, this might refer to a definition in tiles-config.xml.
2. To test that the above mapping works, modify the UserDAOTest (in test/org/appfuse/dao). Near the
bottom of the testAddAndRemoveUser() method, you should have the following line:
assertNull(dao.getUser(user.getId()));
3. Replace this line with the following code to ensure that an exception is thrown when a user isn’t
found:
try {
user = dao.getUser(user.getId());
fail("User found in database");
} catch (DataAccessException dae) {
log.debug("Expected exception: " + dae.getMessage());
assertNotNull(dae);
}
Spring Live
Exception Handling in Controllers 141
<!--
<%
Exception ex = (Exception) request.getAttribute("exception");
ex.printStackTrace(new java.io.PrintWriter(out));
%>
-->
If you’re using the “myusers” project with SiteMesh, this is all you need to do. Tiles users must complete one
more step:
TIP
You can mix and match view classes, such as using JSTL for one view and Tiles for the next. You can do this by
using a ResourceBundleViewResolver for the “viewResolver” and specifying a properties file with the
view’s names and paths. You can see an example of this in the Petclinic sample app that ships with Spring.
However, this won’t help you mix SiteMesh and Tiles.
Spring Live
Exception Handling in Controllers 142
Figure 5.5: Error page stating that the user does not exist
If you need more robust functionality than the SimpleMappingExceptionResolver gives you, look at
implementing the HandlerExceptionResolver for a more customized ExceptionResolver.
Spring Live
Uploading Files 143
Uploading Files
Every so often, you might run into a requirement to upload files in your web application. The good news is that
Spring MVC provides support for uploading files. Better yet, you get a choice of file upload implementations:
Commons FileUpload or COS FileUpload.
NOTE
If you’re using a Servlet 2.2 container, you will not be able to use Spring’s built-in file upload support. However,
Commons FileUpload has excellent support for doing file uploads with a 2.2 container.
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.
CommonsMultipartResolver"/>
2. To implement a file upload feature in MyUsers, add the preceding bean definition, as well as two
classes: a FileUpload command class and a SimpleFormController to handle the uploading
process. Create the command class for this example in src/org/appfuse/web with the following
contents:
package org.appfuse.web;
Spring Live
Uploading Files 144
method, which registers a PropertyEditor to grabbing the uploaded file’s bytes. Without this, the
upload process will fail.
package org.appfuse.web;
if (!dirPath.exists()) {
dirPath.mkdirs();
}
Spring Live
Uploading Files 145
FileCopyUtils.copy(bytes, uploadedFile);
<bean id="fileUploadController"
class="org.appfuse.web.FileUploadController">
<property name="commandClass">
<value>org.appfuse.web.FileUpload</value>
</property>
<property name="formView"><value>fileUpload</value></property>
<property name="successView">
<value>fileUpload</value>
</property>
</bean>
5. Define the URL to access this Controller by adding another <prop> to the “urlMapping” bean:
<prop key="/fileUpload.html">fileUploadController</prop>
Spring Live
Uploading Files 146
6. Create web/fileUpload.jsp with an upload form and a link to display an uploaded file:
<h3>File Upload</h3>
SiteMesh users are finished at this point. Tiles users continue below.
Spring Live
Uploading Files 147
Spring Live
Uploading Files 148
After uploading a file, you should see a screen like the one below.
WARNING
Some files (such as *.html) will not allow viewing by clicking on the filename, so I recommend choosing
LICENSE.txt in the myusers directory.
If you were able to replicate the screenshots above, congratulations! Now you know how to do File Upload with
Spring. Chapter 8: Unit Testing with Spring covers TDD on the FileUploadController.
Spring Live
Intercepting the Request 149
Many MVC Frameworks have the ability to specify Interceptors for Controllers. Interceptors are classes that
intercept the request and perform some sort of logic: controlling flow, setting request attributes, etc. They are
similar to Servlet Filters, the major difference being that Filters are configured in web.xml and Interceptors are
configured in a framework configuration file.
The HandlerInterceptor is an interface that contains methods for intercepting the executing of a handler
before execution (preHandle), after execution (postHandle) and after rendering the view
(afterCompletion). A couple of useful built-in Spring Interceptors are the
OpenSessionInViewInterceptor and the UserRoleAuthorizationInterceptor. The first is
Hibernate-specific, while the second prevents certain roles from accessing certain URLs.
TIP
The OpenSessionInViewInterceptor has a sister filter (OpenSessionInViewFilter) with the same
functionality. Both are used for lazy-loading Hibernate-managed objects when the view renders. The Filter is MVC-
framework agnostic.
Spring Live
Intercepting the Request 150
1. Add the following to the very bottom of the web.xml file in web/WEB-INF:
<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>tomcat</role-name>
<role-name>manager</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>My Users</realm-name>
</login-config>
<security-role>
<role-name>tomcat</role-name>
</security-role>
<security-role>
<role-name>manager</role-name>
</security-role>
2. Run ant deploy reload and go to http://localhost:8080/myusers. You will be prompted to log in.
Use the username “tomcat” and password “tomcat” to log in with the “tomcat” role, and use “admin/
admin” to log in with the “manager” role. These users and roles are configured in Tomcat’s conf/
tomcat-users.xml file (in the $CATALINA_HOME directory).
<bean id="managersOnly"
class="org.springframework.web.servlet.handler.UserRoleAuthorization
Interceptor">
<property name="authorizedRoles">
<value>manager</value>
</property>
</bean>
Spring Live
Intercepting the Request 151
<bean id="managerMappings"
class="org.springframework.web.servlet.handler.SimpleUrlHandler
Mapping">
<property name="interceptors">
<list>
<ref bean="managersOnly"/>
</list>
</property>
<property name="mappings">
<props>
<prop key="/fileUpload.html">
fileUploadController
</prop>
</props>
</property>
</bean>
TIP
You can easily customize this page by specifying a 403 <error-page> in web.xml.
6. Log in again with “admin/admin,” but remember to close your browser to erase your previous login
credentials.
This is just a simple example of using an Interceptor to control access to a URL. You could easily use this same
configuration with a different class and URL mapping to do other things (for example, to make sure certain
attributes are always in the request).
Spring Live
Sending E-Mail 152
Sending E-Mail
E-mail is an excellent notification system; it also can act as a rudimentary workflow system. Spring makes it easy
to send e-mail, and it hides the complexity of the underlying mail system. The main interface of Spring’s mail
support is called MailSender. It also has a SimpleMailMessage class that encapsulates common attributes of
a message (from, to, subject, message). If you want to send e-mails with attachments, you can use the
MimeMessagePreparator to create and send messages.
Create a simple example in the FileUploadController to send an e-mail when the file upload has completed.
In Chapter 9: AOP, you will extract this logic out into a NotificationAdvice class.
1. Add a “mailSender” bean to action-servlet.xml. The “host” property should match an SMTP server
that does not require authentication. If your host requires authentication, you can add “username” and
“password” properties:
<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host"><value>localhost</value></property>
</bean>
NOTE
Using the JavaMailSenderImpl class requires that you have activation.jar and mail.jar in your classpath.
These files are included in the Chapter 5 download bundle.
2. Add a property and setter for MailSender in the FileUploadController class. At the same time,
add a property/setter combination for a SimpleMailMessage. Setting the SimpleMailMessage
with dependency injection allows you to configure a default from and subject in action-servlet.xml.
Spring Live
Sending E-Mail 153
3. To specify the defaults values for an e-mail, add the XML below to action-servlet.xml:
<bean id="mailMessage"
class="org.springframework.mail.SimpleMailMessage">
<property name="from">
<!-- The <value> and CDATA below must be on the same line -->
<value><![CDATA[Uploader <[email protected]>]]></value>
</property>
<property name="subject">
<value>File finished uploading</value>
</property>
</bean>
4. Modify the “fileUploadController” bean definition to inject the “mailSender” and “message”
properties.
<bean id="fileUploadController"
class="org.appfuse.web.FileUploadController">
<!-- other properties hidden for brevity -->
<property name="mailSender"><ref bean="mailSender"/></property>
<property name="message"><ref bean="mailMessage"/></property>
</bean>
5. Add the following code to the end of the onSubmit() method in FileUploadController (before
returning the ModelAndView):
TIP
Be sure to change the e-mail address for msg.setTo() or you’ll just be sending the e-mail to me!
This example simply logs exceptions because it’s not critical that this message be sent. If notification
e-mails are critical in your application, handle the exception with a
SimpleMappingExceptionResolver.
Spring Live
Sending E-Mail 154
6. To test the previous configuration (you must have access to a SMTP server), run ant deploy
reload, go to http://localhost:8080/myusers/fileUpload.html and login as “admin/admin.” Then
upload a file and wait for the e-mail. You should see a similar result as the e-mail below.
TIP
You can also configure the “mailSession” bean to use a MailSession object from JNDI. You can learn more
about this on TheServerSide.com, as well as how to use Velocity templates in the article titled “Sending Velocity-
based E-Mail with Spring.”
Spring Live
Summary 155
Summary
This chapter covered several advanced Spring MVC topics. You learned how to configure and use the two most
popular page decoration frameworks: SiteMesh and Tiles. Validation is an essential part of a web application,
and you saw how to configure Commons Validator, as well as how to implement a native Spring Validator.
Exception handling is important too, and you should now have a grasp of how to throw and handle exceptions.
Uploading files is fairly easy once you have an example, which you now have from this chapter. Interceptors are
similar to Servlet Filters, but Spring has some built-in ones that can be quite useful, like the
UserRoleAuthorizationInterceptor. Finally, you saw how using e-mail as a notification mechanism is
much easier by using Spring’s JavaMail support.
Chapter 6 explores the different views that Spring supports, including Velocity, FreeMarker, XML/XSL, Excel
and PDF.
If you have any feedback on this chapter, or any of the others, please e-mail me at [email protected].
Remember, this is a dynamic book that will live and breathe for quite some time. I can easily update or clarify
any information between monthly releases. Also, if you’d like some material covered more in-depth, don’t
hesitate to ask!
Spring Live
Chapter
6
View Options
The Different View Options in Spring
This chapter covers the view options in Spring’s MVC architecture. At the time of this writing, the options are
JSP, Velocity, FreeMarker, XSLT, PDF and Excel. This chapter aims to become a reference for configuring all
Spring-supported views. It also contains a brief overview how each view works and compares constructing a
page in MyUsers with each option. Additionally, it focuses on internationalization for each view option.
In J2EE applications, JSPs have become the de facto standard for constructing the V in MVC because they’re
really the only page-templating option provided by the J2EE (1.4) specification. However, other options are
quickly gaining recognition. Velocity and FreeMarker are templating technologies that use syntax similar to JSP
2.0. XML/XSL is a nice option if you are pulling XML data and want to transform it, or if you want to serve up
different views (via XSL) on-the-fly. XMLC is similar to the templating technologies, except its syntax is plain
HTML and it uses the id attribute to locate and replace dynamic text. Lastly, outputting data as PDF and Excel
formats is great for reporting and producing portable documents. In this chapter, you will learn how to use each
of these technologies with Spring’s MVC framework. Before learning how to configure the different views, it's
important to understand how Spring determines views.
Spring Live
Views and ViewResolvers 157
Spring ships with a number of ViewResolvers, which allow you to de-couple your Controllers from your View. In
your Controller, you simply specify a logical name for a view and Spring resolves that name to a specific view
type. The View interface prepares the request and hands it over to whichever view technology you have
configured. In its loose-coupling spirit, Spring makes it easy to configure your view choice in one of your XML
context files.
In Chapter 4, you saw how Controllers return a ModelAndView. This object contains the name of a view that the
specified ViewResolver uses to determine what to show to the user. Throughout this chapter, you will learn how
each ViewResolver works. Below is a list of the ViewResolvers currently available in Spring. The “Useful For”
column indicates how one might use each resolver.
Spring Live
Views and ViewResolvers 158
In most cases, you won’t need to choose which ViewResolver to use; you will simply use the one that your view
technology requires. The exception is the ResourceBundleViewResolver, which allows you to choose
different view classes for each view name. You will learn how to configure and use each Spring-supported view
technology before using this resolver.
Each view technology section will show you how to configure the view resolver and how to implement pages for
that technology. The best way to learn is to follow along with the exercises in this chapter. To do this, download
the MyUsers Chapter 6 bundle from http://sourcebeat.com/downloads. This project tree contains all the JARs
you will use in this chapter. You can also use the application you’ve been developing in previous chapters. If you
go this route, download Chapter 6 JARs from http://sourcebeat.com/downloads.
TIP
If you choose to use your existing work, you might want to remove the security you added to demonstrate
Interceptors. It’s a pain to login each time you test the view.
Before getting into view options, write a unit test to verify the basic functionality of listing, adding, saving and
deleting a user.
Spring Live
Testing the View with jWebUnit 159
jWebUnit is an open-source project hosted on SourceForge. It offers a simple API (on top of HttpUnit) for testing
web applications.
1. If you downloaded the Chapter 6 bundle (or the Chapter 6 JARs), you don't need to add any
additional JARs to your application. Otherwise, download jWebUnit (version 1.2) and put the
following JARs in web/WEB-INF/lib:
• jwebunit-1.2.jar
• httpunit-1.5.4.jar
• Tidy.jar
2. If you're developing with Eclipse or IDEA, modify your project's classpath to include these JARs. For
Eclipse 3.0, this is at Project → Properties → Project Build Path → Libraries Tab. In IDEA 4.5, this is
at File → Settings → Project Settings:Paths → Libraries Tab.
TIP
Instructions are available for building and testing the MyUsers application in Eclipse and IDEA.
package org.appfuse.web;
Spring Live
Testing the View with jWebUnit 160
setFormElement("firstName", "Spring");
setFormElement("lastName", "User");
submit("save");
assertTextPresent("saved successfully");
}
/**
* Convenience method to get the id of the inserted user
* Assumes last inserted user is "Spring User"
*/
public String getInsertedUserId() {
String[] paths = {"/WEB-INF/applicationContext.xml"};
ApplicationContext ctx =
new ClassPathXmlApplicationContext(paths);
List users = ((UserDAO) ctx.getBean("userDAO")).getUsers();
// assumed that user inserted in testAddUser() is last user
return ""+((User)users.get(users.size()-1)).getId();
}
}
4. In order for jWebUnit to work, modify a bit of the HTML in the existing JSPs. The
testListUsers() method contains the following line:
assertTablePresent("userList");
Spring Live
Testing the View with jWebUnit 161
This line verifies that a <table> is present in the rendered page. In order for this to work, the table
must have an id attribute with a value of "userList".
5. Open web/userList.jsp and add this attribute so your HTML looks as follows:
6. Change the Hibernate settings so that the database isn’t re-created every time the JVM starts. To do
this, open web/WEB-INF/applicationContext.xml and change the "hibernate.hbm2ddl.auto"
setting from create to update.
<prop key="hibernate.hbm2ddl.auto">update</prop>
7. Since jWebUnit performs in-container testing (it expects a server to be running), you must run ant
clean deploy and start Tomcat (ant deploy reload if Tomcat is already running).
8. Run this test from your IDE or from the command line using ant test –Dtestcase=UserWeb.
WARNING
You may have to delete the database (stop Tomcat, delete “db” in your working directory) if this test doesn’t pass
on your first attempt.
9. Separate the out-of-container and in-container tests in build.xml. To do this, exclude classes with
“WebTest” in their name from being executed in the “test” target. Locate this target in build.xml
and add this line:
10. Add a new “test-web” target right after the existing “test” target.
11. Run all your out-of-container tests using ant test and all the in-container tests using ant test-
web.
Spring Live
JSP 162
JSP
The MyUsers sample application you’ve been developing has been using JSP 2.0. This version of JSP is much
simpler than previous versions in that you can use its Expression Language (EL) to retrieve values for display.
This is not only useful for eliminating scriptlets, but its syntax is similar to that used in Velocity and FreeMarker.
JavaServer Faces (JSF is the J2EE standard web application framework) uses JSPs out-of-the-box. Many folks
believe that JSF will quickly become the dominant way of writing Java-based web application, so the
information below is applicable for JSF applications as well.
In order to use JSP, and the Java Standard Tag Library (JSTL) in particular, you must define an
InternalResourceViewResolver in web/WEB-INF/action-servlet.xml.
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
>
<property name="requestContextAttribute"><value>rc</value></property>
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix"><value>/</value></property>
<property name="suffix"><value>.jsp</value></property>
</bean>
In the above bean definition, the “requestContextAttribute” exposes Spring’s RequestContext object as the
variable rc. If you’re using JSPs, it’s unlikely that you’ll need to use this variable, but you can use it to grab your
application’s contextPath (that is, /myusers) using ${rc.contextPath}. This object is necessary for views that
do not have access to the servlet request (that is, Velocity and FreeMarker templates). The “prefix” and
“suffix” values are convenient ways to make the view name a friendly name, rather than a hard-coded path.
Spring Live
JSP 163
As an example, move the JSPs you created from the web directory to web/WEB-INF/jsp (you’ll need to create the
jsp directory). Below is a partial screenshot of what your project tree should look like at this point. Leave the
following JSPs in the web directory since they're either error pages or used in the SiteMesh decorator:
• 404.jsp
• error.jsp
• index.jsp
• messages.jsp
• taglibs.jsp.
In order to configure Spring to recognize the new location of your JSPs, change the "prefix" of the
viewResolver bean to /WEB-INF/jsp/.
<property name="prefix"><value>/WEB-INF/jsp/</value></property>
To test this, make sure Tomcat is running and execute ant remove. Executing the “remove” target will delete
the application from Tomcat, ensuring that your JSPs aren’t left over in the root directory of your application.
Then type ant deploy and wait for Tomcat to re-install your application. Finally, run ant test-web.
Spring Live
JSP 164
JSTL
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
The value of the property refers to the view class Spring uses to determine the viewable page. In this case, it’s
using JstlView. This class is helpful because it exposes Spring’s local and message bundle to JSTL’s
formatting and message tags. If you’re using JSPs, this is the view class you’ll want to use.
If you’re using JSP and displaying lists of data, check out the display tag library. The display tag is a JSP tag that
provides paging and sorting tables of data. To show you its functionality, implement it in the MyUsers
application. The JAR file for version 1.0 (RC1) is included in the download for this chapter, as well as any CSS
and images. You can also download it from SourceForge.
1. Change the userList.jsp file (in web/WEB-INF/jsp) to have the following contents:
<head>
<title>MyUsers ~ User List</title>
<link href="styles/displaytag.css" type="text/css" rel="stylesheet"/>
</head>
Spring Live
JSP 165
You can use the Excel, XML, and CSV links at the bottom of the table to export the table to the indicated format.
TIP
Use this tag library in your next demo. Most people will be impressed by the ability to sort columns in a web
application.
Spring Live
JSP 166
Tiles
Tiles is a nice page decoration framework that was discussed in Chapter 5. The viewResolver configuration for
Tiles is similar to JSTL: simply set the “viewClass” property to use TilesView or TilesJstlView. I
recommend the JSTL version because it gives you all the features of TilesView, and it gives you access to
JSTL’s formatting tags in your pages.
<property name="viewClass">
<value>org.springframework.web.servlet.view.tiles.TilesJstlView</value>
</property>
To use Tiles, you must set up a bean in web/WEB-INF/action-servlet.xml to read your Tiles configuration file.
The easiest way is to use Spring’s TilesConfigurer class and specify your definition files.
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles.TilesConfigurer">
<property name="factoryClass">
<value>org.apache.struts.tiles.xmlDefinition.I18nFactorySet</value>
</property>
<property name="definitions">
<list>
<value>/WEB-INF/tiles-config.xml</value>
</list>
</property>
</bean>
Using JSPs to code your view is a common way to develop J2EE applications. However, it’s not the only way.
You can use other template frameworks like Velocity or FreeMarker, both of which are lightweight and fast.
Better yet, Spring makes them very easy to use and eliminates the hard part of using them: setup and
configuration.
Spring Live
Velocity 167
Velocity
Velocity works by grabbing items from a context and displaying them on your page. Of course, you must put
items into the context first. Stuffing items into the context is easy, but Spring makes it even easier.
The next section converts the MyUsers application from JSP to Velocity. You will start by configuring Velocity
with Spring. This chapter requires that you have the following Velocity JARs in your classpath (web/WEB-INF/
lib):
• velocity-1.4.jar
• velocity-tools-view-1.1.jar
Spring Live
Velocity 168
In the MyUsers application, SiteMesh is the page decorator. SiteMesh allows you to leave the JSP decoration
intact and use a JSP-based decorator to decorate Velocity-powered pages. However, you may want to use
Velocity for its decorator template as well. In this section, you’ll start by configuring Velocity in the action-
servlet.xml file. Then you'll modify SiteMesh to use a Velocity decorator.
When you add Velocity to a web application, you usually have to add a velocity.properties file to the classpath.
This file has settings in it to tell Velocity which ResourceLoader to use for loading templates. For
convenience, Spring has a VelocityConfigurer class that allows you to specify a path to your templates (that
is, pages) without configuring such a file.
1. Add the XML fragment below to web/WEB-INF/action-servlet.xml. This tells Velocity to load
templates from your application’s WEB-INF/velocity directory.
<bean id="velocityConfig"
class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="resourceLoaderPath">
<value>/WEB-INF/velocity/</value>
</property>
</bean>
2. If you want more control of your ResourceLoader (for example, to load templates from a database),
create a velocity.properties file. To load the file, specify a configLocation property and value on
your velocityConfig bean. You can also specify the properties directly in the bean definition by
replacing configLocation with properties. For more information on using an alternative
ResourceLoader, see Spring’s velocity.properties documentation.
TIP
Make sure to comment out the InternalResourceViewResolver used previously for JSPs.
Spring Live
Velocity 169
The VelocityViewResolver inherits a number of optional properties from its parent, the
AbstractTemplateViewResolver. The table below illustrates these properties and their descriptions. These
properties are applicable for any AbstractTemplateViewResolver (such as the
FreeMarkerViewResolver).
To use Velocity with Spring, you must specify two bean definitions in your *-servlet.xml file
(VelocityConfigurer and VelocityViewResolver). Of course, there’s a lot more work to do. You must
create your Velocity templates and learn how to render error messages in VTL. In the MyUsers application, you
must also learn how to do page decoration with SiteMesh’s Velocity Decorator.
Spring Live
Velocity 170
The most difficult part of converting MyUsers to use Velocity instead of JSP has been configuring SiteMesh to
use a Velocity Decorator. Fortunately, SiteMesh 2.1 added this functionality and it’s now fairly easy.
1. Create a decorator similar to web/decorators/default.jsp; however, this one should use Velocity
instead of JSP tags to pull in SiteMesh content. SiteMesh ships with a
VelocityDecoratorServlet that pre-populates the context with several useful variables.
The VTL syntax is actually quite a bit cleaner than the JSP syntax (for example,
<decorator:title default="MyUsers"/> becomes simply ${title}).
2. Below is the default.jsp decorator re-written as a Velocity decorator. Use these lines as a guide to
create web/decorators/default.vm. Strikethroughs represent text that you must delete. Underlines
represent text that you must change. To save on space, ellipses represent blocks of text that don't
require any changes and therefore are not displayed here.
Notice that the VTL syntax is actually quite a bit cleaner than the JSP syntax (i.e.
<decorator:title default="MyUsers"/> becomes simply ${title}).
TIP
Copy web/decorators/default.jsp to web/decorators/default.vm to prepare for the Velocity conversion below.
Spring Live
Velocity 171
<div id="supportingText">
<div id="underground">$!{page.getProperty("page.underground")}</div>
<div id="footer">
...
<a href="http://bobby.watchfire.com/bobby/
bobbyServlet?URL=${request.requestURL}&output=Submit&gl=sec508&am
p;test=" title="Check the accessibility of this site according to U.S.
Section 508">508</a> ·
<a href="http://bobby.watchfire.com/bobby/
bobbyServlet?URL=${request.requestURL}&output=Submit&gl=wcag1-
aaa&test=" title="Check the accessibility of this site according to
WAI Content Accessibility Guidelines 1">aaa</a>
</div>
3. In the middle of this file, it parses and includes the messages.vm file. This file does not exist, so create
it in the web directory.
## Success Messages
#if ($message)
<div class="message">${message}</div>
${request.session.removeAttribute("message")}
#end
4. Once you’ve created the Velocity template, configure SiteMesh to use it. Open web/WEB-INF/
decorators.xml and change the default decorator’s page attribute to refer to the default.vm file you
just created.
<decorators defaultdir="/decorators">
<decorator name="default" page="default.vm">
<pattern>/*</pattern>
</decorator>
</decorators>
Spring Live
Velocity 172
Edit web.xml
In order for SiteMesh to properly parse the Velocity decorator, add a servlet definition and associated mapping to
web/WEB-INF/web.xml. Add the XML below right after the action servlet declaration:
<servlet>
<servlet-name>sitemesh-velocity</servlet-name>
<servlet-class>com.opensymphony.module.sitemesh.velocity.VelocityDecor
atorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sitemesh-velocity</servlet-name>
<url-pattern>*.vm</url-pattern>
</servlet-mapping>
The last step to converting from JSP to Velocity is to change all the JSP pages to use Velocity’s VTL.
Copy the WEB-INF/jsp directory to WEB-INF/velocity and rename all the files to use a .vm extension. Your
directory structure should be the same as the one in Figure 6.3.
Spring Live
Velocity 173
Below is a list of the four Velocity templates shown above, and the changes that are necessary to make them
work with Velocity. Special notes for each file are directly after the code. Any VTL code that you’ll need in your
templates is underlined.
userList.vm
userForm.vm
#springBind("user.*")
#if ($status.error)
<div class="error">
#foreach ($error in $status.errorMessages)
${error}<br/>
#end
</div>
#end
Spring Live
Velocity 174
Notice the #springBind macro call that exposes variables for each field. The exclamation point after the dollar
sign ($!{...}) indicates that nothing should be printed if no value is found.
These macros don’t have a closing tag or ending statement like the <spring:bind> JSP tags require. The
#springBind macro becomes available when you set the exposeSpringMacroHelpers property to true on
the viewResolver bean.
Client-side validation with JavaScript and Commons Validator is only supported with JSPs.
Spring Live
Velocity 175
fileUpload.vm
<h3>File Upload</h3>
#if ($model.filename)
<p style="font-weight: bold">
Uploaded file (click to view): <a
href="${model.url}">${model.filename}</a>
</p>
#end
dataAccessFailure.vm
This file does not have the Exception’s stack trace printed out in a comment like the JSP. I tried putting
${exception.printStackTrace()} in a comment, but the stack trace is never printed.
Now that you’ve told Spring to configure and use Velocity, configured SiteMesh to use a Velocity Decorator and
converted all the JSPs to Velocity, it's time to test that everything worked. Start Tomcat and run ant clean
deploy reload. Once the new context has restarted, run ant test-web to verify everything works.
WARNING
If the test fails because a user is not found in the database, delete the database (rm -r db), restart Tomcat and
try again. You can also add <delete dir="db"/> to the “delete” target.
Spring Live
Velocity 176
Summary of Velocity
In this section, you learned how to use Velocity as an alternate view technology to JSP. Velocity tends to have a
much cleaner syntax than JSP (though JSP has improved with version 2.0). Compilation is much faster than JSP
when you first access a Velocity-backed template. With JSP, the initial load time can be a couple seconds, which
is slightly painful when developing since you constantly have to wait. It's not something you notice until you've
developed with Velocity and then go back to JSPs. The one downside to Velocity is that you can't use the rich set
of tag libraries (for example, displaytag, oscache, etc.) that are available. However, a fast templating solution that
allows you to use JSP tags is available: FreeMarker.
Spring Live
FreeMarker 177
FreeMarker
The next section converts the MyUsers application from Velocity to FreeMarker. Start by configuring
FreeMarker with Spring. This chapter requires you to have FreeMarker’s freemarker.jar in your classpath (web/
WEB-INF/lib).
NOTE
Spring requires FreeMarker version 2.3 or higher.
<bean id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigur
er">
<property name="templateLoaderPath"><value>/</value></property>
</bean>
Like the VelocityConfigurer, you can configure this class with a properties file (with a
configLocation property pointing to the file). You can also set properties on the bean itself by
specifying a freemarkerSettings list of properties.
2. Comment out the velocityConfig bean and the viewResolver bean, and add one for
FreeMarker. The properties on this resolver are very similar to the Velocity version, the only
difference being the "prefix" and the "suffix".
Spring Live
FreeMarker 178
Keep in mind that you can also expose request and session attributes using the
exposeRequestAttributes and exposeSessionAttributes on this template resolver. This
tutorial doesn't require them, but they have value.
Just like Velocity, SiteMesh has a decorator servlet specifically for FreeMarker called
FreeMarkerDecoratorServlet. It pre-populates FreeMarker’s data model with several context attributes,
which are listed in the table below.
${base} request.getContextPath()
${title} Parsed page <title>
${head} Parsed page <head>
${body} Parsed page <body>
${page} SiteMesh’s internal Page object
FreeMarker supports getting request parameters, request attributes and session variables, which is why you don’t
see $req and $res like you do with Velocity. These values are available in the templates through the variables
Request, RequestParameters, Session and Application (for example, ${Session["user"]}).
1. Below is the Velocity decorator from the previous section re-written using FreeMarker. The listing
displays only the changed lines, so you don't have to copy the entire contents of the file below. Use
these lines as a guide to create web/decorators/default.ftl. The variable references are the same as
Velocity, but the conditional logic syntax is different.
TIP
Copy web/decorators/default.vm to web/decorators/default.ftl to prepare for the conversion.
<div id="content">
<#include "/messages.ftl"/>
${body}
</div>
</div>
<div id="supportingText">
<div id="underground">
<#if page.getProperty("page.underground")?exists>
${page.getProperty("page.underground")}
</#if>
</div>
...
Spring Live
FreeMarker 179
<a href="http://bobby.watchfire.com/bobby/
bobbyServlet?URL=${Request.requestURL}&output=Submit&gl=sec508&am
p;test=" title="Check the accessibility of this site according to U.S.
Section 508">508</a> ·
<a href="http://bobby.watchfire.com/bobby/
bobbyServlet?URL=${Request.requestURL}&output=Submit&gl=wcag1-
aaa&test=" title="Check the accessibility of this site according to
WAI Content Accessibility Guidelines 1">aaa</a>
...
<decorators defaultdir="/decorators">
<decorator name="default" page="default.ftl">
<pattern>/*</pattern>
</decorator>
</decorators>
• To check for a null value in Velocity, you simply write #if (object.property) to test for the
existence of a value. With FreeMarker, you must append ?exists to test for nulls.
FreeMarker has some limitations, which arguably make it a cleaner MVC implementation. The easiest and
cleanest way to solve the issues above is to create a ServletFilter that searches for messages in the session, and if
it finds them, it stuffs them in the request. This way, you can forget about removing them in a view page and let
the filter do the work.
Spring Live
FreeMarker 180
Below is a MessageFilter.java class to put in src/org/appfuse/web. The logic to set the "requestURL" in the
request is to provide full links back to your application as part of the project’s footer page.
package org.appfuse.web;
// grab messages from the session and put them into request
// this is so they're not lost in a redirect
Object message = request.getSession().getAttribute("message");
if (message != null) {
request.setAttribute("message", message);
request.getSession().removeAttribute("message");
}
chain.doFilter(req, res);
}
4. Using this filter, you can eliminate any session removal logic in the all the messages templates (at
web/messages.*). To enable it, add a <filter> and <filter-mapping> to web/WEB-INF/
web.xml. Put the following <filter> declaration just above the sitemesh filter.
<filter>
<filter-name>messageFilter</filter-name>
<filter-class>org.appfuse.web.MessageFilter</filter-class>
</filter>
Spring Live
FreeMarker 181
<filter-mapping>
<filter-name>messageFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Edit web.xml
To edit web.xml for SiteMesh, comment out the sitemesh-velocity servlet and its mapping and add the
following sitemesh-freemarker servlet and mapping.
<servlet>
<servlet-name>sitemesh-freemarker</servlet-name>
<servlet-
class>com.opensymphony.module.sitemesh.freemarker.FreemarkerDecoratorServ
let</servlet-class>
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/</param-value>
</init-param>
<init-param>
<param-name>default_encoding</param-name>
<param-value>ISO-8859-1</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>sitemesh-freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>
Spring Live
FreeMarker 182
The last step for implementing FreeMarker is to create page templates using FreeMarker’s template language.
1. Copy the WEB-INF/velocity directory to WEB-INF/freemarker and rename all the files to use an FTL
extension. Your directory structure should be the same as the one in Figure 6.4.
2. Below is a list of the four FreeMarker templates shown above, and the changes that are necessary to
make them work with Velocity. Special notes for each file are directly after the code. Any lines where
the FreeMarker code is different from Velocity’s VTL are underlined.
Spring Live
FreeMarker 183
userList.ftl
Similar to Velocity, the ${rc.getMessage()} call gets localized messages from web/WEB-INF/classes/
messages.properties.
userForm.ftl
<@spring.bind "user.*"/>
<#if spring.status.error>
<div class="error">
<#list spring.status.errorMessages as error>
${error}<br/>
</#list>
</div>
</#if>
Spring Live
FreeMarker 184
<@spring.bind "user.id"/>
<input type="hidden" name="id"
value="${spring.status.value?default('')}"/>
<table>
<tr>
<th>${rc.getMessage("user.firstName")}:</th>
<td>
<@spring.bind "user.firstName"/>
<input type="text" name="firstName"
value="${spring.status.value?default('')}"/>
<span class="fieldError">${spring.status.errorMessage}</span>
</td>
</tr>
<tr>
<th>${rc.getMessage("user.lastName")}:</th>
<td>
<@spring.bind "user.lastName"/>
<input type="text" name="lastName"
value="${spring.status.value?default('')}"/>
<span class="fieldError">${spring.status.errorMessage}</span>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" class="button" name="save" value="Save"/>
<#if user.id?exists>
<input type="submit" class="button" name="delete" value="Delete"/>
</#if>
</td>
</table>
</form>
The most important thing to notice in this file is the first line where spring.ftl is imported. This file contains the
spring.bind macro needed to expose a properties value.
You may also notice that each field is given a default by adding ?default('') to the end of its expression.
These macros don’t have a closing tag or ending statement like the <spring:bind> JSP tags require. The
<@spring.bind> macro becomes available when you set the exposeSpringMacroHelpers property to
“true” on the viewResolver bean.
Client-side validation with JavaScript and Commons Validator is only supported with JSPs.
Spring Live
FreeMarker 185
fileUpload.ftl
<h3>File Upload</h3>
<#if model?exists>
<p style="font-weight: bold">
Uploaded file (click to view): <a
href="${model.url}">${model.filename}</a>
</p>
</#if>
dataAccessFailure.ftl
The code for this file is the same for FreeMarker and Velocity.
Spring Live
FreeMarker 186
Now that you’ve told Spring to configure and use FreeMarker, configured SiteMesh to use a FreeMarker
Decorator and converted all the pages to use FreeMarker, it's time to test that everything worked. Start Tomcat
and run ant clean deploy. Once everything has started, run ant test-web to verify everything works.
JSP, Velocity and FreeMarker are the dominant view choices when using Spring MVC. However, a few others
can be quite useful. They are XSTL (for displaying and transforming XML), PDF and Excel. In the next sections,
you’ll implement these to generate reports of the user list screen.
Spring Live
XSLT 187
XSLT
XSLT describes the process of combining XML and XSL to transform XML to another output. In most cases,
this output is text-based, but you can also use XSL-FO to generate PDFs. Spring’s XSLT views can be helpful if
you’re loading and presenting XML documents, or you can easily convert your model to XML.
Before creating an XSLT View, prepare the UserController class to handle rendering report views.
2. Open UserController.java (in src/org/appfuse/web) and add some logic to render a different view
name if a report parameter is passed in. This logic will also render the Excel and PDF views
covered later in this chapter.
3. The reports you're about to produce are simply going to contain the user’s full name in a list format.
To make this easier, add a getFullName() method the User class (in src/org/appfuse/model).
Spring Live
XSLT 188
Before creating the class to convert the list of users to XML, create a class to test it.
package org.appfuse.web;
2. In src/org/appfuse/web, create a UserXMLView class that extends AbstractXsltView and has the
following contents.
NOTE
The XML classes used are from dom4j, which should be in your classpath. Also, the Node return type (from
createDomNode()) is from the W3C’s DOM (org.w3c.dom.Node).
package org.appfuse.web;
Spring Live
XSLT 189
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement(rootName);
doc.setRootElement(root);
response.setContentType("text/xml");
NOTE
In this example, you’re going to modify this XML document to produce another XML document (that’s why the
response is set to be content type "text/xml"). If you were going to produce HTML in your XSL stylesheet, you
could eliminate this line.
4. Create a users.xsl file in web/WEB-INF/xsl. Add the following XSL to this document.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="no"/>
<xsl:template match="/">
<users>
<xsl:for-each select="users/user">
<user><xsl:value-of select="."/></user>
</xsl:for-each>
</users>
</xsl:template>
</xsl:stylesheet>
Spring Live
XSLT 190
5. Now that you have created all the view files, configure Spring to know about the userListXML view
name. The easiest way to do this is to create a second viewResolver that uses
ResourceBundleViewResolver to resolve its views. Since you already have a viewResolver
bean for FreeMarker, you must give the new resolver a different id. Using reportViewResolver
is a good solution:
<bean id="reportViewResolver"
class="org.springframework.web.servlet.view.ResourceBundleView
Resolver">
<property name="order"><value>1</value></property>
</bean>
6. The “order” property specifies priorities of view resolvers. Add this same property to the
viewResolver bean with a value of 0. The ResourceBundleViewResolver allows you to configure
your view names, their classes, and their properties in a ResourceBundle (or properties file). By
default, this file name is views.properties, and it should exist in your WEB-INF/classes directory. If
you want to override the name of this file, specify a “basename” property on the
reportViewResolver bean.
7. Create a view.properties file in web/WEB-INF/classes and fill it with the following text:
userListXML.class=org.appfuse.web.UserXMLView
userListXML.stylesheetLocation=/WEB-INF/xsl/users.xsl
userListXML.root=users
Spring Live
XSLT 191
You are now ready to deploy and test the application. Run ant deploy reload populate and load http://
localhost:8080/myusers/users.html?report=XML into your browser. You should see something similar to the
screenshot below.
One report is working; add reports for PDF and Excel too. To make things easier, add a set of links to the current
userlist page. If you’re still using FreeMarker, the filename is userList.ftl and it’s located in web/WEB-INF/
freemarker. Open it and add the following HTML to the top of the file, between the <button> and the
<table>:
Spring Live
Excel 192
Excel
Excel documents are a useful way to export data for manipulation by users. If you simply need to output a list
screen in Excel, I recommend using the Display Tag Library (described earlier). If you need something more
robust, then this section is for you. The next few steps show you how to build and send an Excel spreadsheet
using Jakarta’s POI library.
NOTE
If you downloaded and installed the chapter6-jars.zip file, you will already have poi-2.5-final-20040302.jar in your
classpath. This JAR contains classes that are necessary to complete this exercise.
package org.appfuse.web;
Notifying Spring that the userListExcel view exists is quite easy using the ResourceBundleViewResolver.
userListExcel.class=org.appfuse.web.UserExcelView
Spring Live
Excel 193
2. Click on the Excel link at the top-right of the table. If you have Excel installed, it starts Excel and
brings up a list of the current users.
Spring Live
PDF 194
PDF documents are an excellent way to produce printable reports. In most MVC frameworks, you’re required to
use something like JasperReports to produce PDF output. With Spring, you simply create a subclass of
AbstractPdfView and override the buildPdfDocument() method. This method returns an iText document
that can be easily rendered in your browser.
NOTE
If you downloaded and installed the chapter6-jars.zip file, you will already have itext-1.02b.jar in your classpath.
This JAR contains classes that are necessary to complete this exercise.
1. Create a UserPDFView class in src/org/appfuse/web. Populate this class with the code below.
package org.appfuse.web;
2. To notify Spring where the userListPDF view is, open web/WEB-INF/classes/views.properties and
add the following line:
userListPDF.class=org.appfuse.web.UserPDFView
Spring Live
PDF 195
2. Click on the PDF link at the top-right of the table. This opens a PDF containing a list of the user’s
names.
Spring Live
Summary 196
Summary
This chapter explored the rich functionality offered in Spring’s MVC framework. It showed you how to easily
change from using JSP to Velocity without changing a single line of Java code. Then it showed you how to
change that to FreeMarker by simply altering some XML files and re-working the templates a bit. You also
learned how to use SiteMesh with Velocity and FreeMarker. The ability to use all of these J2EE templating
engines with a highly configurable page decoration engine like SiteMesh is a very powerful solution for quickly
developing web applications. Spring makes it easy to switch from one to the other, so your templating
technology of choice is not a hard-and-fast decision.
The ability to produce reports in PDF and Excel is another powerful and easy-to-use feature of Spring MVC. It’s
even nicer that these features are powered by solid and well-supported open-source projects like iText and POI.
The reporting examples in this chapter are simple, but the main goal is to show you how to get the ball rolling -
not how to win the game.
Spring Live
Summary 197
Spring Live
Summary 198
Spring Live
Chapter
7
Hibernate is quickly becoming a popular choice for persistence in Java applications, but sometimes it doesn’t fit.
If you have an existing database schema, or even pre-written SQL, sometimes it’s better to use JDBC or iBATIS
(which supports externalized SQL in XML files). This chapter refactors the MyUsers application to support both
JDBC and iBATIS as persistence framework options. It also implements the UserDAO using JDO and OJB to
showcase Spring's excellent support for these frameworks.
Spring Live
Overview 200
Overview
Most modern applications talk to a database to load and store their information. Persistence is the process of
retrieving, saving and deleting data from a data store (usually a relational database). Persistence is a critical
feature in web applications, if only for loading and displaying information. For many years, this has been the
ugly part of Java. You could use JDBC, and it usually works across database vendors. After all, the point of the
JDBC API is to provide a standard for accessing databases from Java. However, JDBC is difficult to write,
especially for a newcomer. It requires developers to catch exceptions and close connections in a final block,
which many Java rookies forget to do. Additionally, the exceptions thrown by different JDBC Driver vendors are
not standard, so an error code on one server might mean something completely different on another server.
With the advent of Spring's persistence support, many of the issues with JDBC disappear. Its JDBC framework
converts JDBC's checked exceptions to a common hierarchy of RuntimeExceptions. These exceptions provide
precise information about what went wrong, which is much better than SQLException reports. It uses closures
to handle closing database connections, and it includes a set of common SQL error codes for numerous database
types.
NOTE
A closure is an object that's represented as a block of code (within a method). You can use this object like any Java
object, such as parameter, variable, etc. For more information, see the Java Glossary. Charles Miller also has a
tutorial on Closures and Java.
Spring provides support classes for numerous other persistence frameworks, including Hibernate, iBATIS, JDO
and OJB. It even uses a common methodology in its support, further simplifying the learning curve from one
framework to the other. If you learn how to use Spring's Hibernate support, it's very easy to use Spring's iBATIS
or JDO APIs.
In this chapter, you will learn more about Spring's Hibernate support and how MyUsers uses Hibernate. From
there, you will implement DAOs using iBATIS, Spring JDBC, JDO and OJB. By the end of this chapter, you will
know how to use these technologies and configure them with the Spring Framework. The purpose of this chapter
is to briefly introduce persistence options with Spring and how to configure them. You will not learn how to
solve complicated persistence problems with each framework. Please consult the framework's perspective
project page for that information.
NOTE
Transactions will be covered in Chapter 10.
Spring Live
Overview 201
The code in this chapter should be easy to test since you already have a UserDAOTest that does not contain any
Hibernate-specific code. This test will verify your implementations. Each persistence framework section will
show you how to configure the respective framework with Spring, and how to implement the UserDAO.
Spring Live
Overview 202
The best way to learn is to follow along with the exercises in this chapter. The easiest way to do this is to
download the MyUsers Chapter 7 bundle from http://sourcebeat.com/downloads. This project tree is the result
of Chapter 6 exercises, and contains all the JARs you will use in this chapter. You can also use the application
you've been developing in previous chapters. If you go this route, download Chapter 7 JARs from http://
sourcebeat.com/downloads. The Configuration sub-topic of each section describes the dependencies each project
needs. You can use this chapter as a reference for integrating these principles into your own applications.
If you're using a MyUsers application from a previous chapter, you must change the way that it loads context
files. Rather than just loading applicationContext.xml, change tests and web.xml to load
applicationContext*.xml. This is for convenience. When creating context files for each persistence option, it's
easier to specify a wildcard to match the filename. Otherwise, you'd have to change the tests and web.xml each
time you switched persistence layers.
• test/org/appfuse/dao/BaseDAOTestCase.java
• test/org/appfuse/service/UserManagerTest.java
• test/org/appfuse/web/UserControllerTest.java
• test/org/appfuse/web/UserFormControllerTest.java
• web/WEB-INF/web.xml
Because you'll be isolating all the database configurations to one file, you must also change any <ref local>
directives in applicationContext.xml to <ref bean>, so other files can contain bean definitions. The baseline
applicationContext.xml file for this chapter should not contain references to the following beans: dataSource,
sessionFactory, transactionManager, and userDAO. If they exist in your version, please delete them.
Making the above changes will allow you to create different context files for each DAO implementation.
Spring Live
Hibernate 203
Hibernate
Hibernate is an open-source Object/Relational Mapping (ORM) solution. ORM is the technique of mapping an
Object model to a Relational model (usually represented by a SQL database). Hibernate was created in late 2001
by Gavin King and a handful of other developers. Since then, Hibernate has become a very popular persistence
framework in the Java Community. It's become so popular in fact, that the next versions of EJB and JDO are
using Hibernate as a source of good ideas. The reasons for its popularity are mainly due to its good
documentation, ease of use, excellent features and smart project management. Hibernate's license is LGPL,
which means you can use it for free as long as you don't modify its source code. More information on its license
is available on the Hibernate website.
Hibernate frees you from hand-coding JDBC. Rather than using SQL and JDBC, you can use domain objects
(which are usually POJOs) and simply create XML-based mapping files. These files indicate which fields (in an
object) map to which columns (in a table). Hibernate has a powerful query language called Hibernate Query
Language (HQL). This language allows you to write SQL, but also use object-oriented semantics. One of the best
parts about its query language is you can literally guess at its syntax and get it right.
Hibernate's Session interface is similar to a database connection in that it must be opened and closed at
appropriate times to avoid errors and memory leaks. In my opinion, the biggest advantage of using Spring with
Hibernate is that you don't have to manage opening and closing these Sessions – everything just works.
NOTE
Spring's Hibernate support classes are located in the org.springframework.orm.hibernate and
org.springframework.orm.hibernate.support packages.
Although Chapter 2 covers integrating Hibernate into MyUsers, this chapter covers it as well to provide a single
chapter describing Spring's persistence support. If you downloaded the myusers-ch7 bundle, the Hibernate
configurations are eliminated so you can start with a clean slate.
Spring Live
Hibernate 204
Dependencies
Hibernate has a number of third-party libraries it depends on. All of these are available as part of the Hibernate
download. Below are the JARs included in the MyUsers download as part of Hibernate 2.1.6. They are all
required, except for the one marked optional. Spring requires Hibernate 2.1 or above for its support classes.
• cglib-full-2.0.2.jar – Code Generation Library for generating proxies for persistent classes
• ehcache-0.9.jar – A pure Java, in-process cache; the default cache for Hibernate
TIP
While Spring does not support Hibernate 3 (now in beta), a patch is available.
Spring Live
Hibernate 205
Configuration
To make your objects persistable with Hibernate, first create a mapping file. (This chapter assumes you have
already created a User POJO in src/org/appfuse/model, with id, firstName and lastName properties.
1. In the src/org/appfuse/model directory, create a User.hbm.xml file with the following contents:
<hibernate-mapping>
<class name="org.appfuse.model.User" table="app_user">
</class>
</hibernate-mapping>
In the above mapping, the <id> element uses "increment" to indicate max value + 1 for the
generated primary key. The generator type of "increment" is not recommended for a cluster.
Fortunately, Hibernate has many other options.
Spring Live
Hibernate 206
The "dataSource" bean in this example uses an HSQL database, which is a pure Java database that
runs from a simple hsqldb.jar file in the web/WEB-INF/lib directory. Later, you'll change this to use
MySQL to see how easy it is to change databases.
<beans>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:db/appfuse</value>
</property>
<property name="username"><value>sa</value></property>
<property name="password"><value></value></property>
</bean>
The DriverManagerDataSource is a simple DataSource that configures a JDBC Driver via bean
properties. You can also configure a JNDI DataSource if you'd rather use your container's pre-
configured DataSource. For example, a common strategy is to use the DriverManagerDataSource
for testing, and the JNDI DataSource (below) for production.
NOTE
Chapter 8 shows how to mock the JNDI DataSource and use the configuration below for both testing and
production.
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/appfuse</value>
</property>
</bean>
Spring Live
Hibernate 207
3. Add a "sessionFactory" bean definition, which depends on the previous "dataSource" bean
and the mapping file. The "dialect" property will change based on the database, and the
"hibernate.hbm2ddl.auto" property creates the database on-the-fly when the application starts.
You might notice that the "dataSource" reference has changed (from previous chapters) to use
<ref bean> rather than <ref local>. This is so you can configure the "dataSource" bean in
separate file, possibly switching a testing version with an in-container version.
<bean id="sessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref bean="dataSource"/></property>
<property name="mappingResources">
<list>
<value>org/appfuse/model/User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
net.sf.hibernate.dialect.HSQLDialect
</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
The UserHibernateDAO or UserDAOTest do not use this bean specifically, but the
"userManager" bean's definition references it. The JDO section shows you how to utilize
transactions in your DAO Tests.
Spring Live
Hibernate 208
NOTE
If you're continuing development on a MyUsers application from the previous chapters, change the saveUser()
method in UserDAO to take a User (rather than an object) as a parameter.
package org.appfuse.dao.hibernate;
Spring Live
Hibernate 209
In the UserDAOHibernate class, HibernateTemplate does most of the work. Using templates to
handle persistence calls is a common theme among Spring DAO support classes. Also note the
following items in the UserDAOHibernate.java class:
• It has no checked exceptions. You will likely end up writing a fair amount of try/catch statements
with Hibernate.
if (logger.isDebugEnabled()) {
logger.debug("User's id set to: " + user.getId());
}
Test It!
If you're developing from a previous MyUsers application, you must make a couple of changes to the
UserDAOTest.testGetUsers() method before you can successfully run it. The new method is below. It
removes assertions that checked for an empty database table before the test and a single record after the test. The
reason for this is that you set the "hibernate.hbm2ddl.auto" property (on the "sessionFactory" bean)
to update. This changes Hibernate's behavior and updates the schema when the JVM starts (rather than creating
it each time). The primary motivation for this is other frameworks don't easily create/delete tables, and you want
to have a consistent unit test.
Spring Live
Hibernate 210
Run ant test -Dtestcase=UserDAO. Your results should be similar to Figure 7.2.
Spring Live
Hibernate 211
MySQL Configuration
Switching from HSQL to MySQL is quite easy. Thanks to Spring, it's just a matter of configuration settings.
1. Make sure MySQL is installed and running. If you're using MyUsers from a previous chapter, install
the additional JARs for this chapter or download MySQL's JDBC Driver and copy it to web/WEB-
INF/lib.
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost/myusers</value>
</property>
<property name="username"><value>root</value></property>
<property name="password"><value></value></property>
A username of root and empty password are the default installation settings. You may need to adjust
them for your installation.
TIP
You can use a PropertyPlaceHolderConfigurer bean to set the above property values from a properties
file. This is covered in Chapter 8.
<prop key="hibernate.dialect">
net.sf.hibernate.dialect.MySQLDialect
</prop>
4. Create the myusers database before running unit tests or starting Tomcat:
Optionally, change the driver, url and userid properties in the "browser" target of build.xml if
you want to use that target with MySQL.
Run ant test -Dtestcase=UserDAO. The results should be the same as in Figure 7.2.
Spring Live
Hibernate 212
Caching
A powerful feature in persistence frameworks is the ability to cache data and avoid constant trips to the database.
The Hibernate Session object is a transaction-level cache of persistent data, but it doesn't handle per-class or
collection-by-collection caching at the JVM or cluster level. However, it does support plugging in both JVM or
clustered caches (also called second level caches). For a complete list of supported caches, see Hibernate's
Second Level Cache documentation.
The following example shows how to configure the EHCache for JVM-level caching.
NOTE
EHCache is the default cache, so you don't need to configure a hibernate.cache.provider_class
setting in applicationContext-hibernate.xml.
1. The simplest way to enable caching for an object is to add a <cache> tag to its mapping file. To do
this with the User object, add a <cache> element to the User.hbm.xml file in src/org/appfuse/model.
Optional values are "read-write" and "read-only". You should use the second option only if
you're referring to an object or table that rarely changes.
2. (Optional) Create settings in EHCache's configuration file for this class. Create an ehcache.xml file in
web/WEB-INF/classes and fill it with the following XML:
<ehcache>
<!-- Only needed if overFlowToDisk="true" -->
<diskStore path="java.io.tmpdir"/>
<!-- Required element -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"/>
Spring Live
Hibernate 213
3. To prove that your User object is being cached, turn on debug logging for EHCache in web/WEB-
INF/classes/log4j.xml:
<logger name="net.sf.ehcache">
<level value="DEBUG"/>
</logger>
4. Run ant test -Dtestcase=UserDAO. Your results should be similar to Figure 7.3.
The Hibernate reference documentation has more information on using and configuring Second Level Caches. In
general, caching is something that you shouldn't configure for your application until you've tuned your database
(that is, with indexes). This cache implementation is for demonstration purposes only.
Spring Live
Hibernate 214
One of Hibernate's many features is the ability to lazy-load dependent objects. For example, if a list of users
refers to a collection of roles objects, you probably don't need the roles loaded to display the list of users. By
marking the roles collection with lazy-load="true", they won't be loaded until you try to do something with them
(usually in the UI).
To use this feature with Spring, configure the OpenSessionInViewFilter in your application. This will open
a session when a particular URL is first requested, and close it when the page finishes loading. To enable this
feature, add the XML below to web.xml:
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>org.springframework.orm.hibernate.support.OpenSession
InViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
Using this feature may cause the following error when running DAO unit tests:
Spring Live
Hibernate 215
To fix this, add the following code to the setUp() and tearDown() methods of your test.
While using this open-session-in-view pattern is a well-known Hibernate session handling idiom, some Spring
developers advocate initializing all necessary data in the service layer. Loading everything at once is more
efficient and guarantees the same data for all clients.
Other tips and tricks for Hibernate are available on the Hibernate website.
The Hibernate community is a vibrant one. In addition to having good documentation and well-supported user
forums, it is very popular with tens of thousands of developers. It releases early and often, and downloads
average 30,000 per release.
The Hibernate website has a list of Who Uses Hibernate. Commercial Support and Training are available.
Additionally, Gavin King and Christian Bauer recently authored Hibernate in Action. I recommend this book for
learning Hibernate.
Spring Live
iBATIS 216
iBATIS
iBATIS SQL Maps is an open-source persistence framework that allows you to use your model objects with a
relational database. In contrast to Hibernate, you write SQL, much like you would with JDBC. You do this in a
very simple XML file, allowing abstraction of SQL from Java classes. iBATIS is not an O/R Mapper (ORM).
Rather, it is a Data Mapper. In Martin's Fowler's Patterns of Enterprise Application Architecture, he describes
two patterns: Data Mapper and Metadata Mapping. The difference is that ORMs (Metadata Mappers) map
classes to tables; iBATIS (Data Mapper) maps inputs and outputs to an interface (for example, SQL interface to
an RDBMS). An ORM solution works well when you have control of your database. A Data Mapper like iBATIS
works well when the database is heavily normalized and you need to pull from several tables to populate an
object.
iBATIS is the name of a open-source project started by Clinton Begin in 2001. Clinton had a few products, but
none of them gained much recognition until the .NET Pet Shop was released, claiming that .NET was superior to
Java in developer productivity and performance. Microsoft published a paper claiming that .NET's version of
Sun's PetStore was 10 times faster and 4 times more productive. Knowing this wasn't the case, Clinton responded
with JPetStore 1.0 in July 2002. Not only did this application have fewer lines of code and a better design than its
.NET counterpart, but Clinton implemented it over a few weeks in his spare time!
Clinton's goals while writing JPetStore were to argue the points of 1) Good Design, 2) Code Quality and 3)
Productivity. The original .NET Pet Shop had a horrible design with much of the business logic contained in
stored procedures, whereas JPetStore had a clean and efficient persistence framework.
This framework quickly drew the attention of the open-source community. Today, iBATIS refers to the "iBATIS
Database Layer," which consists of a DAO framework and a SQL Map framework. Spring supports the iBATIS
SQL Maps by providing helper classes to easily configure and use them. Furthermore, the Spring project
includes JPetStore as one of its sample applications, rewriting many of its pieces to use Spring features.
In my opinion, iBATIS is a "sleeper project" in the open-source community. Not many folks know about it, but
those who do, really like it. Perhaps the Spring supporting classes will boost its popularity.
iBATIS's license is Apache, which means you can use it freely as long as your end-user documentation states that
your product contains software developed by the Apache Software Foundation. You can modify the code, but
then you can no longer distribute it under the Apache name without permission.
NOTE
iBATIS recently applied to become an Apache top-level project. If accepted, it will likely be renamed iBATIS Data
Mapper.
Spring Live
iBATIS 217
iBATIS is an excellent persistence framework to use with existing or legacy databases. You can easily migrate a
JDBC-based application to iBATIS (most of the work involves extracting the SQL out of Java classes and into
Java files). Not only is iBATIS fast and efficient, but it doesn't hide SQL, which is one of the most powerful and
oldest languages today. Using iBATIS's SQL Maps, developers write SQL in XML files and populates objects
based on the results of those queries. Much like the Spring/Hibernate combination, iBATIS DAOs require very
few lines of code in each method.
• Easy to learn
NOTE
iBATIS's SqlMapClient is similar to Hibernate's Session and JDBC's Connection. Spring's iBATIS
support classes are located in the org.springframework.orm.ibatis and
org.springframework.orm.ibatis.support packages.
Dependencies
For J2SE 1.4, iBATIS has only one third-party dependency: Commons Logging. For J2SE 1.3, see the jar-
dependencies.txt file included in the download. Below are the JARs included in the MyUsers download as part of
iBATIS 2.0.5. Spring supports both iBATIS 1.x and 2.x; the main difference is the word "Client" on the 2.x
classes. This section only covers 2.x.
Spring Live
iBATIS 218
Configuration
To integrate iBATIS you must create a SQL Map for the User object. A SQL Map is an XML file that contains
SQL statements to map a query's inputs and outputs to objects. In order to avoid conflicts among bean names,
rename the applicationContext-hibernate.xml file (in web/WEB-INF) to applicationContext-hibernate.xml.txt.
This will prevent Spring from loading it.
1. In the src/org/appfuse/model directory, create a file named UserSQL.xml with the following contents:
<sqlMap namespace="UserSQL">
<insert id="addUser" parameterClass="org.appfuse.model.User">
insert into app_user (id, first_name, last_name)
values (#id#, #firstName#, #lastName#);
<selectKey resultClass="java.lang.Long" keyProperty="id" >
select last_insert_id();
</selectKey>
</insert>
Spring Live
iBATIS 219
In this file, you can see that different elements (<insert>, <update>, <select>, <delete>) indicate
database operations. Note that each element can optionally specify a parameterClass or
resultClass attribute. Dynamic variables are enclosed in #value# and indicate properties in the
parameterClass.
2. Create a sql-maps-config.xml file in src/org/appfuse/dao/ibatis (you must create this directory) that
indicates the location of the UserSQL.xml SQL Map.
<sqlMapConfig>
<settings enhancementEnabled="true" maxTransactions="5"
maxRequests="32" maxSessions="10"/>
See iBATIS's Documentation (PDF) for more information on the <settings> element. The values
shown here should be sufficient for most applications.
<beans>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost/myusers</value>
</property>
<property name="username"><value>root</value></property>
<property name="password"><value></value></property>
</bean>
<!-- Add bean definitions here -->
</beans>
Spring Live
iBATIS 220
<bean id="sqlMapClient"
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation">
<value>classpath:/org/appfuse/dao/ibatis/sql-map-config.xml
</value>
</property>
</bean>
NOTE
The primary reason you need a bean named "transactionManager" is that the "userManager" bean
references a bean with that name.
package org.appfuse.dao.ibatis;
if (user == null) {
throw new ObjectRetrievalFailureException(User.class, id);
}
Spring Live
iBATIS 221
return user;
}
7. Add a "userDAO" bean definition to applicationContext-ibatis.xml. The example below uses the
autowire attribute rather than specifying dataSource and sqlMapClient properties. When
using autowire, it's a good idea to specify the injected dependencies in a comment.
8. The app_user table created by Hibernate does not allow nulls in the id column. With iBATIS, it's
easiest to insert nulls for primary keys and retrieve the generated id back from the database. To do
this, drop and recreate the app_user table.
a. Login to MySQL by typing mysql -u root -p myusers from the command line.
Spring Live
iBATIS 222
Test It!
If you're developing MyUsers from a previous application, you may need to change build.xml before the tests
will pass. The compile target has the following XML that copies Hibernate mapping files to the build directory.
Run ant clean test -Dtestcase=UserDAO. The output from this test should be similar to the
UserDAOHibernate class.
Caching
iBATIS supports many caching strategies for SQL Maps. To add a caching strategy to the UserSQL.xml file, add
the following <cacheModel> element:
<sqlMap namespace="UserSQL">
<cacheModel id="userCache" type="LRU">
<flushInterval hours="24"/>
<property name="size" value="1000"/>
</cacheModel>
<insert id="addUser" parameterClass="org.appfuse.model.User">
Add a cacheModel attribute to any <select> statements. For example, add it to the "getUser" statement:
Spring Live
iBATIS 223
The "addUser" statement uses a database-specific means to retrieve the generated primary key. The
<selectKey> element allows you to retrieve generated primary keys quite easily.
The problem with this approach is that it's database-specific. Using select last_insert_id() will only
work with MySQL. This shows how the SQL standard allows variations and is not a true standard. A good ORM
tool like Hibernate or JDO does this transparently for you.
NOTE
If you'd like to try switching back to HSQLDB at this point, simply change the <selectKey> statement to call
identity();. Also change the dataSource bean appropriately. Run ant browse and drop/recreate the
app_user table. The following SQL script will help you with the table:
Spring Live
iBATIS 224
As an alternative, use a Spring class to generate the primary key for you. For example, to use the
MySQLMaxValueIncrementer, perform the following steps:
getSqlMapClientTemplate().insert("addUser", user);
logger.info("new User id set to: " + id);
} else {
getSqlMapClientTemplate().update("updateUser", user);
}
}
If you clean and run the UserDAOTest, all your tests should pass.
Spring Live
iBATIS 225
The problem with putting the MySQLMaxValueIncrementer into your class is now you've hard-coded your
DAO to depend on MySQL. Having the select last_insert_id() statement seems more configurable
since it was in XML. Refactor this class to use dependency injection.
2. Change the saveUser() method to use this incrementer. Just delete the three lines that initialize the
incrementer.
<bean id="incrementer"
class="org.springframework.jdbc.support.incrementer.MySQLMaxValueIncremen
ter">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="incrementerName">
<value>user_sequence</value>
</property>
<property name="columnName"><value>value</value></property>
</bean>
Unfortunately, iBATIS doesn't support JDBC 3.0's getGeneratedKeys() method, so you must use one of the
above methods to generate primary keys. Of course, you can also select max(id) and increment the primary
key yourself.
Developers who use iBATIS tend to be very happy with it. It has clean and concise documentation and tutorials.
The documentation is a mere 53 pages and the tutorial is only 9! In my experience, this is a framework you can
learn and use in the same day. iBATIS doesn't have a user mailing list, but it does have help forums. Its
application to be a top-level project at Apache is another favorable indicator.
Spring Live
Spring JDBC 226
Spring JDBC
If you've written JDBC code before, you know that it can be tedious. Not only do you have to set up
Connections, Statements and ResultSets, but you have to close them after you've retrieved your data. Closing
resources in JDBC code is an area that many new developers don't know how to do properly. They tend to forget
to use the finally block, even though it's is a basic JDBC code pattern.
As you've seen in the previous examples, Spring removes the open/close resource responsibility from the
developer. It manages these operations for you, allowing you to write application code rather than infrastructure
code. Spring's JDBC core is an abstraction on top of J2SE's JDBC that allows you to write a minimal amount of
code to retrieve, save and delete your data.
Four packages make up the JDBC abstraction framework: core, datasource, object and support. The
functionality of each package is below:
In this section, you will use JdbcTemplate and JdbcDaoSupport. The names are similar to the Template and
DaoSupport classes you used with Hibernate and iBATIS. This section will not cover SQLException translation
because you rarely have to do it yourself. The classes are used internally and a number of default translations are
built-in for the following databases: DB2, HSQL, SQL Server, MySQL, Oracle, Informix, PostgreSQL and
Sybase. You can find a complete list of error code to exception mappings in the jdbc/support/sql-error-codes.xml
file.
Spring Live
Spring JDBC 227
Similar to Hibernate and iBATIS, Spring JDBC requires you to map results from a query to your POJOs. With
Hibernate and iBATIS, you did this using XML. With Spring JDBC, you must do this programmatically. You
don't necessarily have to map results to objects; you can simply return maps of your data. For instance, the
following code returns a map with each entry as a row:
In most cases, however, you'll want to map these results to a List of objects. To do this with Spring JDBC, use a
MappingSqlQuery class to convert each row of the JDBC ResultSet to an object. The following is an example
of this type of class retrieving a list of users in the MyUsers application:
When extending MappingSqlQuery, you must override the mapRow(ResultSet rs, int rowNum)
method. Use the UsersQuery class to get a list of User objects:
Spring Live
Spring JDBC 228
Mapping ResultSets to objects helps you retrieve data, but it doesn't help you update it. For that, use a
SqlUpdate class to create a reusable object for updating rows. Here is an example:
Object[] params =
new Object[] {user.getId(), user.getFirstName(),
user.getLastName()};
new UserUpdate(getDataSource()).update(params);
getJdbcTemplate().update(
"UPDATE app_user SET first_name = ?, last_name = ? WHERE id = ?",
new Object[] {user.getFirstName(), user.getLastName(), user.getId()});
Spring Live
Spring JDBC 229
Finally, you don't need to implement a full SqlUpdate class; simply declare one in-line within a method:
su.update(params);
JDBC 3.0, which is part of J2SE 1.4, specifies that a JDBC 3.0-compliant driver has to implement the
java.sql.Statement.getGeneratedKeys() method. This method is to retrieve generated keys from
databases (for example, calling select last_insert_id(); from MySQL). The MySQL driver in MyUsers
is JDBC 3.0-compliant.
To take advantage of retrieving generated keys with Spring JDBC, use a KeyHolder as a second parameter to
the SqlUpdate.update() method.
For non-compliant JDBC Drivers, use the DataFieldMaxValueIncrementer strategy as described earlier.
Dependencies
Since Spring JDBC is part of Spring, it has no third-party dependencies like the other libraries. If you only want
to use the JDBC functionality of Spring, use spring-dao.jar (rather than the all-encompassing spring.jar) in your
application.
Spring Live
Spring JDBC 230
Configuration
Spring JDBC has no mapping files or sql maps so it's simple to configure in the MyUsers application.
1. Create an applicationContext-jdbc.xml file in the web/WEB-INF directory and add a bean definition
for "dataSource". Be sure to rename the previous applicationContext-ibatis.xml to
applicationContext-ibatis.xml.txt.
NOTE
You may want to duplicate the iBATIS version of this file and remove the sqlMapClient bean.
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost/myusers</value>
</property>
<property name="username"><value>root</value></property>
<property name="password"><value></value></property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>
package org.appfuse.dao.jdbc;
Spring Live
Spring JDBC 231
.execute(new Object[]{id});
if (users.isEmpty()) {
throw new ObjectRetrievalFailureException(User.class, id);
}
return (User) users.get(0);
}
if (logger.isDebugEnabled()) {
logger.info("user's id is: " + user.getId());
}
} else {
getJdbcTemplate().update("UPDATE app_user SET first_name = ?,
last_name = ? WHERE id = ?",
new Object[] {user.getFirstName(), user.getLastName(),
user.getId()});
}
}
Spring Live
Spring JDBC 232
This class has a lot more lines of code than the previous two. However, it does not require any
configuration files. The UserQuery and UsersQuery classes are inner classes, but could just as
easily be refactored into their own .java files.
Test It!
1. Login to MySQL by typing mysql -u root -p myusers from the command line.
Spring Live
Spring JDBC 233
Spring's JDBC framework is a clean abstraction on top of JDBC. It frees you from opening and closing resources
and works great for batch processing large amounts of data. Spring's reference documentation is a good resource
for learning more about it, and Spring's Data Access Support Forum is a great place to get your questions
answered.
Commercial support for Spring and its JDBC Framework is available through Interface21.
Spring Live
JDO 234
JDO
JDO is a Java standard, which means it was developed as part of the Java Community Process. As a standard, it's
not an actual product, but a specification of how the product should be built. The goals of JDO are described best
in section 1.1 (Overview) of the JDO 2.0 specification.
"There are two major objectives of the JDO architecture: first, to provide application programmers a
transparent Java-centric view of persistent information, including enterprise data and locally stored
data; and second, to enable pluggable implementations of datastores into application servers."
This section covers how to use Spring to configure and use your JDO implementation, which may be from your
app server vendor, or from an open-source provider like JPOX or ObjectWeb's Speedo. These examples use
JPOX because it is the most popular open-source implementation.
NOTE
Spring's JDO support classes are located in the org.springframework.orm.jdo and
org.springframework.orm.jdo.support packages.
JDO handles primary keys (also called object ids) very differently than other persistence options. This is because
it's designed for both object databases and relational databases. In most cases, you'll be working with a relational
database and will therefore want to use application identity. However, if you're prototyping an application, it
might be easier to use database identity and not worry about primary keys. Below is a list of the different identity
types in the JDO specification.
The purpose of the datastore identity type is to let the JDO completely handle the primary key. For example, to
use the User object with type #1, remove the id field and its access method, as JDO does not use them. This
creates its own column (user_id) in the database and its own table (user). The assumption is that you don't
need to know the primary key in your action.
Spring Live
JDO 235
With type #2, you can define a column name that matches the id property in User, but the database won't
populate it when you persist the object. In other words, it won't tell you what the generated primary key is.
If you want to know the primary key, you must use the application identity type. Using this option, you can still
have your tables generated for you (type #3), or you can use MetaData (which is really a mapping file similar to
Hibernate's) to specify a table name (type #4). To use application identity, you must specify a primary key class.
In MyUsers, the UserDAOTest's testSaveUser() method verifies that the primary key was assigned.
dao.saveUser(user);
assertTrue("primary key assigned", user.getId() != null);
log.info(user);
assertTrue(user.getFirstName() != null);
}
Because of this, the JDO implementation of UserDAO will use application identity.
Dependencies
This example uses JPOX version 1.1.0-alpha-2, which is an implementation of JDO 2.0. Below are the JARs
included in the MyUsers download as part of JPOX.
Spring Live
JDO 236
Configuration
JDO requires a little more setup than the other frameworks. You must add an extra step to the build process that
enhances your persistable objects. To avoid conflicts with the previous section on iBATIS, rename the
applicationContext-jdbc.xml file (in web/WEB-INF) to applicationContext-jdbc.xml.txt. This will prevent it from
being loaded.
NOTE
The JPOX implementation for this example is an alpha release of JDO 2.0. The 2.0 specification has an early draft
available and will soon have a community review. After that, you a lot more vendors will be implementing JDO 2.0
solutions. The enhancement step described here will likely be replaced by a byte-code enhancing solution like
cglib.
1. Open the build.xml file in MyUsers and add the following "enhance" target just before the "test"
target.
Spring Live
JDO 237
2. Create a MetaData file to map the User object to the app_user table. In the org/appfuse/model
directory, create a file named package.jdo with the following contents:
<?xml version="1.0"?>
<!DOCTYPE jdo PUBLIC
"-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"
"http://java.sun.com/dtd/jdo_2_0.dtd">
<jdo>
<package name="org.appfuse.model">
<class name="User" identity-type="application"
objectid-class="org.appfuse.model.PrimaryKey"
table="app_user">
<field name="id" primary-key="true"/>
<field name="firstName" persistence-modifier="persistent">
<column name="first_name" jdbc-type="VARCHAR" length="50"/>
</field>
<field name="lastName" persistence-modifier="persistent">
<column name="last_name" jdbc-type="VARCHAR" length="50"/>
</field>
<extension vendor-name="jpox" key="use-poid-generator"
value="true"/>
</class>
</package>
</jdo>
Most of the items in this class are self-explanatory. You can see where the table name, columns and
sizes are set, and how each field is defined as a column in the User class. Note that the package.jdo
file can hold metadata for all the classes in the org.appfuse.model package (defined by the
<package> element). You may also notice the following line towards the bottom:
This is an indicator to use a sequence table for generating primary keys. There are many ways to
control how primary keys are generated. For more information, please see JPOX's documentation.
NOTE
When JPOX has fully implemented JDO 2.0, this extension will likely be replaced.
3. In order to copy and package the package.jdo file with the war, modify the "compile" target in
build.xml to include *.jdo files:
Spring Live
JDO 238
4. The <class> element of the package.jdo file has an objectid-class attribute. This specifies a
primary key class and is required with an application identity. Create a PrimaryKey.java class in
src/org/appfuse/model and populate it with the code below:
package org.appfuse.model;
public PrimaryKey() {}
A primary key class has many requirements as defined by the JDO spec. They are as follows:
• The class must provide a String constructor to return an instance that is equal to an instance
returned by the toString() method.
• The class must implement equals(), hashCode() and toString() methods. In the previous
PrimaryKey class, this is handled via reflection in the BaseObject class.
Spring Live
JDO 239
5. Run ant clean enhance to see the metadata enhance the org.appfuse.model.User class.
<bean id="persistenceManagerFactory"
class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean">
<property name="jdoProperties">
<props>
<prop key="javax.jdo.PersistenceManagerFactoryClass">
org.jpox.PersistenceManagerFactoryImpl
</prop>
<prop key="javax.jdo.option.ConnectionDriverName">
com.mysql.jdbc.Driver
</prop>
<prop key="javax.jdo.option.ConnectionUserName">root</prop>
<prop key="javax.jdo.option.ConnectionPassword"></prop>
<prop key="javax.jdo.option.ConnectionURL">
jdbc:mysql://localhost/myusers
</prop>
<prop key="org.jpox.autoCreateSchema">true</prop>
<prop key="org.jpox.validateTables">false</prop>
<prop key="javax.jdo.option.NontransactionalRead">
Spring Live
JDO 240
true
</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jdo.JdoTransactionManager">
<property name="persistenceManagerFactory">
<ref bean="persistenceManagerFactory"/>
</property>
</bean>
8. Create a UserDAOJdo.java class in src/org/appfuse/dao/jdo (you must create this directory). This
class extends JdoDaoSupport and implements UserDAO.
package org.appfuse.dao.jdo;
Spring Live
JDO 241
Test It!
Testing this class isn't as simple as previous examples. JDO requires a transaction for any write operations,
unless you specifically indicate that it doesn't need one. If you run ant test -Dtestcase=UserDAO, you will
see a stack trace in your console complaining that a transaction isn't active.
Figure 7.5: Stack trace, result of the ant test -Dtestcase=UserDAO test
Spring Live
JDO 242
The JDO spec says you can turn this off using by setting javax.jdo.option.NontransactionalWrite to
true, but JPOX doesn't support this (yet). In reality, you do want the save operation to participate in a transaction.
If you don't have an active transaction, you must create a new one. That's why the userManager bean has a
PROPOGATION_REQUIRED transaction attribute on its save* methods.
In the normal course of the application, the save methods in the UserManager will participate in a transaction,
so the only issue is in the UserDAOTest. To prove the issue is with this class and not the UserDAOJdo, run ant
test -Dtestcase=UserManager.
The easiest way to fix this is to override the definition of userDAO and wrap it with declarative transactions.
1. Create a file named applicationContext-test.xml in test/org/appfuse/dao. Put the following XML into
this file:
<beans>
<!-- userDAO for testing UserDAOJdo -->
<bean id="userDAO"
class="org.springframework.transaction.interceptor.TransactionProxyFactor
yBean">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="target">
<bean class="org.appfuse.dao.jdo.UserDAOJdo"
autowire="byName"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
Spring Live
JDO 243
2. Change the setUp() method in UserDAOTest to load this file before grabbing the userDAO.
3. Improve the "compile" target in build.xml to copy this file into the test classpath.
Spring Live
JDO 244
Figure 7.6: Successful build, result of the ant test -Dtestcase=UserDAO test
Another approach to using transactions in your unit tests is available on the Spring Forums.
Caching
Spring Live
JDO 245
JDO is a standard, so unlike the other persistence options, it's likely that many vendors will create JDO-
compliant products. Many good books on JDO are available. As you may have noticed from this short tutorial,
it's not as refined as the other options; it requires an enhancement step that the other frameworks don't. The JPOX
project has user forums, good documentation and seems to be one of the few that offers JDO 2.0 support.
Kodo JDO is a commercial implementation of JDO by SolarMetric. Along with their product, they offer
commercial support and training. They also have a sample app for download that uses Spring and Kodo JDO.
For the past year, there's been competition in the J2EE Community between the JDO 2 Expert Group and the EJB
3 Expert Group. Both groups are implementing persistence based on ORM, but neither wanted to cooperate with
the other. The EJB 3 group is basing much of its work on Hibernate, while JDO's Expert Group is simply trying
to improve its first standard to be friendlier for relational database.
In a surprising turn of events, it was recently announced that the EJB 3 and JDO 2 groups would begin
cooperating to define a new, unified POJO persistence model for Java in the J2EE 5.0 time frame. This will most
likely result in a new standard for O/R mapping. Then both groups will use this standard to define their
mappings.
Spring Live
OJB 246
OJB
ObJectRelationalBride (OJB) is an Apache project that is most similar to Hibernate. In fact, its description is
quite similar: an O/R mapping tool that allows transparent persistence for Java Objects against relational
databases. Because of this similarity, this chapter doesn't describe how it works. You can find a complete feature
list on OJB's website.
This section explains how to configure and use OJB with Spring.
NOTE
Spring's OJB support classes are located in the org.springframework.orm.ojb and
org.springframework.orm.ojb.support packages.
Dependencies
This example uses OJB version 1.0.0. Below are the JARs included in the MyUsers download as part of OJB.
Spring Live
OJB 247
Configuration
The first step to integrating OJB is to create a repository file that maps objects to tables.
1. Create a new file named repository.xml and put it the src/org/appfuse/model directory.
<descriptor-repository version="1.0">
<jdbc-connection-descriptor jcd-alias="dataSource"
default-connection="true" useAutoCommit="1" platform="MySQL">
<sequence-manager
className="org.apache.ojb.broker.util.sequence.SequenceManagerNativeImpl"
/>
</jdbc-connection-descriptor>
The first part of this file describes the JDBC connection information (<jdbc-connection-
descriptor>). The platform helps OJB figure out how it should retrieve primary keys, so it's
important that it match your database. The jcd-alias attribute points to a "dataSource" bean
that you need to define.
<beans>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost/myusers</value>
</property>
<property name="username"><value>root</value></property>
Spring Live
OJB 248
<property name="password"><value></value></property>
</bean>
</beans>
<bean id="transactionManager"
class="org.springframework.orm.ojb.PersistenceBrokerTransaction
Manager"/>
<bean id="ojbConfigurer"
class="org.springframework.orm.ojb.support.LocalOjbConfigurer"/>
5. Configure OJB to recognize Spring's "dataSource" bean. Do this by overriding a few settings in
the default OJB.properties.
a. Download the default OJB.properties file and put it in web/WEB-INF/classes. Be sure it's named
OJB.properties.
repositoryFile=org/appfuse/model/repository.xml
ConnectionFactoryClass=org.springframework.orm.ojb.support.LocalDataSo
urceConnectionFactory
ObjectCacheClass=org.apache.ojb.broker.cache.ObjectCachePerBrokerImpl
Spring Live
OJB 249
6. Create a UserDAOOjb.java class in src/org/appfuse/dao/ojb (you must create this directory). This
class extends PersistenceBrokerDaoSupport and implements UserDAO.
package org.appfuse.dao.ojb;
Spring Live
OJB 250
8. The app_user table created by JDO does not allow nulls in the id column. With OJB, it's easiest to
insert nulls for primary keys and retrieve the generated id back from the database. To do this, drop
and recreate the app_user table.
a. Login to MySQL by typing mysql -u root -p myusers from the command line.
Test It!
Before you run the UserDAOTest, change its setUp() method so it doesn't override the userDAO you just
created. Below is a setUp() method that eliminates loading the transaction-wrapped DAO for JDO.
You can also simply change the class attribute of the target bean in applicationContext-test.xml:
<property name="target">
<bean class="org.appfuse.dao.ojb.UserDAOOjb"/>
</property>
Run ant test -Dtestcase=UserDAO. If you've used the first option (commenting out lines in setUp()),
you'll see this warning: "No running tx found." The second option will eliminate this warning.
Caching
OJB 1.0 was released on June 30, 2004. It's an Apache project with a fair amount of real-world implementations.
For support, your best option is to use its mailing lists. The OJB Website also has excellent documentation.
Spring Live
Summary 251
Summary
This chapter explored the different persistence options that Spring supports: Hibernate, iBATIS SQL Maps, its
own JDBC abstraction, JDO and OJB. You saw how Spring configures each one in the MyUsers application. It is
my opinion that Hibernate and iBATIS are the best tools to use for persistence.
Hibernate makes it easy to persist objects using simple mapping files, and you can even use XDoclet to generate
the mapping files for you. iBATIS is ideal if you have an existing, complicated schema or legacy database - or if
you simply prefer writing your own SQL.
This chapter has shown how powerful Spring is as a configuration tool and loose-coupling promoter. Of the five
persistence frameworks, you only had to change the UserDAOTest for one. Not only does Spring promote good
design, it makes good design a lot easier to use.
Spring Live
Chapter
8
This chapter explains how to use test-driven development to create high-quality, well-tested, Spring-based
applications. You will learn how to test your components using tools like EasyMock, jMock and DBUnit. For the
Controllers, you will learn how to use Cactus for in-container testing, and Spring Mocks for out-of-container
testing. Lastly, you will learn how to use jWebUnit and Canoo's WebTest for testing the web interface.
Spring Live
Overview 253
Overview
Using test-driven development is the best way to produce high-quality code. Not only do you think about how
your application is supposed to work as you're writing the test (from your user's APIs perspective), but you
actually design the contracts that your application is supposed to fulfill. If your tests don't pass as you expect,
then you have a problem with either your test or your implementation. If your tests do as expected in that
contract, you must modify your implementation to produce that expectation.
Test-first development is where developers write tests and ensure they fail
before proceeding. They continue to take baby steps to get the test to pass/
fail as they develop. The diagrama on the left illustrates this technique.
While test-driven and test-first techniques are similar, the most important
aspect is that the process of writing tests forces you to consider how your
classes behave. Test-first advocates a more rigorous system of small steps.
In this chapter, you will learn how to use testing technologies to develop
Spring applications faster and more efficiently. By employing test-first
development, you can virtually eliminate the trial-and-error procedures
that are common with developing browser-based applications. You can
also gain confidence in your code and add regular automation to ensure
that no one "broke the build."
a. This diagram is based on one in Scott W. Ambler's Test Driven Development essay.
Spring Live
Overview 254
Spring makes it easy to write testable software. Its IoC Container gives you the freedom to set dependencies on
classes any way you like. You can load Spring's ApplicationContext in your test and use your beans like
your application does, or you can set a bean's dependencies using mock objects. This chapter explores both
techniques and shows you how to use tools like EasyMock and jMock to create mock versions of your classes
on-the-fly. You will also see how to use DbUnit to load data in a database before testing.
NOTE
A mock object is a fake and simplistic version of a real object. For instance, you can use mocks to imitate the
Servlet API so you can run tests outside of a servlet container.
For testing the Controllers in your application, you will learn how to use StrutsTestCase, Spring Mocks and
Cactus. For testing the view layer, you will learn how to use jWebUnit and Canoo WebTest.
NOTE
A future chapter will cover Anthill, CruiseControl and DamageControl. These automated testing systems are
essential for maintaining a high-quality code base.
Spring Live
JUnit 255
JUnit
JUnit has quickly become the de facto standard for TDD in Java. It's an easy framework to use and most modern
IDEs support it. If you don't use an IDE, you probably use Ant, which is also easy to use with a <junit> task. In
this chapter, you will use JUnit to write most of your tests. If you're not using JUnit directly, you'll be using an
extension. For example, DbUnit, Cactus and StrutsTestCase are all JUnit extensions.
A basic JUnit test simply contains a testXXX() method, where XXX is a descriptive name of what the test
does. It's important to use a name that describes the test accurately. For example, if you have an object that allows
results to be filtered, testGetUsersWithNoFiltering() is better than the more generic testGetUsers().
Below is an example that demonstrates a simple JUnit test:
package org.appfuse.dao;
import junit.framework.TestCase;
import org.appfuse.model.User;
Running this test results in a failure because you're getting the last name, rather than the full name. Running your
tests with failures first is important to make sure they meet your expectations. Changing assertEquals() to
the following will yield a passing test:
Optional methods to implement in a JUnit Test include setUp(), tearDown() and main(String[]). Using
setUp() and tearDown() are useful for creating a testing environment, then tearing down that environment
before running the next test. These methods are called before and after each testXXX() method. If you have
test-wide setup procedures, you should implement those in a static initialization block or use JUnit's TestSetup
class.
It is necessary to implement the main(String[]) to run a JUnit test from the command line using the java
command. However, with modern IDE and Ant support, you may never need to implement this method. To prove
it, remove the main(String[]) from the UserDAOTest in MyUsers; your tests will continue to run.
Spring Live
JUnit 256
In the Java Community, using TDD has increasingly become the norm, especially among open-source
developers. The projects with a rich test suite typically gain more credibility from other developers. However,
there is a philosophical difference of how one should do test-driven development. Many advocate the use of
Mock Objects, or mocks, while others think integration testing is good enough.
This chapter often refers to unit tests. You may not agree that it's a unit test, but rather an integration test. J. B.
Rainsberger said it best in JUnit Recipes:
"The testing that programmers do is generally called unit testing, but we prefer not to use this term. It
is overloaded and overused, and it causes more confusion than it provides clarity. As a community,
we cannot agree on what a unit is - is it a method, a class, or a code path? If we cannot agree on what
unit means, then there is little chance we will agree on what unit testing means."
This chapter shows you two types of testing: mock testing and integration testing. Let's take a brief look at the
philosophies behind them.
Mock Testing
Mock testing is the process of isolating your test case to test a single unit of work, most often a class. Using
mocks, you can test your class in isolation, without reliance on any of its dependencies. In the instance of a
business façade (or Manager), you would mock its dependent DAO. This allows you to test the business façade
for its specific functionality, not including the DAO's functionality.
A mock is typically a stub (a minimal test-only class you write yourself) or a dynamic mock. Dynamic mocks are
a slick way of setting expectations on a class or interface. With dynamic mocks, you can create an instance of
your class on-the-fly and write code to set expected behavior. The general rule for using mocks is "only mock
types that you can change," or "only mock APIs that are yours."
Spring Live
JUnit 257
Integration Testing
Integration testing is the process of testing your classes in their normal environment with all of their
dependencies intact. The main disadvantages of integration testing are speed and non-isolated tests. For example,
for a business façade test that depends on its real DAO, you must establish a database connection to retrieve
actual data. By coding and running non-isolated tests, you're writing tests that depend on your environment,
which really has nothing to do with your test.
There is a place for both types of tests. Dynamic mocks are great for team development, where different
developers are responsible for different layers. For instance, if developer Bruce is responsible for the DAO layer
and developer James is responsible for the business logic layer, they have no reason to be dependent on each
other's implementations. Bruce can provide James with interfaces and James can create mocks of these in order
to test his code in isolation. Then it doesn't matter if Bruce takes longer to deliver his DAO implementation.
If you're working on a small team or alone, integration tests will probably suffice. Of course, this means you will
have to develop your application in a specific order, where dependencies are coded first so implementations that
depend on them can run properly.
In my opinion, a properly designed application has integration tests in the database layer, mock tests in the
business and web layers and integration tests in the UI. Of course, this all depends on how complicated each
layer is. For the database layer, it's important to test that its interaction with your underlying datastore works.
There's no reason to mock the database connection or Spring-supplied Template. These are framework-level
components and shouldn't be a concern of your application. Once your DAOs return the expected results, it's
pointless to verify this again in the business layer. Therefore, you can mock the DAOs in the business (or service)
layer and concentrate on testing your business logic.
In the web tier, particularly with MVC Controllers (regardless of web framework), it's helpful to mock a J2EE
container environment. Mocks can impersonate requests, responses and other servlet-related classes, allowing
you to run your tests out-of-container, which is often much faster. Using real business objects vs. mocks in your
Controller tests is a matter of taste at that point. Testing them in isolation is usually a good idea, since the
important functionality in Controllers is controlling which views are returned for specific inputs.
The UI layer is the best place to test the full stack of integrated functionality. Testing the UI layer involves
shadowing the actions a user would take when using your application: clicking links, entering data, clicking
buttons, etc. These tests don't talk to your classes directly, but perform browser actions. They typically pull
HTML from a URL, parse it and submit request parameters based on your test code. The frameworks for UI
testing make this very simple so you can write one-liners to submit forms, enter data and verify page titles or
text.
Spring Live
JUnit 258
Now that you understand the different strategies of testing, let's look at some specific examples in the context of
the MyUsers application. In this chapter, like previous ones, you can follow along and do the examples as you
go. The easiest way to do this is to download the MyUsers Chapter 8 bundle from http://sourcebeat.com/
downloads. This project tree is the result of Chapter 7 exercises. It also contains all the JARs you will need in
this chapter in its web/WEB-INF/lib directory. You can also use the application you've been developing in
previous chapters. If you go this route, download Chapter 8 JARs from http://sourcebeat.com/downloads. Each
section notes new JARs you might need, so you can use this chapter as a reference for integrating these principles
into your own applications.
In Chapter 7, you integrated a number of persistence strategies. If you completed all the exercises in the chapter,
your MyUsers application should be using OJB, since that was the last strategy explored. If you'd like to change
your application to use a different framework, follow the specific section for that framework in Chapter 7. Below
are a few things to remember if you decide to switch from OJB to something else:
• JDO requires modifications to build.xml so "enhance" is called by the "test" and "war"
targets. You may also have to change UserDAOTest so it loads applicationContext-test.xml.
• You may need to drop the app_user table and recreate it (see the instructions for each DAO
type).
If you have issues with switching DAOs, feel free to e-mail me at [email protected] or enter an issue in the
Spring Live Issue Tracker provided by Atlassian.
Spring Live
Testing the Database Layer 259
As mentioned earlier, it's best to test your DAOs directly against your database. This ensures that your
persistence framework works as you want. Testing a database layer involves retrieving, adding, updating and
removing data using your application-specific classes. To review the UserDAOTest you created in Chapter 2:
package org.appfuse.dao;
dao.saveUser(user);
assertTrue("primary key assigned", user.getId() != null);
log.info(user);
assertTrue(user.getFirstName() != null);
}
Spring Live
Testing the Database Layer 260
user.setFirstName("Bill");
user.setLastName("Joy");
dao.saveUser(user);
assertTrue(user.getId() != null);
assertTrue(user.getFirstName().equals("Bill"));
if (log.isDebugEnabled()) {
log.debug("removing user...");
}
dao.removeUser(user.getId());
try {
user = dao.getUser(user.getId());
fail("User found in database");
} catch (DataAccessException dae) {
log.debug("Expected exception: " + dae.getMessage());
assertTrue(dae != null);
}
}
}
This class is an integration test because it depends on Spring's ApplicationContext to retrieve the UserDAO
implementation class, with all its dependencies set. The ApplicationContext is initialized using
ClassPathXmlApplicationContext in the BaseDAOTestCase (which this class extends).
public BaseDAOTestCase() {
String[] paths = { "/WEB-INF/applicationContext*.xml" };
ctx = new ClassPathXmlApplicationContext(paths);
}
public BaseDAOTestCase() {
String[] paths = { "web/WEB-INF/applicationContext*.xml" };
ctx = new FileSystemXmlApplicationContext(paths);
}
Spring Live
Testing the Database Layer 261
TIP
It is possible to store your bean definitions in .properties files, too. Read more about
PropertiesBeanDefinitionReader.
<classpath>
<path refid="classpath"/>
<path location="${build.dir}/classes"/>
<path location="${test.dir}/classes"/>
<path location="web/WEB-INF/classes"/>
<path location="web"/>
</classpath>
Spring Live
DbUnit 262
DbUnit
DbUnit is a testing framework useful for putting a database in a known state before running tests. DbUnit has a
single JAR that you need in your classpath: dbunit-2.1.jar. This is included in the MyUsers download and
Chapter 8 JARs download. You can also download it from SourceForge.
In the UserDAOTest, a record is added in the testGetUsers() method. Refactor this class to use DbUnit to
load a record before running the test. You must first create some sample data with which to populate the
database. A number of datasets are available to use with DbUnit, but the easiest is the XmlDataSet.
TIP
To export data from an existing database, use DbUnit's Ant tasks.
1. Create a sample-data.xml file in test/data (you must create this directory). Populate it with the
following XML:
2. With DbUnit, you can extend its DatabaseTestCase or use your own TestCase. It's easier to use
your own TestCase than to change the parent class. Add the following two variables as member
variables of the BaseDAOTestCase class:
Spring Live
DbUnit 263
3. Create a setUp() method to clear out the database for any tables specified in the sample-data.xml
file.
4. Add logic in the tearDown() method to close the connection and delete any added data.
5. Alter UserDAOTest so the setUp() and tearDown() methods call super.setUp() and
super.tearDown().
NOTE
If you'd prefer not to explicitly grab beans from the context, use Dependency Injection in your unit tests. Explicitly
grabbing your beans allows you to use whatever naming convention you like for the variables. In Spring 1.1.1, an
AbstractDependencyInjectionSpringContextTests class was added for dependency injection in your
tests.
Spring Live
DbUnit 264
6. Change the getUsers() method to verify only one record is in the database, and it's the one DbUnit
entered.
Running ant -Dtestcase=UserDAO should yield "BUILD SUCCESSFUL," and the app_user table should
be empty in the database. In performance comparisons, this test takes a mere 0.21 seconds longer than the
previous one (0.056 vs. 0.77).
Ant is another way to use DbUnit in your application. You can configure a target that loads the database before
running your tests. AppFuse uses this strategy; see its build.xml file and the db-load target for an example. This
is useful because you can run a block of tests with pre-defined data, rather than reloading the data for each test.
The advantage of using this strategy is your suite of tests will run faster since the database isn't reset before each
test is run. An advantage of using DbUnit directly in your classes is you can run your tests in an IDE without any
Ant dependencies.
Database Switching
Using Spring to configure your database connection makes it easy to swap the database with which you test your
code. For example, you could use an HSQL database for unit tests, and switch to a DB2 database for production.
In most cases, it's a good idea to test against your production database, but if your developers use PowerBooks,
DB2 doesn't have an install for OS X.
One way to configure database switching is to use two properties files. Most of Spring's sample applications (in
CVS/samples) use this strategy. By defining a "propertyConfigurer" bean (with class
PropertyPlaceholderConfigurer), you can configure your "dataSource" bean's properties with ant-
style placeholders: ${...}. This allows you to refer to test settings and production settings. It also allows you to
share your database configuration between your Spring config files and your build.xml file. To configure a
properties file with your database settings in MyUsers, complete the following steps:
Spring Live
DbUnit 265
1. Modify your current DAO strategy's XML file. This should be applicationContext-ojb.xml. Replace
the "dataSource" bean's property values with Ant-style properties:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
</bean>
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/myusers
jdbc.username=root
jdbc.password=
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholder
Configurer">
<property name="location">
<value>classpath:jdbc.properties</value>
</property>
</bean>
To specify multiple properties files for loading, use the "locations" property instead of
"location".
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
Spring Live
DbUnit 266
<property file="build.properties"/>
<property file="web/WEB-INF/classes/jdbc.properties"/>
NOTE
Another strategy (used in AppFuse) is to use a manually configured DataSource when testing and a JNDI
DataSource when in production.
Spring Live
DbUnit 267
If you have DAOs with methods that require transactions to be wrapped around them, you can define beans
specifically for unit tests. This was done in Chapter 7 for the JDO DAO because JDO requires that any
makePersistent() calls are inside a transaction. Using Spring's declarative transactions in your test is much
easier than using Transactions. If you're still using the OJB DAO, you may have received several warning
messages about no running transaction.
Spring Live
DbUnit 268
To fix this, create a bean definition just for testing, and override the regular userDAO by loading this definition
in UserDAOTest.
<beans>
<!-- userDAO for testing UserDAOJdo -->
<bean id="userDAO"
class="org.springframework.transaction.interceptor.TransactionProxy
FactoryBean">
<property name="transactionManager"><ref
bean="transactionManager"/></property>
<property name="target">
<bean class="org.appfuse.dao.ojb.UserDAOOjb" autowire="byName"/
>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
2. Refactor the setUp() method of UserDAOTest to load this file before each test.
3. Now the UserDAOTest should run (ant test -Dtestcase=UserDAO) without the transactions
warning.
NOTE
You won't use this test configuration in the UserManagerTest since the "userManager" bean is already
wrapped in transactions.
Spring Live
DbUnit 269
As mentioned previously, you can define two different "dataSource" beans for testing and production. The
production bean will likely be a DataSource that's looked up via JNDI. An example definition is below:
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/myusers</value>
</property>
</bean>
To use this bean in your tests, use Spring's JNDI mocks to bind this to a simple JNDI implementation.
try {
SimpleNamingContextBuilder builder =
SimpleNamingContextBuilder.emptyActivatedContextBuilder();
To try this, put the JNDI DataSource in applicationContext-test.xml and add the context build code (above) to the
beginning of the static initialization block in BaseDAOTestCase.java. For the DriverManagerDataSource's
properties above, you could also use a ResourceBundle to pull the information from jdbc.properties.
To use a JNDI DataSource in Tomcat, replace the "dataSource" bean in your DAO context file with the JNDI
one configured in your container appropriately. Below are instructions for Tomcat 5.x:
Spring Live
DbUnit 270
<resource-ref>
<description>Connection Pool</description>
<res-ref-name>jdbc/myusers</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Now if you run ant deploy and stop/start Tomcat, everything should work as before.
Spring Live
DbUnit 271
To speed up your unit tests, especially the ones that load an ApplicationContext, alter your tests so the
context is only loaded once for the entire test. In the previous examples, the context is loaded by setUp() before
running each testXXX() method. You can do this using one of two ways:
To use either of these methods, you must first change some variables to be static.
3. The second static initialization block is easier to implement; simply add the following block to the
beginning of BaseDAOTestCase.
static {
String[] paths = {"/WEB-INF/applicationContext*.xml",
"/org/appfuse/dao/applicationContext-test.xml"};
ctx = new ClassPathXmlApplicationContext(paths);
}
4. Remove the block to load the test context in the UserDAOTest.setUp() method.
Spring Live
DbUnit 272
Now running ant test -Dtestcase=UserDAO should be a bit faster. Right now, the difference is negligible,
but it will greatly improve as your context files grow in size.
While using TestSetup is easy, I've chosen not to include it in this chapter. Using a static initialization block
works great and it's significantly faster than TestSetup. Using TestSetup vs. static initialization blocks is a
matter of personal preference. Other alternatives are available, such as Cedric Beust's TestNG framework. For
more information on TestSetup, and how JUnit initializes objects for each test, see Martin Fowler's website.
Spring Live
Testing the Service Layer 273
In the last section, you learned how to test your DAO implementations against a database. This is important for
your database layer to verify that what you put into the database is the same thing that comes out of it. However,
once you have your DAOs tested and verified, you don't have much reason to use them in your service layer
tests.
A nice feature of Spring is that it not only promotes interface-based design, but it doesn't put any restrictions on
how you write your unit tests. As the last section demonstrates, you can easily load you context files in your tests
and interact with your beans just as you would in a production environment. This is an important concept that is
often overlooked. Many folks get caught up in mocking everything, which is great for test isolation, but it ignores
testing how the application's layers interact with each other.
You should always try to use integration tests first (load the context in your test and operate on your beans
accordingly). This is because it's easy to set up and it tests your real code, not a fake object. However, this
approach breaks down in two areas:
• You're working on a team and each developer is responsible for different layers. You don't want to
wait for an implementation before writing tests for a parent layer.
• Your application context files grow so large that it takes several seconds to load them.
To work around these issues, use mocks to simulate dependencies of your class.
Spring Live
Testing the Service Layer 274
This section refactors the UserManagerTest to use mocks for the UserDAO. There are two types of mocks:
stubs, which you create yourself, and dynamic mocks, which allow you to implement classes on-the-fly. In the
process of creating dynamic mocks, the developer is responsible for setting expected results when methods are
called. Dynamic mocks are typically easier to use and don’t litter your source tree with a second set of classes to
maintain.
EasyMock
EasyMock is a project that provides mocks for interfaces in JUnit tests by generating them on-the-fly using
Java's proxy mechanism. A class extension is also available to mock implementation classes. To use EasyMock
in a test, perform the following steps:
Spring Live
Testing the Service Layer 275
control.setVoidCallable();
This code never uses an implementation of the UserDAO. Rather, a mock is created from its interface and a
dynamic implementation is created using EasyMock.
In the MyUsers application, the UserManagerTest integration test already exists for the middle tier. This class
loads applicationContext.xml files and uses the beans as you would normally in your application. Refactoring
this class to use EasyMock is easy and will run much faster in the long run. You might as well keep your original
UserManagerTest around, since it will continue to serve as a nice integration test.
Create a new class named UserManagerEMTest in test/org/appfuse/service. This class does not contain any
Spring dependencies in this example and has the same basic functionality as UserManagerTest. The main
difference is this class is isolated (thanks to mocks) and will run very quickly. It has the following code:
package org.appfuse.service;
Spring Live
Testing the Service Layer 276
user = mgr.saveUser(user);
assertEquals(user.getFullName(), "Easter Bunny");
control.verify();
if (log.isDebugEnabled()) {
log.debug("removing user...");
}
mgr.removeUser(userId);
control.verify();
try {
// reset to record state
control.reset();
control.expectAndThrow(mockDAO.getUser(user.getId()),
new ObjectRetrievalFailureException(
User.class, user.getId()));
// switch to playback
control.replay();
user = mgr.getUser(userId);
control.verify();
fail("User '" + userId + "' found in database");
} catch (DataAccessException dae) {
log.debug("Expected exception: " + dae.getMessage());
assertNotNull(dae);
}
}
}
Spring Live
Testing the Service Layer 277
Run this test using ant test -Dtestcase=UserManagerEM. The speed difference is substantial between this
test and UserManagerTest. In my meager tests, the UserManagerEM is 0.172 seconds, while the
UserManagerTest is 1.59 seconds.
jMock
jMock is another open-source project that produces dynamic mocks. jMock has a slightly different approach to
mocking than EasyMock. It requires you to subclass MockObjectTestCase. This class performs method
invocation verification and provides the syntactic sugar that makes jMock tests easy to read. To use jMock in a
test, perform the following steps:
4. Verify expectations.
// Set dependencies
mgr.setValidator(new UserValidator());
mgr.setUserDAO((UserDAO) mockDAO.proxy());
}
// Verify expectations
mockDAO.verify();
}
Spring Live
Testing the Service Layer 278
jMock's syntax is a bit cleaner, but its expectations syntax is more complicated. A nice feature of jMock is the
ability to write custom stubs to simulate side-effects of calling methods. For instance, in the
UserDAOHibernate.saveUser(), the User object is assigned an id by Hibernate (if one doesn't exist).
To mimic this same functionality when mocking UserDAO, create an AssignIdStub class in test/org/appfuse/
service. The code for this class contains logic that merely sets a random Long as the id on the User object.
package org.appfuse.service;
return null;
}
}
Spring Live
Testing the Service Layer 279
package org.appfuse.service;
user = mgr.saveUser(user);
// verify expectations
mockDAO.verify();
assertTrue(user.getId() != null);
if (log.isDebugEnabled()) {
log.debug("removing user...");
}
mockDAO.expects(once()).method("removeUser")
.with( eq(new Long(userId)) );
mgr.removeUser(userId);
// verify expectations
mockDAO.verify();
Spring Live
Testing the Service Layer 280
try {
// set expectations
Throwable ex =
new ObjectRetrievalFailureException(
User.class, user.getId());
mockDAO.expects(once()).method("getUser")
.with( eq(new Long(userId)))
.will(throwException(ex));
user = mgr.getUser(userId);
// verify expectations
mockDAO.verify();
fail("User '" + userId + "' found in database");
} catch (DataAccessException dae) {
log.debug("Expected exception: " + dae.getMessage());
assertNotNull(dae);
}
}
Run this test with ant test -Dtestcase=UserManagerJM. The test should run as fast as the EasyMock test,
and its output should resemble the following screenshot.
Spring Live
Testing the Service Layer 281
This section has shown you how to do true unit tests on your business façades and how using Mocks in your
testing strategy can greatly reduce the amount of time for tests to run.
In these tests, the UserManagerImpl class was instantiated with its default constructor, and a mock object
simulated its interactions with the UserDAO. Spring was not in any of the mock tests, proving that it's non-
evasive. Spring's JavaBeans philosophy for setter-based dependency injection made it easy to set whatever
dependencies you wanted.
If you'd like to learn more about the differences between jMock and EasyMock, the jMock website has a detailed
comparison. In my experience, jMock is more flexible, but its API can be a bit difficult to understand. Since it
requires you extend its MockObjectTestCase, you cannot always use it. jMock has a much more active
mailing list, but EasyMock enjoys the benefit of being released first and used more widely.
Spring Live
Testing the Web Layer 282
Testing the web layer in an application is the most important part of an application test suite. By properly testing
your Controllers, you verify the control-flow of your application and determine if inputs will return the expected
outputs. In Spring MVC, inputs are requests and outputs are ModelAndView objects returned by Controller
methods. Spring Mocks allow you to easily mimic requests and verify results. You can test in-container with
Cactus.
While testing Controllers is important, testing the view by interacting with the User Interface (UI) is usually the
best way to be sure your application works properly. You may have a Quality Assurance (QA) department that
does this for you. However, it's easy to write tests for the UI using jWebUnit and Canoo WebTest, both of which
are simplifications of HttpUnit. HttpUnit emulates the relevant portions of a browser's behavior, including form
submission, JavaScript, basic http authentication, cookies and automatic page redirection. It allows Java test
code to examine returned pages as either text, XML DOM, or containers of forms, tables, and links. Tests that
interact with the UI are great integration tests to verify all layers of your application. Of course, they won't verify
that colors, fonts and position are correct; that will always need a human eye to test.
You can automate all of the tests mentioned in this chapter. That is, you can test them without human interaction.
This ability enables a healthy continuous build mechanism that you will explore in the Automated Testing
section.
Testing Controllers
In Chapter 4, you created JUnit tests for the two main Controllers in MyUsers: UserController and
UserFormController. In this section, you will refactor those tests to be independent of Spring's
ApplicationContext. Instead, you will use EasyMock and jMock to mock your Controller's dependencies.
You will create Cactus tests to see how to test Controllers in-container. You will learn how to use mocks in many
of these tests to isolate your tests and make them true unit tests (they only test a single class). You will also learn
how to write a test for the FileUploadController and test the e-mail it sends using Dumbster.
NOTE
Dumbster is a fake SMTP server for unit and system testing applications that send e-mail messages. It responds to
all standard SMTP commands but does not deliver messages to the user. The messages are stored within the
Dumbster for later extraction and verification.
Spring Live
Testing the Web Layer 283
Spring Mocks
Spring Mocks is a general name for the mock classes that Spring distributes for testing JNDI and Controllers.
These classes were originally used internally by Spring to test the framework itself. As developers tested Spring's
Controllers with mocks (specifically, Mock Object's Servlet API), it became apparent that Mock Objects did not
provide rich enough functionality. In June 2002, Spring developers realized that their mocks were actually quite
feature-rich, and they began releasing them as part of the distribution (since 1.0.2).
One of the best things about Spring mocks is that the classes are for mocking the Servlet API and aren't tied to
Spring at all. This means that you could easily use them to test other Controllers, such as WebWork. In my
experience, Struts, Spring and WebWork have the best support for testing. Struts has it via StrutsTestCase, Spring
has its mocks and WebWork can be tested using regular JUnit tests. A later chapter will implement WebWork,
Tapestry and JSF in MyUsers. Unlike the request/response web frameworks, Tapestry and JSF are component-
and event-driven web frameworks. They often depend on JavaScript and currently don't have a lot of support for
unit testing their Controller components.
Spring Live
Testing the Web Layer 284
Testing Controllers
package org.appfuse.web;
This class makes use of two Spring mocks: MockHttpServletRequest and MockServletContext. It loads
context files and grabs the beans from the initialized XmlWebApplicationContext. This works well, but is
more of an integration test, rather than a unit test. This is because Spring has already wired the
UserController and all its dependent objects. In order to create a unit test, you must remove any trace of an
ApplicationContext and set the dependencies manually in the unit test.
Spring Live
Testing the Web Layer 285
package org.appfuse.web;
ModelAndView mav =
c.handleRequest((HttpServletRequest) null,
(HttpServletResponse) null);
Map m = mav.getModel();
assertNotNull(m.get("users"));
assertEquals(mav.getViewName(), "userList");
Spring Live
Testing the Web Layer 286
To run this test, execute ant test –Dtestcase=UserControllerEM. Your console output should resemble
the screenshot below.
Spring Live
Testing the Web Layer 287
This test runs much faster than the previous one. To compare, run ant test –
Dtestcase=UserControllerTest and compare the "Time elapsed" value. On my 1.33Mhz/512RAM
PowerBook, there's quite a difference: 0.223 seconds for the mocked test and 6.455 seconds for the non-mock
version.
Figure 8.5: Showing the time elapsed for the non-mocked version
You've created an EasyMock version of UserControllerTest; now create a jMock version. Create a new
UserControllerJMTest.java class in test/org/appfuse/web. This class extends jMock's
MockObjectTestCase.
package org.appfuse.web;
Spring Live
Testing the Web Layer 288
.will(returnValue(new ArrayList()));
ModelAndView mav =
c.handleRequest((HttpServletRequest) null,
(HttpServletResponse) null);
Map m = mav.getModel();
assertNotNull(m.get("users"));
assertEquals(mav.getViewName(), "userList");
// verify expectations
mockManager.verify();
}
}
Spring Live
Testing the Web Layer 289
Testing FormControllers
Testing form classes, which extend SimpleFormController, is much like testing classes that implement
Controller. The major difference is that FormControllers do more; for example, they validate data, set
messages and use request parameters. The test for UserFormController (from Chapter 4) is listed below. It
verifies that no validation errors occur in its testSave() method, and that a success message is set.
package org.appfuse.web;
Spring Live
Testing the Web Layer 290
While this test works, it takes several seconds to run (10.2 on my PowerBook!). Refactoring it to remove Spring
dependencies and use jMock will speed things up dramatically.
package org.appfuse.web;
Spring Live
Testing the Web Layer 291
// verify expectations
mockManager.verify();
}
Spring Live
Testing the Web Layer 292
Errors errors =
(Errors) mv.getModel().get(BindException.ERROR_KEY_PREFIX +
"user");
assertNull(errors);
assertNotNull(request.getSession().getAttribute("message"));
// verify expectations
mockManager.verify();
}
// verify expectations
mockManager.verify();
}
}
In the above class's setUp() method, Spring's StaticApplicationContext provides a convenient way to
add and use beans in unit tests. Its registerSingleton() method registers the messages.properties file for
accessing messages. Without doing this, it would throw a NullPointerException when the FormController
tries to access the "messageSource" bean. You could also use the StaticMessageSource class for adding
messages programmatically in your tests. More information on using these classes in your tests is available in
Spring's StaticApplicationContextTestSuite.
NOTE
Spring's internal tests are an excellent source of information on how to write your own unit tests.
Spring Live
Testing the Web Layer 293
Cactus
This section uses Cactus to test Controllers in their deployed environment. Cactus is an extension of JUnit for
unit testing server-side Java code.
To use Cactus for testing Controllers, modify MyUser's build.xml by adding a "test-cactus" target.
<cactifywar srcfile="${dist.dir}/${webapp.name}.war"
destfile="${dist.dir}/${webapp.name}-cactus.war">
<classes dir="${test.dir}/classes"/>
<classes dir="test" includes="cactus.properties"/>
<!-- If you use EasyMock or Spring Mocks in a Cactus test
it needs to be included in the WAR -->
<lib dir="web/WEB-INF/lib" includes="*mock.jar"/>
<servletredirector/>
</cactifywar>
<mkdir dir="${test.dir}/data/tomcat"/>
<cactus warfile="${dist.dir}/${webapp.name}-cactus.war"
printsummary="yes" failureproperty="tests.failed">
<classpath>
<path refid="classpath"/>
<path location="${build.dir}/classes"/>
<path location="${test.dir}/classes"/>
</classpath>
<containerset>
<tomcat5x dir="${tomcat.home}" if="tomcat.home"
port="8080" todir="${test.dir}/data/tomcat"/>
</containerset>
<formatter type="xml"/>
<formatter type="brief" usefile="false"/>
<batchtest todir="${test.dir}/data" if="testcase">
<fileset dir="${test.dir}/classes">
<include name="**/*${testcase}*"/>
<exclude name="**/*TestCase.class"/>
</fileset>
</batchtest>
<batchtest todir="${test.dir}/data" unless="testcase">
<fileset dir="${test.dir}/classes">
<include name="**/*Cactus*Test.class*"/>
</fileset>
Spring Live
Testing the Web Layer 294
</batchtest>
</cactus>
<fail if="tests.failed">Cactus test(s) failed.</fail>
</target>
The <cactifywar> task at the beginning of this listing is responsible for modifying the web.xml and WAR files
so Cactus can test it. A <tomcat5x> task is in the middle of this target. This task specifies the container in
which to run the tests. See Cactus's project site for more information on its Ant Integration and the list of
supported containers for the <cactus> task.
Now that you've set up Cactus, create a UserCactusTest.java class in test/org/appfuse/web. This class
extends ServletTestCase. This first version manually creates the UserController and
UserFormController, then sets their necessary dependencies (UserManager and context for
"messageSource" bean). The ApplicationContext for this test is retrieved from the ServletContext,
not initialized from the ClassPathXmlApplicationContext. This is because the test will be running in the
context of the application, and Spring's application context is initialized by the ContextLoaderListener in
web.xml.
package org.appfuse.web;
Spring Live
Testing the Web Layer 295
Create a cactus.properties file in the test directory. Cactus requires the following information to run its servlet
tests:
Another way to write this test is to grab the Controllers directly from the ApplicationContext, where
their dependencies are already set.
...
public class UserCactusTest extends ServletTestCase {
private static Log log = LogFactory.getLog(UserCactusTest.class);
private UserController list = null;
private UserFormController form = null;
Spring Live
Testing the Web Layer 296
Finally, if you choose, you can mock the UserManager with EasyMock. The UserCactusEMTest class below
illustrates this technique. You cannot use jMock with Cactus since both of them require you to extend their
TestCase classes.
package org.appfuse.web;
form.handleRequest(request, response);
Spring Live
Testing the Web Layer 297
.getAttribute("message") != null);
}
Map m = mav.getModel();
assertNotNull(m.get("users"));
assertEquals(mav.getViewName(), "userList");
}
}
The second approach (grab controllers directly) is usually the best approach. Running Cactus tests requires quite
a bit of time since it has to parse/modify the WAR and start/stop the container; therefore, using mocks is unlikely
to increase your test performances. However, they are still helpful for isolating your test in the container.
Spring Live
Testing the Web Layer 298
As promised in Chapter 5, you will now learn how to test the FileUploadController class. This class is
responsible for uploading files and sending a notification e-mail.
package org.appfuse.web;
ctx.setServletContext(servletContext);
ctx.refresh();
fileUpload = (FileUploadController)
ctx.getBean("fileUploadController");
// continued below
Spring Live
Testing the Web Layer 299
Second, setUp() contains a custom MailSender bean that sends e-mail on a non-standard port
(2525; 25 is standard). This is so you can use Dumbster. You will use Dumbster to start/stop an SMTP
server on port 2525 before and after running the FileUploadControllerTest.
NOTE
This example uses port 2525 to avoid conflicts with a server that might be running on port 25.
MockCommonsMultipartResolver resolver =
new MockCommonsMultipartResolver();
ctx.getServletContext();
request.setContentType("multipart/form-data");
request.addHeader("Content-type", "multipart/form-data");
assertTrue(resolver.isMultipart(request));
MultipartHttpServletRequest multipartRequest =
resolver.resolveMultipart(request);
ModelAndView mav =
fileUpload.handleRequest(multipartRequest,
new MockHttpServletResponse());
server.stop();
// the getReceieved() method is spelled wrong in the API. ;-)
assertEquals(1, server.getReceievedEmailSize());
assertNotNull(request.getSession().getAttribute("message"));
Spring Live
Testing the Web Layer 300
uploadDir.getFile().delete();
assertFalse(uploadDir.exists());
}
Spring Live
Testing the Web Layer 301
3. Run this test using ant test -Dtestcase=FileUploadController. The test's output should
resemble the screenshot below.
From all of the examples in this section, you can see that many options for testing Controllers are available. The
simplest way to write Controller tests is to initialize a context in your test class, retrieve your Controllers (using
ctx.getBean("controllerName")) and execute methods on them. This technique allows Spring to wire the
Controller's dependencies. Using mocks requires a little more work because you must know your Controller's
dependencies. You also must be aware what methods will be called on your mocked object. The advantage is
you'll know exactly what your Controller depends on and how it interacts with those dependencies.
Spring Live
Testing the Web Layer 302
Testing Views
In Chapter 6, you created a jWebUnit test (UserWebTest) that interacted with links and buttons to verify the
functionality of the UI. jWebUnit is a nice framework for testing views because it allows you to write your tests
in Java. This section modifies the "test-web" target in build.xml so that Tomcat starts and stops before any
jWebUnit tests run. You will also change the current test to switch locales and verify text against
messages.properties, rather than hard coding it in the class. Finally, you will use Canoo WebTest as an alternative
method of testing the UI. WebTest is friendlier to non-programmers because tests are written in an Ant build file
using XML.
The tests in this section are commonly referred to as integration tests. They verify that all the layers are
integrated properly by navigating the UI as a user would in a browser. Both jWebUnit and Canoo WebTest
support JavaScript testing. This means that if you have an "onclick" handler on a button, and you specify to click
this button, the JavaScript call will execute. However, this JavaScript support is quite flakey and reports errors in
JavaScript when there are none. For this reason, I advocate 1) developing your UIs so they don't require
JavaScript to run and 2) writing your tests to work around any UIs that have JavaScript in them. This means if
the URL calls <button onclick="addPage()"/> by the addPage() function, it can simply be called in the
test.
jWebUnit
Before you change the UserWestTest in MyUsers, review its source. The constructor should always contain a
call to setBaseUrl(). Since jWebUnit refers to submit buttons by name, you must specify a name attribute on
your buttons. If you want to test tables and their data, you must specify an id attribute on your tables. MyUsers
should already contain all the necessary names and ids.
package org.appfuse.web;
Spring Live
Testing the Web Layer 303
/**
* Convenience method to get the id of the inserted user
* Assumes last inserted user is "Spring User"
*/
public String getInsertedUserId() {
String[] paths = {"/WEB-INF/applicationContext*.xml"};
ApplicationContext ctx =
new ClassPathXmlApplicationContext(paths);
List users = ((UserDAO) ctx.getBean("userDAO")).getUsers();
// assumed that user inserted in testAddUser() is last user
return ""+((User)users.get(users.size()-1)).getId();
}
}
In order to internationalize this test, you first must internationalize the titles in MyUsers. Currently, they are
hard-coded into their respective templates.
Spring Live
Testing the Web Layer 304
# Page titles
index.title=MyUsers ~ Welcome
userList.title=MyUsers ~ User List
userForm.title=MyUsers ~ User Details
2. The download for this chapter uses Velocity templates for the view, so you must edit the Velocity
templates:
• web/WEB-INF/velocity/userList.vm:
<title>${rc.getMessage("userList.title")}</title>
• web/WEB-INF/velocity/userForm.vm:
<title>${rc.getMessage("userForm.title")}</title>
• web/index.vm:
<title>${rc.getMessage("index.title")}</title>
3. The welcome page (index.vm) requires a bit more work to resolve the rc variable. In the download
for this chapter, index.vm is located in the web directory and configured as a welcome-file in
web.xml. It's not easy to expose the messages.properties file to the SiteMesh Servlet (which parses
index.vm); however, you can work around this issue by putting index.vm with the rest of the templates
and using Paul Tuckey's UrlRewriteFilter to resolve it as the welcome page. Below are the steps to
make this change:
<prop key="/index.html">filenameController</prop>
c. To make this the welcome file, use the UrlRewriteFilter. To configure it, declare it as a filter in
web/WEB-INF/web.xml:
<filter>
<filter-name>rewriteFilter</filter-name>
<filter-class>
org.tuckey.web.filters.urlrewrite.UrlRewriteFilter
</filter-class>
</filter>
Spring Live
Testing the Web Layer 305
<filter-mapping>
<filter-name>rewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<urlrewrite>
<rule>
<from>/$</from>
<to type="forward">index.html</to>
</rule>
<rule>
<from>/index.vm</from>
<to type="forward">index.html</to>
</rule>
</urlrewrite>
NOTE
Use these same steps to alter the FreeMarker templates. For JSP, use JSTL's <fmt:message> tag in the <title>
tags.
At this point, start Tomcat and run ant deploy test-web to verify that this change worked. After verifying
that it worked, modify this class to take advantage of jWebUnit's internationalization capabilities. Namely, it has
the ability to set a ResourceBundle and verify titles and text based on key names, rather than text values. More
information on this feature and others is available in jWebUnit's QuickStart Guide.
The main difference between the class below and the previous un-internationalized class is that the
testAddUser() method verifies the resulting page's title, rather than the success message text. This is because
the success message gets the new user's name substituted into its final output and, therefore, the key's value is
different from the value displayed. A patch for jWebUnit to allow testing keys with dynamic values is available.
Spring Live
Testing the Web Layer 306
Changed code is underlined below, and previous assertions are commented out.
package org.appfuse.web;
import java.util.List;
import java.util.Locale;
import net.sourceforge.jwebunit.WebTestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.appfuse.dao.UserDAO;
import org.appfuse.model.User;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
Spring Live
Testing the Web Layer 307
assertTextInTable("userList",
new String[] {"Spring", "User"});
}
/**
* Convenience method to get the id of the inserted user
* Assumes last inserted user is "Spring User"
*/
public String getInsertedUserId() {
String[] paths = {"/WEB-INF/applicationContext*.xml"};
ApplicationContext ctx =
new ClassPathXmlApplicationContext(paths);
List users = ((UserDAO) ctx.getBean("userDAO")).getUsers();
// assumed that user inserted in testAddUser() is last user
return ""+((User)users.get(users.size()-1)).getId();
}
}
To test this class using a different locale (and messages.properties file), follow the steps below:
1. Duplicate the messages.properties file and name it message_de.properties. Change some of the title
values so you know it's using the new message bundle.
2. Change the setLocale() call in the constructor to set the locale to German.
getTestContext().setLocale(Locale.GERMAN);
Spring Live
Testing the Web Layer 308
This will load the German messages file, but it will not set the locale for the request. This has been
reported as a bug in jWebUnit. To work around this bug, set the "Accept-Language" header as part
of the underlying HttpUnit WebClient.
getTestContext().getWebClient()
.setHeaderField("Accept-Language", "de");
The current setup for running jWebUnit tests requires you to start Tomcat before running the tests. To alleviate
this pain, add a new target to build.xml that automates the startup and shutdown of Tomcat before and after
running the test. Cargo is an open-source project started by Vincent Massol (who also created Cactus). It
provides a Java API and Ant Tasks to start/stop and configure Java containers. Since the Cargo JAR is already
included in this chapter's download, you must simply add the following target to build.xml:
Now you should be able to stop Tomcat and run ant test-tomcat to run the UserWebTest in Tomcat
without manually starting it.
NOTE
Cargo is a very new project and the JAR used in MyUsers is an alpha release from early September 2004. A few
things still need improvement, like allowing the startup/shutdown log messages to be printed to the console.
Currently, they can only be directed to the file specified in the "output" attribute.
Spring Live
Testing the Web Layer 309
Canoo WebTest
Canoo WebTest is a free, open-source tool for automating web application tests. It's similar to jWebUnit, except
you write its tests using XML and execute them using Ant. One nice feature is the ability to test PDFs and their
contents. In order to add Canoo WebTest tests to MyUsers, complete the following steps:
1. Add links to the userList.vm page in web/WEB-INF/velocity so that you can bring up a PDF by just
clicking on the PDF link. Put the following HTML just after the Add button.
<taskdef file="webtestTasks.properties">
<classpath>
<path refid="classpath"/>
<!-- for log4j.xml -->
<path location="web/WEB-INF/classes"/>
</classpath>
</taskdef>
<mkdir dir="${test.dir}/data"/>
<!-- Delete old results file if it exists -->
<delete file="${test.dir}/data/web-tests-result.xml"/>
3. Create a config.xml file in the test directory and add the following XML to it:
Spring Live
Testing the Web Layer 310
4. Create a web-tests.xml file in the test directory with the following structure:
<!DOCTYPE project [
<!ENTITY config SYSTEM "file:./config.xml">
]>
<project basedir="." default="run-all-tests">
<!-- Include messages.properties so we can test against
keys, rather than values -->
<property file="web/WEB-INF/classes/messages.properties"/>
<!-- Verify adding a user, viewing and deleting a user works -->
<target name="UserTests"
description="Adds a new user profile">
<canoo name="userTests">
&config;
<steps>
<!-- View add screen -->
<invoke url="/editUser.html"/>
<verifytitle text="${userForm.title}"/>
<!-- Enter data and save -->
<setinputfield name="firstName" value="Test"/>
<setinputfield name="lastName" value="Name"/>
<clickbutton label="Save"/>
<!-- View user list -->
<verifytitle text="${userList.title}"/>
<!-- Verify PDF contains new user -->
<clicklink label="PDF"/>
<verifyPdfText text="Test Name"/>
<!-- Delete first user in table -->
<invoke url="/users.html"/>
<clicklink href="editUser.html"/>
<verifytitle text="${userForm.title}"/>
<clickbutton label="Delete"/>
<verifytitle text="${userList.title}"/>
</steps>
</canoo>
</target>
</project>
Spring Live
Testing the Web Layer 311
This file's "UserTests" target contains the tasks to test the various operations on the MyUsers UI.
In the middle of the target is a call to bring up the PDF and verify the newly added user.
WARNING
If the rendering of the PDF doesn't work for your application, make sure the "viewResolver2" bean in action-
servlet.xml contains the following property:
<property name="order"><value>1</value></property>
If you get errors about log4j, make sure the following XML is in your log4j.xml file (in web/WEB-
INF/classes):
<logger name="com.canoo">
<level value="WARN"/>
</logger>
5. Run ant deploy, start Tomcat and then run ant test-canoo. All tests should pass and you will
see a console output similar to Figure 8.8.
Spring Live
Testing the Web Layer 312
Just like with jWebUnit, it’s easy to automate the startup/shutdown of Tomcat before and after running Canoo
tests. Use the Cargo API again, and add the following "test-view" target to build.xml.
If Tomcat isn't running, you should be able to test this target by running ant test-view.
TIP
The Canoo WebTest team keeps a weblog of tips related to this project.
From this brief overview of jWebUnit and Canoo's WebTest, I hope you learned enough about how each one
works to use in your projects. Many developers don't know it's possible to automate UI tests, and both of these
tools can save you a lot of time. Of course, you must still often look at your UI in a browser to tweak colors, fonts
and position.
Spring Live
Summary 313
Summary
This chapter covered a lot of information on how to test Spring, as well as how to write unit tests in general. You
learn how to use JUnit, DbUnit, EasyMock, jMock, Spring Mocks, Cactus, jWebUnit and Canoo's WebTest. For
the database layer, you saw how it's best to talk directly to a running database and verify that your DAOs are
operating as they should. It described techniques for overriding beans for tests (for example, to wrap transactions
around a DAO implementation).
In the business façade layer, demonstrations were given of using EasyMock and jMock, both of which greatly
simplify mocking objects. The best strategy for deciding what to mock is to never mock APIs that aren't yours.
Since the UserDAO interface and its implementation(s) are classes you've created, it makes sense to mock it in
the UserManagerTest unit test. Using mocks provides a nice way to isolate a test and, in most cases, makes
your tests run much faster.
In the web layer, you learned how to use jWebUnit to write a Java-based test for verifying the UI's features.
Canoo's WebTest demonstrated the same testing functionality, but with Ant/XML instead of Java. The new
Cargo project showed how you can easily start/stop Tomcat as part of running your tests.
This chapter did not cover testing rich clients (for example, SWT or Swing applications). This is mainly because
the Spring Rich has not been released at the time of this writing. Also, there seems to be a lack of tools for testing
these types of applications.
Testing is an important part of developing any software. This chapter has provided you with the tools and
techniques you need to use TDD in your projects to produce high-quality software.
Spring Live
Chapter
9
AOP
What is AOP and How Can Spring Make It Easier?
Aspect Oriented Programming has received a lot of hype in the Java community in the last year. What is AOP
and how can it help you in your applications? This chapter will cover the basics of AOP and give some useful
examples of how AOP might help you.
Spring Live
Overview 315
Overview
If you're active in the Java Community by reading weblogs, magazines or TheServerSide.com, you've probably
heard of Aspect-Oriented Programming (AOP). While AOP has been around for several years, it has only
recently gained attention. This is probably because many Java-based frameworks are now available to ease the
use of AOP.
The most basic definition of AOP is "it's a way of removing code duplication." Java is an Object-Oriented (OO)
language. It allows you to create applications and their objects in a hierarchical fashion. However, it doesn't offer
an easy way to remove code duplication among classes that aren't in the same hierarchy. In this chapter, you will
learn how to use AOP in your projects. You will modify the MyUsers application to use AOP for controlling
logging, caching, transactions and e-mail.
An application typically has two types of concerns: core concerns and crosscutting concerns. Core concerns
relate to application functionality, while crosscutting concerns don't apply to specific classes or modules, but are
system-wide. Modules created to manage these crosscutting concerns are known as aspects - hence the name
Aspect-Oriented Programming.
AOP is simply a nice way to modularize crosscutting concern logic from your classes and abstract them into
application-wide concerns, such as logging, security, transactions, etc. By writing generic AOP classes, you can
easily apply these concerns in other projects. Using Spring's IoC container, you can easily configure these
classes, and simply package them as a JAR to include in your projects.
In Spring terms, an aspect is typically an interceptor configured to inspect method calls. During inspection, this
interceptor can do many things: logging that the method is being called, modifying the returned object(s) or
sending notifications. Servlet Filters are a form of AOP that offers the ability to perform pre- and post-processing
of servlet invocations.
The Java Community's consensus on AOP seems to be "only factor concerns into AOP that your system can live
without." In other words, if it's something vitally important to your application (such as business logic), it should
remain in your application's code. This is because AOP configuration and code is not highly visible. With Spring,
you must examine an XML file to see what interceptors are applied to which methods. New developers may not
be aware of this and spend hours trying to figure out why certain behavior (performed by interceptors) is
happening. If you need to the see the functionality in your class, it shouldn't be in an aspect.
AOP helps you in terms of what you should see vs. what you shouldn't see. For example, it's better to have
Spring handle transaction-handling code than to see or write it yourself. This works especially well in an
environment with junior and senior developers; the experienced developers can hide aspects from the junior
developers.
Spring Live
Overview 316
This chapter is for new users of AOP. Entire books are available on AOP, which are beyond the scope of this
book. I recommend AspectJ in Action by Ramnivas Laddad. Its first chapter provides an excellent introduction
to AOP. This chapter shows you how to use AOP, while keeping the theory and low-level details to a minimum.
By showing you how Spring allows you to use AOP, you can concentrate on doing your job, rather than learning
everything about AOP.
Spring Live
Getting Started 317
Getting Started
In this chapter, like previous ones, you can follow along and do the examples as we go. The easiest way to do this
is to download the MyUsers Chapter 9 bundle from http://sourcebeat.com/downloads. This project tree is the
result of Chapter 8 exercises. It also contains all the JARs you will need for this chapter in the web/WEB-INF/lib
directory. You can also use the application you've been developing in previous chapters. If you go this route,
download Chapter 9 JARs from http://sourcebeat.com/downloads. Each section has a list of new JARs you may
need for each new technology. You can use this chapter as a reference for integrating these principles into your
own applications.
Note: If you have any issues downloading or running these examples, e-mail me at
[email protected] or enter an issue in the Spring Live Issue Tracker.
Logging Example
In most AOP books and articles, the first example shown is how to use AOP for logging. For debugging
purposes, it's sometimes nice to add log messages to the beginning and end of methods. Spring makes this very
easy, and it doesn't require any coding (only XML).
Note: In order to apply interceptors to beans, configure them as proxied classes. This is so JDK dynamic
proxies can create implementations of interfaces at runtime. This section covers the different AOP
implementation options after the logging examples below.
Spring ships with a TraceInterceptor that you can add as an interceptor to your proxied beans. In MyUsers,
the userManager bean is already proxied (using TransactionProxyFactoryBean), so adding an
interceptor is easy.
<bean id="loggingInterceptor"
class="org.springframework.aop.interceptor.TraceInterceptor"/>
<property name="preInterceptors">
<list>
<ref bean="loggingInterceptor"/>
</list>
</property>
Spring Live
Getting Started 318
<logger name="org.springframework.aop.interceptor">
<level value="DEBUG"/>
</logger>
4. Run ant test -Dtestcase=UserManagerTest. Your results should resemble the screenshot
below.
Spring Live
Getting Started 319
If you look at the source of TraceInterceptor, you can see that how simple AOP can be. The
org.springframework.aop.interceptor package contains a number of other interceptors that you may
find useful in your projects.
This simple example is a brief introduction to AOP. Before diving into more intricate examples, it's important to
be familiar with AOP terminology.
Spring Live
Getting Started 320
AOP, much like other programming paradigms, has its own vocabulary. The table below defines a number of the
words and phrases you may encounter when reading about or working with AOP. These definitions are not
Spring-specific.
AOP Definitions
Concern A particular issue, concept or area of interest for an application. Examples include
transaction management, persistence, logging and security.
Crosscutting A concern in which the implementation cuts across many classes. These are often
Concern difficult to implement and maintain with OOP.
Aspect The modularization of a crosscutting concern; implemented by gathering and
isolating code.
Join Point A point during the execution of a program or class. In Spring's AOP implementation, a
join point is always a method invocation. Other examples include accessing fields,
where read or write access occurs on an instance variable, and exception handling.
Advice An action taken at a particular join point. Different types of advice in Spring include
around, before, throws and after returning. Of these, around is the most powerful, as
you get the opportunity to do something before and after a method is invoked. The
previous TraceInterceptor used around advice by implementing the AOP
Alliance's MethodInterceptor. You can use the other advice types by
implementing the following Spring Interfaces:
• MethodBeforeAdvice
• ThrowsAdvice
• AfterReturningAdvice
Pointcut A set of join points specifying when an advice should fire. Pointcuts often use regular
expressions or wildcard syntax.
Introduction Adds fields or methods to an advised class. Spring allows you to introduce new
interfaces to any advised object. For example, you could use an introduction to make
any object implement an IsModified interface, to simplify caching.
Weaving Assembles aspects to create an advised object. This can be done at compile time
(this is how AspectJ does it) or at runtime. The Weaving Strategies section later in
this chapter discusses in detail the different strategies for weaving (that is,
implementing AOP).
Interceptor An AOP implementation strategy, where a chain of interceptors may exist for a
particular join point.
AOP Proxy An object created by the AOP framework, including advice. In Spring, an AOP proxy
will be a JDK dynamic proxy or a CGLIB proxy.
Target Object An object containing the join point. In frameworks using interception, it's the object
instance at the end of an interceptor chain. Also called an advised or proxied object.
The next section is on Pointcuts, which are the rules for applying advice. Because Spring's AOP is interceptor-
based, I will often refer to advice as interceptors.
Spring Live
Getting Started 321
Pointcuts
Pointcuts are an important part of AOP. They give you the ability to specify when and where to invoke
interceptors. In a sense, they're often similar to declarative validation, but rather than specifying a field to
validate, you specify a method to inspect. In the table, pointcuts are defined as "a set of join points specifying
when an advice (interceptor) should fire." Since Spring only supports method invocation join points, a pointcut is
just a declaration of methods to which an interceptor should be applied.
The easiest way to define pointcuts in Spring AOP is using regular expressions in a context file. Below is an
example that defines a pointcut for data manipulation operations:
<bean id="dataManipulationPointcut"
class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*save.*</value>
<value>.*remove.*</value>
</list>
</property>
</bean>
This pointcut says, "Intercept methods that start with save or remove."
Note: The JdkRegexpMethodPointcut class above requires J2SE 1.4, which has built-in regular
expression support. You can also use the Perl5RegexpMethodPointcut, which requires
Jakarta ORO (included in MyUsers).
In most cases, you won't need to define individual pointcuts like the one listed above. Rather, Spring provides
advisor classes that encapsulate interceptors and pointcuts in the same bean definition.
For regular expression pointcuts, you can use the RegexpMethodPointcutAdvisor. Below is an example of a
RegularExpressionPointcutAdvice that triggers a NotificationInterceptor when a user's
information is saved:
<bean id="notificationAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="notificationInterceptor"/>
</property>
<property name="pattern">
<value>.*saveUser</value>
</property>
</bean>
Spring Live
Getting Started 322
Currently, the RegexpMethodPointcutAdvisor only supports Perl5 pointcuts, meaning that you must have
jakarta-oro.jar in your classpath if you want to use it. A complete list of pointcuts and their useful advisors are
available in the org.springframework.aop.support package.
Weaving Strategies
Weaving is the process of applying aspects to target objects. The list below shows the primary strategies for
implementing AOP, ordered from simplest to most sophisticated.
Note: Much of the information in this section is based on information from J2EE without EJB.
• Language Extensions
Note: The best part about all of these frameworks is they have an interest in using the same standard in
their APIs. To back this up, they created the AOP Alliance project, which specifies a number of
interfaces for implementation. To see who's involved in this project, read its member list.
Dynamic Proxies is a feature built into J2SE 1.3+. It allows you to create an implementation of one or more
interfaces on-the-fly. Dynamic proxies are built into the JDK, eliminating the risk of strange behavior in various
environments. Their one limitation is they can't proxy classes, only interfaces. If you've designed your
application correctly with interfaces, this shouldn't be an issue.
There is some reflection overhead when using dynamic proxies, but the performance impact is negligible in J2SE
1.4+ JVMs. Spring uses dynamic proxies by default when proxying against interfaces. The dynaop project uses
this strategy when proxying interfaces.
For more information on Dynamic Proxies, see the Java 2 SDK Documentation.
Spring Live
Getting Started 323
Spring uses dynamic byte-code generation when proxying against classes. A popular tool for this is CGLIB
(Code Generation Library). It intercepts methods by generating subclasses dynamically. These subclasses
override parent methods and have hooks to invoke interceptor implementations. Hibernate uses CGLIB
extensively, and it has been proven to be a very stable solution in J2EE environments.
One limitation is that dynamic subclasses cannot override and proxy final methods.
Using a custom class loader allows you to advise all instances of created classes. This can be quite powerful,
since it gives you the opportunity to change the behavior of the new operator. JBoss AOP and AspectWerkz use
this approach, loading classes and weaving in their advices as defined in XML files.
The main danger in this approach is J2EE servers must typically control the class-loading hierarchy. What works
on one server may not work in another.
Language Extensions
AspectJ is the pioneering framework of AOP in Java. Rather than using a simple strategy for weaving in aspects,
it contains language extensions and its own compiler for using them.
While AspectJ is a very powerful and mature AOP implementation, its syntax is somewhat complex and not very
intuitive. However, AOP itself is not very intuitive, so trying to implement with a new language can be difficult.
Another limitation of this approach is the learning curve associated with a new language. However, if you want
the full power of AOP, including field-level interception, AspectJ will likely become your new best friend.
Integrating AspectJ with Spring is covered near the end of this chapter.
Spring takes a pragmatic "80/20 rule" approach to its AOP implementation. Rather than trying to satisfy
everyone's needs, it solves the most common ones and leaves the rest to more specialized AOP frameworks.
Spring Live
Getting Started 324
As mentioned earlier, in order to apply advice to beans defined in a context file, they must be proxied. Spring
contains a number of support classes (or Proxy Beans) to make proxying easier. The first is the
ProxyFactoryBean, which allows you to specify beans to be proxied and interceptors to apply. Below is an
example of using this bean to create a proxy of a business object:
<bean id="businessObject"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<bean class="org.appfuse.service.BusinessObject"/>
</property>
<property name="interceptorNames">
<list><ref bean="loggingInterceptor"/></list>
</property>
</bean>
The example above uses an inner-bean in the "target" property's value. This is a convenient way to hide a
business object behind a proxy, so it will always be advised when grabbed from the ApplicationContext.
The TransactionProxyFactoryBean is the most useful and most used Proxy Bean. It allows you to use AOP
to define transactions declaratively on target objects. This useful feature was previously only available with EJB
Container Managed Transactions (CMT). The usage of this bean is described in the Practical AOP Examples
section.
Spring Live
Getting Started 325
AutoProxy Beans
The aforementioned Proxy Beans provide easy manipulation of single beans, but what if you want to proxy
multiple beans, or all beans in a context? Spring provides two classes (in the
org.springframework.aop.framework.autoproxy package) that simplify this procedure.
The first is the BeanNameAutoProxyCreator, which allows you to specify a list of bean names as a property.
This property supports literals (actual bean names) and wildcards like *Manager. You can set interceptors using
the "interceptorNames" property.
<bean id="managerProxyCreator"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreat
or">
<property name="beanNames"><value>*Manager</value></property>
<property name="interceptorNames">
<list>
<value>loggingInterceptor</value>
</list>
</property>
</bean>
The second, more generic, multiple bean proxy creator is the DefaultAdvisorAutoProxyCreator. To use
this proxy bean, simply define it in your context file.
<bean id="autoProxyCreator"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAuto
ProxyCreator"/>
Unlike the BeanNameAutoProxyCreator, you cannot specify interceptors to apply. Rather, it will inspect any
advisors you have in this file and figure if their pointcuts make them applicable to other beans. For more
information on advisors, see Spring's reference documentation.
Spring Live
Practical AOP Examples 326
This section contains several examples of using AOP to manage crosscutting concerns in your application. The
concerns include transactions, caching and event notification.
Transactions
Specifying that operations should occur within a transaction can often result in a lot of duplication in your DAOs.
Using transactions the traditional way requires a lot of calls to tx.begin() and tx.commit() in data
manipulation methods. The good news is Spring and AOP can consolidate transaction concerns into a single
location and configuration.
You might not have been aware, but you've been using AOP in the MyUsers application since the QuickStart
Chapter. The ability to declaratively specify transaction attributes on the userManager bean is supported by
Spring's AOP and the TransactionProxyFactoryBean. The listing below shows a refactored version of the
userManager bean – this time using a transaction template bean and an inner-bean.
Spring Live
Practical AOP Examples 327
</bean>
</property>
</bean>
Further details on declarative transactions and handling their exceptions and rollbacks will be covered in Chapter
10.
Another good example of a crosscutting concern is caching data. The main reason for adding a cache is to
improve performance, especially if fetching data is a costly operation. Most of the frameworks discussed in the
last chapter have their own caching solutions; however, caching is a practical crosscutting concern.
Since your motivation behind introducing caching is to improve performance, add a test to UserManagerTest
to test the performance of UserMangerImpl. Put the code below in test/org/appfuse/service/
UserManagerTest.java.
Note: The StopWatch class in this test is a utility class for timing tasks like this.
user = mgr.saveUser(user);
sw.stop();
log.info(sw.shortSummary());
log.debug("End timing of method '" + name + "'");
}
Spring Live
Practical AOP Examples 328
Warning! If you use -Dtestcase=UserManager (without the "Test" suffix), it will run the mock tests
we created in the last chapter. Since these tests isolate the UserManager from Spring, they won't
demonstrate applying interceptors to be defined in the applicationContext.xml file.
In order to improve performance, add a simple cache to the UserManagerImpl. The basic requirements for this
cache are as follows:
1. Whenever you fetch objects from the database, put them into the cache (which is a simple HashMap
in this example).
2. When the save() or delete() methods are called, remove objects from the cache.
In the MyUsers application, you can implement the basic (non-AOP) solution by adding a BaseManager (in the
org.appfuse.service.impl package) from which all ManagerImpls extend:
package org.appfuse.service.impl;
Spring Live
Practical AOP Examples 329
To modify the UserManagerImpl to use this cache, make it extend BaseManager, then add calls to the cache
in each method. Below is a simple version of UserManagerImpl before adding these calls:
Spring Live
Practical AOP Examples 330
return user;
}
Running ant test -Dtestcase=UserManagerTest increases the performance. In Figure 9.3, the running
time is 9 seconds (1 second faster).
While adding these calls to add/remove from the cache is easy with a simple application like MyUsers, it will
become increasingly difficult with a larger application. You will have to remember to add these methods to each
Manager. Since caching is not a core concern of the application, you shouldn't really be concerned with it.
By using AOP, you can remove these method calls and intercept the appropriate methods to use the cache.
Furthermore, by abstracting the caching away from your code, it allows you to concentrate on the business logic
and doing the project.
Spring Live
Practical AOP Examples 331
1. Create a CacheInterceptor class in the org.appfuse.aop package (you must create this).
Populate it with the following code:
package org.appfuse.aop;
Spring Live
Practical AOP Examples 332
<bean id="cacheInterceptor"
class="org.appfuse.aop.CacheInterceptor"/>
<property name="preInterceptors">
<list>
<ref bean="cacheInterceptor"/>
</list>
</property>
Spring Live
Practical AOP Examples 333
Using a caching aspect, you've reduced the performance to 0 seconds! The reason for the dramatic increase in
performance is the CacheInterceptor returns the data before almost all of the method invocations on
UserManagerImpl.
The caching example provided here is very simplistic. For a more robust caching implementation, refer to Pieter
Coucke's Spring AOP Cache or using EHCache with Spring.
Spring Live
Practical AOP Examples 334
Event Notification
If you're developing a web application that's open to the public, you might want to be aware when new users sign
up. This can be particularly useful if you want to examine the user's information before authorizing an account.
In the following example, you'll create a NotificationInterceptor and configure it to send e-mail when a
new user registers.
1. Modify the UserManagerTest to use Dumbster to ensure that a message is sent when a new user
registers. Only relevant sections are included below. The new code in this class is underlined. After
adding this, run ant test -Dtestcase=UserManagerTest to ensure it fails.
user = mgr.saveUser(user);
server.stop();
assertEquals(1, server.getReceievedEmailSize()); // spelling is
correct
SmtpMessage sentMessage =
(SmtpMessage) server.getReceivedEmail().next();
assertTrue(sentMessage.getBody().indexOf("Easter Bunny") != -1);
log.debug(sentMessage);
assertTrue(user.getId() != null);
Spring Live
Practical AOP Examples 335
package org.appfuse.aop;
mailSender.send(message);
}
return user;
}
}
Spring Live
Practical AOP Examples 336
3. Configure this interceptor and its dependent beans (MailSender and SimpleMailMessage) in
web/WEB-INF/applicationContext.xml.
Warning! Be sure to change the e-mail address in the "to" property of the "accountMessage"
bean below. If you don't, the test will fail.
<bean id="notificationInterceptor"
class="org.appfuse.aop.NotificationInterceptor">
<property name="mailSender"><ref bean="mailSender"/></property>
<property name="message"><ref bean="accountMessage"/></property>
</bean>
<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host"><value>localhost</value></property>
</bean>
<bean id="notificationAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="notificationInterceptor"/>
</property>
<property name="pattern">
<value>.*saveUser</value>
</property>
</bean>
Spring Live
Practical AOP Examples 337
<property name="preInterceptors">
<list>
<!--ref bean="loggingInterceptor"/-->
<!--ref bean="cacheInterceptor"/-->
<ref bean="notificationAdvisor"/>
</list>
</property>
6. Save all your files and run ant test -Dtestcase=UserManagerTest. Your console output
should be similar to Figure 9.5.
Spring Live
Practical AOP Examples 338
Using a setup like this, you could easily add notifications based on values or conditions.
These examples are a good introduction to AOP and how to use it in your application. Some other examples of
where you might use AOP in your applications include:
• Resource pooling
• Thread pooling
Examples and code for implementing these are available in AspectJ in Action.
AOP is very powerful and new users should be careful when introducing it into their codebase. Two good ways
to get into AOP and learn more about it are listed below:
2. Non-production: The nice thing about AOP is you don't have to use all its features at once. It has a
nice incremental curve. You can start using it in development (for example, the Logging/Tracing
example), then you can move into having items that check on policy or help with testing. Once you
get comfortable, you can start using it in production.
While AOP might not be for everyone, it has a place for creating modular, maintainable Java applications.
Spring Live
Summary 339
Summary
AOP has been around for several years, but only recently has it gained so much popularity in the Java arena. This
is likely due to the many open-source frameworks for implementing AOP. At the time of this writing, these
include AspectJ, AspectWerkz, dynaop, JAC, JBoss AOP and Spring AOP.
Spring provides a simple-to-use API for AOP, and it integrates well with other AOP frameworks. As of Spring
version 1.1, it provides powerful integration with AspectJ. It also integrates nicely with AspectWerkz. In both of
these integrations, Spring's IoC container configures and manages the lifecycle of the created aspects.
In this chapter, you learned about the different concepts in AOP, from aspects to pointcuts to weaving. After
defining the verbiage in AOP, the different implementation strategies were explored. JDK Dynamic Proxies and
byte-code manipulation are the most popular weaving strategies used in the aforementioned AOP frameworks.
AspectJ is the most mature and sophisticated AOP implementation, providing its own language and compiler for
integrating aspects and classes. Finally, you learned how to write aspects and advisors in the MyUsers
application to implement logging, caching, transactions and event notification.
So what's the future of Spring's AOP framework? According to its roadmap, many of its goals have been met in
the 1.1 release, with improved performance and AspectJ integration. Spring AOP Cache and the Acegi Security
System for Spring are good examples of generic implementations for caching and security. This pool of
resources will grow extensively as more users become familiar with AOP and start using it in their applications.
Spring Live